datalegion-cli 1.2.0__tar.gz → 1.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/PKG-INFO +4 -3
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/README.md +1 -1
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/pyproject.toml +6 -1
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/client.py +5 -1
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/config.py +2 -1
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/tests/test_client.py +9 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/tests/test_config.py +16 -0
- datalegion_cli-1.2.2/tests/test_package_contents.py +56 -0
- datalegion_cli-1.2.0/.github/dependabot.yml +0 -42
- datalegion_cli-1.2.0/.github/workflows/ci.yml +0 -33
- datalegion_cli-1.2.0/.github/workflows/release.yml +0 -229
- datalegion_cli-1.2.0/uv.lock +0 -645
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/.gitignore +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/__init__.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/async_typer.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/commands/__init__.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/commands/company.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/commands/credits.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/commands/person.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/commands/utility.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/logging.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/main.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/output.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/src/datalegion_cli/update.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/tests/__init__.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/tests/conftest.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/tests/test_commands.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/tests/test_logging.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/tests/test_output.py +0 -0
- {datalegion_cli-1.2.0 → datalegion_cli-1.2.2}/tests/test_update.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: datalegion-cli
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: CLI for the Data Legion API — agent-friendly, fully async
|
|
5
5
|
Project-URL: Homepage, https://www.datalegion.ai
|
|
6
6
|
Project-URL: Documentation, https://www.datalegion.ai/docs
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Typing :: Typed
|
|
21
22
|
Requires-Python: >=3.11
|
|
22
23
|
Requires-Dist: cryptography>=44.0.0
|
|
@@ -246,7 +247,7 @@ The CLI is designed to be called by AI agents and scripts. Key patterns:
|
|
|
246
247
|
|
|
247
248
|
```bash
|
|
248
249
|
# All output is JSON to stdout — parse it directly
|
|
249
|
-
datalegion-cli person enrich --email john@example.com | jq '.matches[0].full_name'
|
|
250
|
+
datalegion-cli person enrich --email john@example.com | jq '.matches[0].person.full_name'
|
|
250
251
|
|
|
251
252
|
# Pipe JSON payloads via stdin
|
|
252
253
|
echo '{"email":"john@example.com","company":"Acme"}' | datalegion-cli person enrich --stdin
|
|
@@ -213,7 +213,7 @@ The CLI is designed to be called by AI agents and scripts. Key patterns:
|
|
|
213
213
|
|
|
214
214
|
```bash
|
|
215
215
|
# All output is JSON to stdout — parse it directly
|
|
216
|
-
datalegion-cli person enrich --email john@example.com | jq '.matches[0].full_name'
|
|
216
|
+
datalegion-cli person enrich --email john@example.com | jq '.matches[0].person.full_name'
|
|
217
217
|
|
|
218
218
|
# Pipe JSON payloads via stdin
|
|
219
219
|
echo '{"email":"john@example.com","company":"Acme"}' | datalegion-cli person enrich --stdin
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "datalegion-cli"
|
|
3
|
-
version = "1.2.
|
|
3
|
+
version = "1.2.2"
|
|
4
4
|
description = "CLI for the Data Legion API — agent-friendly, fully async"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -24,6 +24,7 @@ classifiers = [
|
|
|
24
24
|
"Programming Language :: Python :: 3.11",
|
|
25
25
|
"Programming Language :: Python :: 3.12",
|
|
26
26
|
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
27
28
|
"Environment :: Console",
|
|
28
29
|
"Typing :: Typed",
|
|
29
30
|
]
|
|
@@ -56,6 +57,10 @@ build-backend = "hatchling.build"
|
|
|
56
57
|
[tool.hatch.build.targets.wheel]
|
|
57
58
|
packages = ["src/datalegion_cli"]
|
|
58
59
|
|
|
60
|
+
# The sdist carries the source and tests, never repo tooling (.github/, .claude/, lefthook.yml).
|
|
61
|
+
[tool.hatch.build.targets.sdist]
|
|
62
|
+
only-include = ["src/datalegion_cli", "tests"]
|
|
63
|
+
|
|
59
64
|
[dependency-groups]
|
|
60
65
|
dev = [
|
|
61
66
|
"pytest>=8.3.0",
|
|
@@ -7,6 +7,7 @@ from typing import Any
|
|
|
7
7
|
import httpx
|
|
8
8
|
from loguru import logger
|
|
9
9
|
|
|
10
|
+
from datalegion_cli import __version__
|
|
10
11
|
from datalegion_cli.config import get_api_key, get_base_url
|
|
11
12
|
|
|
12
13
|
|
|
@@ -42,7 +43,7 @@ class LegionClient:
|
|
|
42
43
|
timeout: float = 60.0,
|
|
43
44
|
):
|
|
44
45
|
self._base_url = base_url or get_base_url()
|
|
45
|
-
self._api_key = api_key or get_api_key()
|
|
46
|
+
self._api_key = (api_key or "").strip() or get_api_key()
|
|
46
47
|
self._timeout = timeout
|
|
47
48
|
self._client: httpx.AsyncClient | None = None
|
|
48
49
|
self.last_credits_used: str | None = None
|
|
@@ -56,6 +57,9 @@ class LegionClient:
|
|
|
56
57
|
headers={
|
|
57
58
|
"API-Key": self._api_key,
|
|
58
59
|
"Content-Type": "application/json",
|
|
60
|
+
# Lets the API tell CLI traffic apart (its request log
|
|
61
|
+
# classifies datalegion-cli/* as 'cli').
|
|
62
|
+
"User-Agent": f"datalegion-cli/{__version__}",
|
|
59
63
|
},
|
|
60
64
|
)
|
|
61
65
|
return self._client
|
|
@@ -114,7 +114,8 @@ def save_config(cfg: dict[str, object]) -> None:
|
|
|
114
114
|
|
|
115
115
|
def get_api_key() -> str:
|
|
116
116
|
"""Return the configured API key or raise."""
|
|
117
|
-
key
|
|
117
|
+
# Trimmed: a key from a file or a paste with a trailing newline is otherwise refused by httpx.
|
|
118
|
+
key = str(load_config().get("api_key", "")).strip()
|
|
118
119
|
if not key:
|
|
119
120
|
raise SystemExit(
|
|
120
121
|
"No API key configured. Run `datalegion-cli config set api_key <your-key>` "
|
|
@@ -47,6 +47,15 @@ class TestLegionClientInit:
|
|
|
47
47
|
assert client._api_key == "legion_k"
|
|
48
48
|
assert client._timeout == 10.0
|
|
49
49
|
|
|
50
|
+
async def test_sends_identifying_user_agent(self):
|
|
51
|
+
"""The API's request log classifies datalegion-cli/* as CLI traffic."""
|
|
52
|
+
from datalegion_cli import __version__
|
|
53
|
+
|
|
54
|
+
client = LegionClient(base_url="https://x.local", api_key="legion_k")
|
|
55
|
+
http = await client._get_client()
|
|
56
|
+
assert http.headers["User-Agent"] == f"datalegion-cli/{__version__}"
|
|
57
|
+
await client.close()
|
|
58
|
+
|
|
50
59
|
def test_credit_headers_init_none(self, mock_api_key):
|
|
51
60
|
client = LegionClient(base_url="https://x.local", api_key=mock_api_key)
|
|
52
61
|
assert client.last_credits_used is None
|
|
@@ -49,6 +49,22 @@ class TestSaveAndLoad:
|
|
|
49
49
|
cfg = load_config()
|
|
50
50
|
assert cfg["api_key"] == "legion_roundtrip"
|
|
51
51
|
|
|
52
|
+
def test_api_key_is_trimmed(self, tmp_config_dir, monkeypatch):
|
|
53
|
+
from datalegion_cli.config import get_api_key, save_config
|
|
54
|
+
|
|
55
|
+
monkeypatch.setenv("LEGION_API_KEY", "legion_fromenv\n")
|
|
56
|
+
assert get_api_key() == "legion_fromenv"
|
|
57
|
+
monkeypatch.delenv("LEGION_API_KEY")
|
|
58
|
+
save_config({"api_key": " legion_fromfile "})
|
|
59
|
+
assert get_api_key() == "legion_fromfile"
|
|
60
|
+
|
|
61
|
+
def test_whitespace_only_api_key_is_missing(self, tmp_config_dir, monkeypatch):
|
|
62
|
+
from datalegion_cli.config import get_api_key
|
|
63
|
+
|
|
64
|
+
monkeypatch.setenv("LEGION_API_KEY", " \n")
|
|
65
|
+
with pytest.raises(SystemExit):
|
|
66
|
+
get_api_key()
|
|
67
|
+
|
|
52
68
|
def test_save_sets_file_permissions_600(self, tmp_config_dir):
|
|
53
69
|
from datalegion_cli.config import CONFIG_FILE, save_config
|
|
54
70
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""What the published wheel and sdist contain.
|
|
2
|
+
|
|
3
|
+
Customers install the wheel: it must hold only the ``datalegion_cli`` package, and its only
|
|
4
|
+
command is ``datalegion-cli``. The sdist carries the source and tests, never repo tooling.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
import tarfile
|
|
9
|
+
import zipfile
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture(scope="module")
|
|
18
|
+
def dist(tmp_path_factory: pytest.TempPathFactory) -> Path:
|
|
19
|
+
out = tmp_path_factory.mktemp("dist")
|
|
20
|
+
# Each straight from the source tree: a default build makes the wheel from the sdist, which
|
|
21
|
+
# would hide a wheel misconfiguration behind the sdist's own include list.
|
|
22
|
+
for target in ("--sdist", "--wheel"):
|
|
23
|
+
subprocess.run(
|
|
24
|
+
["uv", "build", target, "--quiet", "--out-dir", str(out)], cwd=ROOT, check=True
|
|
25
|
+
)
|
|
26
|
+
return out
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_wheel_ships_only_the_cli(dist: Path) -> None:
|
|
30
|
+
(wheel,) = dist.glob("*.whl")
|
|
31
|
+
with zipfile.ZipFile(wheel) as archive:
|
|
32
|
+
names = archive.namelist()
|
|
33
|
+
commands = next(
|
|
34
|
+
(archive.read(n).decode() for n in names if n.endswith("entry_points.txt")), ""
|
|
35
|
+
)
|
|
36
|
+
top_level = {n.split("/")[0] for n in names if not n.split("/")[0].endswith(".dist-info")}
|
|
37
|
+
assert top_level == {"datalegion_cli"}
|
|
38
|
+
assert commands.split() == [
|
|
39
|
+
"[console_scripts]",
|
|
40
|
+
"datalegion-cli",
|
|
41
|
+
"=",
|
|
42
|
+
"datalegion_cli.main:run",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_sdist_carries_no_repo_tooling(dist: Path) -> None:
|
|
47
|
+
(sdist,) = dist.glob("*.tar.gz")
|
|
48
|
+
with tarfile.open(sdist) as archive:
|
|
49
|
+
names = archive.getnames()
|
|
50
|
+
leaked = [
|
|
51
|
+
n
|
|
52
|
+
for n in names
|
|
53
|
+
if any(part in n.split("/") for part in (".claude", ".github", "scripts", "lefthook.yml"))
|
|
54
|
+
]
|
|
55
|
+
assert leaked == []
|
|
56
|
+
assert any(n.endswith("src/datalegion_cli/main.py") for n in names)
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
version: 2
|
|
2
|
-
updates:
|
|
3
|
-
- package-ecosystem: "uv"
|
|
4
|
-
directory: "/"
|
|
5
|
-
schedule:
|
|
6
|
-
interval: "weekly"
|
|
7
|
-
day: "sunday"
|
|
8
|
-
time: "09:00"
|
|
9
|
-
timezone: "America/Los_Angeles"
|
|
10
|
-
cooldown:
|
|
11
|
-
default-days: 1
|
|
12
|
-
labels:
|
|
13
|
-
- "dependencies"
|
|
14
|
-
- "python"
|
|
15
|
-
commit-message:
|
|
16
|
-
prefix: "chore(deps)"
|
|
17
|
-
groups:
|
|
18
|
-
minor-patch:
|
|
19
|
-
patterns:
|
|
20
|
-
- "*"
|
|
21
|
-
update-types:
|
|
22
|
-
- "minor"
|
|
23
|
-
- "patch"
|
|
24
|
-
|
|
25
|
-
- package-ecosystem: "github-actions"
|
|
26
|
-
directory: "/"
|
|
27
|
-
schedule:
|
|
28
|
-
interval: "weekly"
|
|
29
|
-
day: "sunday"
|
|
30
|
-
time: "09:00"
|
|
31
|
-
timezone: "America/Los_Angeles"
|
|
32
|
-
cooldown:
|
|
33
|
-
default-days: 1
|
|
34
|
-
labels:
|
|
35
|
-
- "dependencies"
|
|
36
|
-
- "github-actions"
|
|
37
|
-
commit-message:
|
|
38
|
-
prefix: "chore(deps)"
|
|
39
|
-
groups:
|
|
40
|
-
actions:
|
|
41
|
-
patterns:
|
|
42
|
-
- "*"
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
branches: [main]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
lint:
|
|
9
|
-
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
10
|
-
steps:
|
|
11
|
-
- uses: actions/checkout@v7
|
|
12
|
-
- uses: astral-sh/setup-uv@v9.0.0
|
|
13
|
-
with:
|
|
14
|
-
enable-cache: true
|
|
15
|
-
- uses: actions/setup-python@v7
|
|
16
|
-
with:
|
|
17
|
-
python-version: "3.11"
|
|
18
|
-
- run: uv sync --group dev
|
|
19
|
-
- run: uv run ruff check src/ tests/
|
|
20
|
-
- run: uv run ruff format --check src/ tests/
|
|
21
|
-
|
|
22
|
-
test:
|
|
23
|
-
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
24
|
-
steps:
|
|
25
|
-
- uses: actions/checkout@v7
|
|
26
|
-
- uses: astral-sh/setup-uv@v9.0.0
|
|
27
|
-
with:
|
|
28
|
-
enable-cache: true
|
|
29
|
-
- uses: actions/setup-python@v7
|
|
30
|
-
with:
|
|
31
|
-
python-version: "3.11"
|
|
32
|
-
- run: uv sync --group dev
|
|
33
|
-
- run: uv run pytest
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- "v*"
|
|
7
|
-
|
|
8
|
-
concurrency:
|
|
9
|
-
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
-
cancel-in-progress: true
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
lint:
|
|
14
|
-
name: Lint & Build
|
|
15
|
-
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
16
|
-
timeout-minutes: 5
|
|
17
|
-
permissions:
|
|
18
|
-
contents: read
|
|
19
|
-
steps:
|
|
20
|
-
- name: Checkout
|
|
21
|
-
uses: actions/checkout@v7
|
|
22
|
-
|
|
23
|
-
- name: Install uv
|
|
24
|
-
uses: astral-sh/setup-uv@v9.0.0
|
|
25
|
-
with:
|
|
26
|
-
enable-cache: true
|
|
27
|
-
cache-dependency-glob: "uv.lock"
|
|
28
|
-
|
|
29
|
-
- name: Set up Python
|
|
30
|
-
run: uv python install 3.11
|
|
31
|
-
|
|
32
|
-
- name: Install dependencies
|
|
33
|
-
run: uv sync --frozen --group dev
|
|
34
|
-
|
|
35
|
-
- name: Run lint
|
|
36
|
-
run: uv run ruff check src/ tests/
|
|
37
|
-
|
|
38
|
-
- name: Check formatting
|
|
39
|
-
run: uv run ruff format --check src/ tests/
|
|
40
|
-
|
|
41
|
-
- name: Build
|
|
42
|
-
run: uv build
|
|
43
|
-
|
|
44
|
-
- name: Prune uv cache
|
|
45
|
-
if: always()
|
|
46
|
-
run: uv cache prune --ci
|
|
47
|
-
|
|
48
|
-
test:
|
|
49
|
-
name: Test (Python ${{ matrix.python-version }})
|
|
50
|
-
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
51
|
-
timeout-minutes: 10
|
|
52
|
-
permissions:
|
|
53
|
-
contents: read
|
|
54
|
-
strategy:
|
|
55
|
-
fail-fast: false
|
|
56
|
-
matrix:
|
|
57
|
-
python-version: ["3.11", "3.12", "3.13"]
|
|
58
|
-
steps:
|
|
59
|
-
- name: Checkout
|
|
60
|
-
uses: actions/checkout@v7
|
|
61
|
-
|
|
62
|
-
- name: Install uv
|
|
63
|
-
uses: astral-sh/setup-uv@v9.0.0
|
|
64
|
-
with:
|
|
65
|
-
enable-cache: true
|
|
66
|
-
cache-dependency-glob: "uv.lock"
|
|
67
|
-
|
|
68
|
-
- name: Set up Python ${{ matrix.python-version }}
|
|
69
|
-
run: uv python install ${{ matrix.python-version }}
|
|
70
|
-
|
|
71
|
-
- name: Install dependencies
|
|
72
|
-
run: uv sync --frozen --group dev
|
|
73
|
-
|
|
74
|
-
- name: Run tests
|
|
75
|
-
run: uv run pytest tests/ -v --tb=short
|
|
76
|
-
|
|
77
|
-
- name: Prune uv cache
|
|
78
|
-
if: always()
|
|
79
|
-
run: uv cache prune --ci
|
|
80
|
-
|
|
81
|
-
publish-pypi:
|
|
82
|
-
name: Publish to PyPI
|
|
83
|
-
needs: [lint, test]
|
|
84
|
-
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
85
|
-
timeout-minutes: 5
|
|
86
|
-
permissions:
|
|
87
|
-
contents: read
|
|
88
|
-
id-token: write
|
|
89
|
-
steps:
|
|
90
|
-
- name: Checkout
|
|
91
|
-
uses: actions/checkout@v7
|
|
92
|
-
|
|
93
|
-
- name: Install uv
|
|
94
|
-
uses: astral-sh/setup-uv@v9.0.0
|
|
95
|
-
|
|
96
|
-
- name: Set up Python
|
|
97
|
-
run: uv python install 3.11
|
|
98
|
-
|
|
99
|
-
- name: Set version from tag
|
|
100
|
-
run: |
|
|
101
|
-
VERSION="${GITHUB_REF_NAME#v}"
|
|
102
|
-
sed -i "s/^version = .*/version = \"${VERSION}\"/" pyproject.toml
|
|
103
|
-
|
|
104
|
-
- name: Build
|
|
105
|
-
run: uv build
|
|
106
|
-
|
|
107
|
-
- name: Check if version exists on PyPI
|
|
108
|
-
id: pypi-check
|
|
109
|
-
run: |
|
|
110
|
-
VERSION="${GITHUB_REF_NAME#v}"
|
|
111
|
-
if curl -sSf "https://pypi.org/pypi/datalegion-cli/${VERSION}/json" > /dev/null 2>&1; then
|
|
112
|
-
echo "Version ${VERSION} already exists on PyPI, skipping publish"
|
|
113
|
-
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
114
|
-
else
|
|
115
|
-
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
116
|
-
fi
|
|
117
|
-
|
|
118
|
-
- name: Publish to PyPI
|
|
119
|
-
if: steps.pypi-check.outputs.exists != 'true'
|
|
120
|
-
run: uv publish --trusted-publishing always
|
|
121
|
-
|
|
122
|
-
update-homebrew:
|
|
123
|
-
name: Update Homebrew Tap
|
|
124
|
-
needs: [publish-pypi]
|
|
125
|
-
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
126
|
-
timeout-minutes: 10
|
|
127
|
-
permissions:
|
|
128
|
-
contents: read
|
|
129
|
-
steps:
|
|
130
|
-
- name: Wait for PyPI availability
|
|
131
|
-
run: |
|
|
132
|
-
VERSION="${GITHUB_REF_NAME#v}"
|
|
133
|
-
for i in $(seq 1 30); do
|
|
134
|
-
if curl -sSf "https://pypi.org/pypi/datalegion-cli/${VERSION}/json" > /dev/null 2>&1; then
|
|
135
|
-
echo "Version ${VERSION} available on PyPI"
|
|
136
|
-
exit 0
|
|
137
|
-
fi
|
|
138
|
-
echo "Waiting for PyPI... (attempt $i/30)"
|
|
139
|
-
sleep 10
|
|
140
|
-
done
|
|
141
|
-
echo "::error::Timed out waiting for PyPI availability"
|
|
142
|
-
exit 1
|
|
143
|
-
|
|
144
|
-
- name: Generate Homebrew formula
|
|
145
|
-
run: |
|
|
146
|
-
VERSION="${GITHUB_REF_NAME#v}"
|
|
147
|
-
|
|
148
|
-
# Get the actual sdist URL and digest from PyPI JSON API
|
|
149
|
-
PYPI_JSON=$(curl -sSf "https://pypi.org/pypi/datalegion-cli/${VERSION}/json")
|
|
150
|
-
URL=$(echo "$PYPI_JSON" | python3 -c "import sys,json; urls=json.load(sys.stdin)['urls']; print(next(u['url'] for u in urls if u['packagetype']=='sdist'))")
|
|
151
|
-
SHA256=$(echo "$PYPI_JSON" | python3 -c "import sys,json; urls=json.load(sys.stdin)['urls']; print(next(u['digests']['sha256'] for u in urls if u['packagetype']=='sdist'))")
|
|
152
|
-
|
|
153
|
-
cat > datalegion-cli.rb <<FORMULA
|
|
154
|
-
class DatalegionCli < Formula
|
|
155
|
-
desc "CLI for the Data Legion API - agent-friendly, fully async"
|
|
156
|
-
homepage "https://www.datalegion.ai"
|
|
157
|
-
url "${URL}"
|
|
158
|
-
sha256 "${SHA256}"
|
|
159
|
-
license "MIT"
|
|
160
|
-
|
|
161
|
-
depends_on "python@3.13"
|
|
162
|
-
|
|
163
|
-
def install
|
|
164
|
-
venv = libexec/"venv"
|
|
165
|
-
system Formula["python@3.13"].opt_bin/"python3.13", "-m", "venv", venv
|
|
166
|
-
system venv/"bin/pip", "install", "datalegion-cli==#{version}"
|
|
167
|
-
bin.install_symlink Dir[venv/"bin/datalegion-cli"]
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
test do
|
|
171
|
-
assert_match version.to_s, shell_output("#{bin}/datalegion-cli version")
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
FORMULA
|
|
175
|
-
|
|
176
|
-
# Remove leading whitespace from heredoc indentation
|
|
177
|
-
sed -i 's/^ //' datalegion-cli.rb
|
|
178
|
-
|
|
179
|
-
- name: Push formula to tap
|
|
180
|
-
env:
|
|
181
|
-
GH_PAT: ${{ secrets.GH_PAT }}
|
|
182
|
-
run: |
|
|
183
|
-
VERSION="${GITHUB_REF_NAME#v}"
|
|
184
|
-
git clone "https://x-access-token:${GH_PAT}@github.com/datalegion-ai/homebrew-tap.git" tap
|
|
185
|
-
mkdir -p tap/Formula
|
|
186
|
-
cp datalegion-cli.rb tap/Formula/datalegion-cli.rb
|
|
187
|
-
cd tap
|
|
188
|
-
git config user.name "github-actions[bot]"
|
|
189
|
-
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
190
|
-
git add Formula/datalegion-cli.rb
|
|
191
|
-
git diff --staged --quiet || git commit -m "datalegion-cli ${VERSION}"
|
|
192
|
-
git push
|
|
193
|
-
|
|
194
|
-
github-release:
|
|
195
|
-
name: Create GitHub Release
|
|
196
|
-
needs: [publish-pypi]
|
|
197
|
-
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
198
|
-
timeout-minutes: 5
|
|
199
|
-
permissions:
|
|
200
|
-
contents: write
|
|
201
|
-
steps:
|
|
202
|
-
- name: Checkout
|
|
203
|
-
uses: actions/checkout@v7
|
|
204
|
-
with:
|
|
205
|
-
ref: main
|
|
206
|
-
token: ${{ secrets.GH_PAT }}
|
|
207
|
-
|
|
208
|
-
- name: Update version in pyproject.toml
|
|
209
|
-
run: |
|
|
210
|
-
VERSION="${GITHUB_REF_NAME#v}"
|
|
211
|
-
sed -i "s/^version = .*/version = \"${VERSION}\"/" pyproject.toml
|
|
212
|
-
git config user.name "github-actions[bot]"
|
|
213
|
-
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
214
|
-
git add pyproject.toml
|
|
215
|
-
git diff --staged --quiet || git commit -m "chore: bump version to ${VERSION} [skip ci]"
|
|
216
|
-
git push
|
|
217
|
-
|
|
218
|
-
- name: Create Release
|
|
219
|
-
uses: actions/github-script@v9
|
|
220
|
-
with:
|
|
221
|
-
script: |
|
|
222
|
-
const tag = context.ref.replace('refs/tags/', '');
|
|
223
|
-
await github.rest.repos.createRelease({
|
|
224
|
-
owner: context.repo.owner,
|
|
225
|
-
repo: context.repo.repo,
|
|
226
|
-
tag_name: tag,
|
|
227
|
-
name: tag,
|
|
228
|
-
generate_release_notes: true,
|
|
229
|
-
});
|