terp-cli 0.33.0__tar.gz → 0.34.0__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.
- {terp_cli-0.33.0 → terp_cli-0.34.0}/PKG-INFO +8 -8
- {terp_cli-0.33.0 → terp_cli-0.34.0}/pyproject.toml +8 -8
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/verify.py +57 -62
- {terp_cli-0.33.0 → terp_cli-0.34.0}/.gitignore +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/__init__.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/_appref.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/_engine.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/_output.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/_subjects.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/access.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/apidocs.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/authz_surface.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/capabilities.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/deploy_safety.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/dev.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/docker.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/docker_masks.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/envfile.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/envschema.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/envseams.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/fmt.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/grants.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/jobs.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/leases.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/module_roles.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/openapi.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/outbox.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/ports.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/profiles.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/py.typed +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/routes.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/scaffold.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/schema.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/seed.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/service_accounts.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/smoke.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/users.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/version.py +0 -0
- {terp_cli-0.33.0 → terp_cli-0.34.0}/src/terp/cli/workbench.py +0 -0
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: terp-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.34.0
|
|
4
4
|
Summary: Terp command-line tool — inspect, scaffolding, migrations, checks, api-docs.
|
|
5
5
|
Project-URL: Repository, https://github.com/AITT-NL/terp-framework
|
|
6
6
|
Project-URL: Changelog, https://github.com/AITT-NL/terp-framework/blob/main/CHANGELOG.md
|
|
7
7
|
License-Expression: Apache-2.0
|
|
8
8
|
Requires-Python: >=3.13
|
|
9
9
|
Requires-Dist: pyyaml>=6.0
|
|
10
|
-
Requires-Dist: terp-arch==0.
|
|
11
|
-
Requires-Dist: terp-core==0.
|
|
12
|
-
Requires-Dist: terp-migrations==0.
|
|
10
|
+
Requires-Dist: terp-arch==0.34.0
|
|
11
|
+
Requires-Dist: terp-core==0.34.0
|
|
12
|
+
Requires-Dist: terp-migrations==0.34.0
|
|
13
13
|
Provides-Extra: jobs
|
|
14
|
-
Requires-Dist: terp-cap-outbox==0.
|
|
15
|
-
Requires-Dist: terp-cap-scheduler-apscheduler==0.
|
|
14
|
+
Requires-Dist: terp-cap-outbox==0.34.0; extra == 'jobs'
|
|
15
|
+
Requires-Dist: terp-cap-scheduler-apscheduler==0.34.0; extra == 'jobs'
|
|
16
16
|
Provides-Extra: scheduler
|
|
17
|
-
Requires-Dist: terp-cap-scheduler-apscheduler==0.
|
|
17
|
+
Requires-Dist: terp-cap-scheduler-apscheduler==0.34.0; extra == 'scheduler'
|
|
18
18
|
Provides-Extra: worker
|
|
19
|
-
Requires-Dist: terp-cap-outbox==0.
|
|
19
|
+
Requires-Dist: terp-cap-outbox==0.34.0; extra == 'worker'
|
|
@@ -4,18 +4,18 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "terp-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.34.0"
|
|
8
8
|
description = "Terp command-line tool — inspect, scaffolding, migrations, checks, api-docs."
|
|
9
9
|
requires-python = ">=3.13"
|
|
10
10
|
license = "Apache-2.0"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"terp-core==0.
|
|
12
|
+
"terp-core==0.34.0",
|
|
13
13
|
# `terp migrate` delegates to terp-migrations (lazily imported), keeping Alembic
|
|
14
14
|
# off the path for `terp inspect` / `terp guide` while making migrations work.
|
|
15
|
-
"terp-migrations==0.
|
|
15
|
+
"terp-migrations==0.34.0",
|
|
16
16
|
# `terp guide rules` projects the live rule registry; terp-arch is imported lazily
|
|
17
17
|
# (only when that topic is rendered), so it stays off the common `terp guide` path.
|
|
18
|
-
"terp-arch==0.
|
|
18
|
+
"terp-arch==0.34.0",
|
|
19
19
|
# `terp verify --only env-seams` and `terp smoke` read the compose profiles as data
|
|
20
20
|
# (anchors and `<<:` merge keys resolved), so both answer without a Docker daemon or
|
|
21
21
|
# even the `docker` binary. Declared explicitly rather than leant on: every app
|
|
@@ -29,11 +29,11 @@ dependencies = [
|
|
|
29
29
|
# or a scheduler engine into apps that only use inspect/check/migrate. Deployments can select
|
|
30
30
|
# one process role, while `jobs` is the convenient complete jobs-process bundle.
|
|
31
31
|
[project.optional-dependencies]
|
|
32
|
-
worker = ["terp-cap-outbox==0.
|
|
33
|
-
scheduler = ["terp-cap-scheduler-apscheduler==0.
|
|
32
|
+
worker = ["terp-cap-outbox==0.34.0"]
|
|
33
|
+
scheduler = ["terp-cap-scheduler-apscheduler==0.34.0"]
|
|
34
34
|
jobs = [
|
|
35
|
-
"terp-cap-outbox==0.
|
|
36
|
-
"terp-cap-scheduler-apscheduler==0.
|
|
35
|
+
"terp-cap-outbox==0.34.0",
|
|
36
|
+
"terp-cap-scheduler-apscheduler==0.34.0",
|
|
37
37
|
]
|
|
38
38
|
|
|
39
39
|
[project.scripts]
|
|
@@ -1090,6 +1090,45 @@ def _node_modules_problem(root: pathlib.Path, workspace: str) -> str | None:
|
|
|
1090
1090
|
)
|
|
1091
1091
|
|
|
1092
1092
|
|
|
1093
|
+
#: The status a shell gives a command it cannot find, and the one a check answers with
|
|
1094
|
+
#: when the tool it runs is not installed.
|
|
1095
|
+
_NOT_FOUND = 127
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
def _run_argv(
|
|
1099
|
+
argv: list[str], root: pathlib.Path, *, env: dict[str, str] | None = None
|
|
1100
|
+
) -> subprocess.CompletedProcess[str]:
|
|
1101
|
+
"""Run *argv* in *root* as a fixed argv, with no shell; a missing tool is a result.
|
|
1102
|
+
|
|
1103
|
+
Every check that shells out comes through here, so a tool that is not installed
|
|
1104
|
+
answers the way a shell would, exit 127 naming the tool, instead of raising. Only the
|
|
1105
|
+
manifest runner used to catch it. The runners with a body of their own (route types,
|
|
1106
|
+
the API client, the API reference) let ``FileNotFoundError`` escape the profile loop:
|
|
1107
|
+
the run ended in a traceback about ``subprocess``, every check after that one went
|
|
1108
|
+
unrun and unreported, and the one fact worth reading, which tool is missing, was a
|
|
1109
|
+
frame deep in the stack.
|
|
1110
|
+
|
|
1111
|
+
The message is the result's stdout, so each caller's own formatting of a finished
|
|
1112
|
+
command carries it unchanged.
|
|
1113
|
+
"""
|
|
1114
|
+
executable = shutil.which(argv[0]) or argv[0]
|
|
1115
|
+
try:
|
|
1116
|
+
return subprocess.run( # noqa: S603 - fixed argv, shell=False
|
|
1117
|
+
[executable, *argv[1:]],
|
|
1118
|
+
cwd=root,
|
|
1119
|
+
capture_output=True,
|
|
1120
|
+
text=True,
|
|
1121
|
+
encoding="utf-8",
|
|
1122
|
+
errors="replace",
|
|
1123
|
+
check=False,
|
|
1124
|
+
env=None if env is None else {**os.environ, **env},
|
|
1125
|
+
)
|
|
1126
|
+
except FileNotFoundError:
|
|
1127
|
+
return subprocess.CompletedProcess(
|
|
1128
|
+
argv, _NOT_FOUND, stdout=f"{argv[0]}: executable not found on PATH", stderr=""
|
|
1129
|
+
)
|
|
1130
|
+
|
|
1131
|
+
|
|
1093
1132
|
def _run_subprocess(
|
|
1094
1133
|
check: VerifyCheck, root: pathlib.Path, *, env: dict[str, str] | None = None
|
|
1095
1134
|
) -> tuple[int, str]:
|
|
@@ -1109,20 +1148,7 @@ def _run_subprocess(
|
|
|
1109
1148
|
problem = _node_modules_problem(root, _npm_workspace(argv))
|
|
1110
1149
|
if problem is not None:
|
|
1111
1150
|
return 1, problem
|
|
1112
|
-
|
|
1113
|
-
try:
|
|
1114
|
-
completed = subprocess.run( # noqa: S603 - fixed manifest argv, shell=False
|
|
1115
|
-
[executable, *argv[1:]],
|
|
1116
|
-
cwd=root,
|
|
1117
|
-
capture_output=True,
|
|
1118
|
-
text=True,
|
|
1119
|
-
encoding="utf-8",
|
|
1120
|
-
errors="replace",
|
|
1121
|
-
check=False,
|
|
1122
|
-
env=None if env is None else {**os.environ, **env},
|
|
1123
|
-
)
|
|
1124
|
-
except FileNotFoundError:
|
|
1125
|
-
return 127, f"{argv[0]}: executable not found on PATH"
|
|
1151
|
+
completed = _run_argv(argv, root, env=env)
|
|
1126
1152
|
return completed.returncode, completed.stdout + (
|
|
1127
1153
|
"\n" + completed.stderr if completed.stderr else ""
|
|
1128
1154
|
)
|
|
@@ -1640,17 +1666,7 @@ def _run_routes_drift(root: pathlib.Path) -> tuple[int, str]:
|
|
|
1640
1666
|
problem = _node_modules_problem(root, "frontend")
|
|
1641
1667
|
if problem is not None:
|
|
1642
1668
|
return 1, problem
|
|
1643
|
-
|
|
1644
|
-
executable = shutil.which(argv[0]) or argv[0]
|
|
1645
|
-
completed = subprocess.run( # noqa: S603 - fixed manifest argv, shell=False
|
|
1646
|
-
[executable, *argv[1:]],
|
|
1647
|
-
cwd=root,
|
|
1648
|
-
capture_output=True,
|
|
1649
|
-
text=True,
|
|
1650
|
-
encoding="utf-8",
|
|
1651
|
-
errors="replace",
|
|
1652
|
-
check=False,
|
|
1653
|
-
)
|
|
1669
|
+
completed = _run_argv(routes_argv(check=True), root)
|
|
1654
1670
|
return completed.returncode, f"{completed.stdout}{completed.stderr}"
|
|
1655
1671
|
|
|
1656
1672
|
|
|
@@ -1663,21 +1679,6 @@ _API_DOCS_ARTIFACTS = ("platform-api.md", "terp_core.pyi")
|
|
|
1663
1679
|
_API_DOCS_ADOPT_HINT = "commit docs/platform-api.md + docs/terp_core.pyi to enable"
|
|
1664
1680
|
|
|
1665
1681
|
|
|
1666
|
-
def _tracked(root: pathlib.Path, relative: str) -> bool:
|
|
1667
|
-
"""Whether *relative* is a file git is tracking in *root*."""
|
|
1668
|
-
git = shutil.which("git") or "git"
|
|
1669
|
-
completed = subprocess.run( # noqa: S603 - fixed argv, shell=False
|
|
1670
|
-
[git, "ls-files", "--error-unmatch", "--", relative],
|
|
1671
|
-
cwd=root,
|
|
1672
|
-
capture_output=True,
|
|
1673
|
-
text=True,
|
|
1674
|
-
encoding="utf-8",
|
|
1675
|
-
errors="replace",
|
|
1676
|
-
check=False,
|
|
1677
|
-
)
|
|
1678
|
-
return completed.returncode == 0
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
1682
|
def _run_api_docs_drift(root: pathlib.Path) -> tuple[int, str]:
|
|
1682
1683
|
"""Regenerate the API reference and fail on drift from the committed copy.
|
|
1683
1684
|
|
|
@@ -1707,7 +1708,20 @@ def _run_api_docs_drift(root: pathlib.Path) -> tuple[int, str]:
|
|
|
1707
1708
|
f"({_API_DOCS_ADOPT_HINT})",
|
|
1708
1709
|
)
|
|
1709
1710
|
relatives = [f"docs/{name}" for name in _API_DOCS_ARTIFACTS]
|
|
1710
|
-
|
|
1711
|
+
probes = {
|
|
1712
|
+
relative: _run_argv(["git", "ls-files", "--error-unmatch", "--", relative], root)
|
|
1713
|
+
for relative in relatives
|
|
1714
|
+
}
|
|
1715
|
+
if any(probe.returncode == _NOT_FOUND for probe in probes.values()):
|
|
1716
|
+
# Not "untracked, skipped": without git there is no answer to whether the pair is
|
|
1717
|
+
# tracked, and reading that silence as "no" would turn this check green over a
|
|
1718
|
+
# comparison it never made.
|
|
1719
|
+
return (
|
|
1720
|
+
_NOT_FOUND,
|
|
1721
|
+
"git: executable not found on PATH - the drift check asks git whether "
|
|
1722
|
+
f"{' + '.join(relatives)} are tracked, so it cannot run without it",
|
|
1723
|
+
)
|
|
1724
|
+
tracked = [relative for relative, probe in probes.items() if probe.returncode == 0]
|
|
1711
1725
|
if not tracked:
|
|
1712
1726
|
return (
|
|
1713
1727
|
0,
|
|
@@ -1729,16 +1743,7 @@ def _run_api_docs_drift(root: pathlib.Path) -> tuple[int, str]:
|
|
|
1729
1743
|
written = [str(path) for path in api_docs(str(docs))]
|
|
1730
1744
|
finally:
|
|
1731
1745
|
os.chdir(previous)
|
|
1732
|
-
|
|
1733
|
-
completed = subprocess.run( # noqa: S603 - fixed argv, shell=False
|
|
1734
|
-
[git, "diff", "--exit-code", "--", *relatives],
|
|
1735
|
-
cwd=root,
|
|
1736
|
-
capture_output=True,
|
|
1737
|
-
text=True,
|
|
1738
|
-
encoding="utf-8",
|
|
1739
|
-
errors="replace",
|
|
1740
|
-
check=False,
|
|
1741
|
-
)
|
|
1746
|
+
completed = _run_argv(["git", "diff", "--exit-code", "--", *relatives], root)
|
|
1742
1747
|
output = "\n".join(
|
|
1743
1748
|
["\n".join(f"wrote {path}" for path in written), completed.stdout]
|
|
1744
1749
|
)
|
|
@@ -1941,17 +1946,7 @@ def _run_api_client(root: pathlib.Path) -> tuple[int, str]:
|
|
|
1941
1946
|
return 1, f"could not export the OpenAPI document: {refusal}"
|
|
1942
1947
|
finally:
|
|
1943
1948
|
os.chdir(previous)
|
|
1944
|
-
|
|
1945
|
-
executable = shutil.which(argv[0]) or argv[0]
|
|
1946
|
-
completed = subprocess.run( # noqa: S603 - fixed argv, shell=False
|
|
1947
|
-
[executable, *argv[1:]],
|
|
1948
|
-
cwd=root,
|
|
1949
|
-
capture_output=True,
|
|
1950
|
-
text=True,
|
|
1951
|
-
encoding="utf-8",
|
|
1952
|
-
errors="replace",
|
|
1953
|
-
check=False,
|
|
1954
|
-
)
|
|
1949
|
+
completed = _run_argv(["npm", "--prefix", "frontend", "run", "generate"], root)
|
|
1955
1950
|
output = f"wrote {written.name}\n{completed.stdout}{completed.stderr}"
|
|
1956
1951
|
if completed.returncode != 0:
|
|
1957
1952
|
output += (
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|