katalyst-taxonomy-cli 2.0.1a2__tar.gz → 2.0.1a4__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.
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/PKG-INFO +8 -1
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/pyproject.toml +13 -1
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/_wiring.py +4 -2
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/app.py +72 -3
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_schemas.py +26 -2
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/core.py +121 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/create.py +38 -7
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/init.py +1 -1
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/lifecycle.py +3 -1
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/lint.py +45 -18
- katalyst_taxonomy_cli-2.0.1a4/src/katalyst_taxonomy_cli/commands/migrate.py +210 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/plugin.py +17 -2
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/relate.py +31 -5
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/relocate.py +16 -5
- katalyst_taxonomy_cli-2.0.1a4/src/katalyst_taxonomy_cli/commands/rescaffold.py +149 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/status.py +45 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/update.py +55 -4
- katalyst_taxonomy_cli-2.0.1a4/src/katalyst_taxonomy_cli/fields.py +117 -0
- katalyst_taxonomy_cli-2.0.1a4/tests/test_backend_option.py +135 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_cli_version.py +29 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_schemas_command.py +37 -3
- katalyst_taxonomy_cli-2.0.1a4/tests/test_core_schema.py +226 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_create.py +41 -1
- katalyst_taxonomy_cli-2.0.1a4/tests/test_create_owners.py +58 -0
- katalyst_taxonomy_cli-2.0.1a4/tests/test_import_inside_repo.py +89 -0
- katalyst_taxonomy_cli-2.0.1a4/tests/test_labels_annotations.py +289 -0
- katalyst_taxonomy_cli-2.0.1a4/tests/test_lifecycle_kind_selector.py +113 -0
- katalyst_taxonomy_cli-2.0.1a4/tests/test_lint_profile_graph.py +155 -0
- katalyst_taxonomy_cli-2.0.1a4/tests/test_migrate.py +142 -0
- katalyst_taxonomy_cli-2.0.1a4/tests/test_optional_extras.py +69 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_plugin_command.py +32 -1
- katalyst_taxonomy_cli-2.0.1a4/tests/test_template_merge.py +201 -0
- katalyst_taxonomy_cli-2.0.1a2/src/katalyst_taxonomy_cli/fields.py +0 -70
- katalyst_taxonomy_cli-2.0.1a2/tests/test_core_schema.py +0 -95
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/.gitignore +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/__init__.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/__main__.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/__init__.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/api.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/backend.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/bundle.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/clone.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_filters.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_home.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_plugins.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_profile.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_sources.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/deps.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/diff.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/drafts.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/export.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/kinds.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/promote.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/query.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/remove.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/resolve.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/rollback.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/self_update.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/show.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/sync.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/versions.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/context.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/discovery.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/install.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/options.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/output.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/py.typed +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/conftest.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_add.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_add_bundle.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_api_command.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_clone.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_command_signatures.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_command.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_explain.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_home.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_plugins_command.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_profiles.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_sources.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_create_typed_and_variants.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_deps.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_git_backend_cli.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_github_backend_cli.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_install.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_kinds.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_kinds_plugin_settings.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_lint_output.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_lint_strict.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_output.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_plugin_gating.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_relate_update.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_relocate.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_remove.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_repository_targets.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_self_update.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: katalyst-taxonomy-cli
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1a4
|
|
4
4
|
Summary: CLI for katalyst-taxonomy — the kata command
|
|
5
5
|
Author: eSimplicity
|
|
6
6
|
License: Proprietary
|
|
@@ -8,5 +8,12 @@ Requires-Python: >=3.12
|
|
|
8
8
|
Requires-Dist: click>=8.1
|
|
9
9
|
Requires-Dist: katalyst-core<1,>=0.1
|
|
10
10
|
Requires-Dist: katalyst-engine<6,>=5
|
|
11
|
+
Requires-Dist: katalyst-plugin-context<1,>=0.2
|
|
12
|
+
Requires-Dist: katalyst-plugin-harness<1,>=0.1
|
|
13
|
+
Requires-Dist: katalyst-plugin-scaffolding<2,>=1
|
|
11
14
|
Requires-Dist: katalyst-taxonomy-mcp<5,>=4
|
|
12
15
|
Requires-Dist: katalyst-taxonomy<6,>=5
|
|
16
|
+
Provides-Extra: bard
|
|
17
|
+
Requires-Dist: katalyst-plugin-bard<1,>=0.1; extra == 'bard'
|
|
18
|
+
Provides-Extra: tui
|
|
19
|
+
Requires-Dist: katalyst-taxonomy-tui<3,>=2; extra == 'tui'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "katalyst-taxonomy-cli"
|
|
3
|
-
version = "2.0.
|
|
3
|
+
version = "2.0.1a4"
|
|
4
4
|
description = "CLI for katalyst-taxonomy — the kata command"
|
|
5
5
|
license = { text = "Proprietary" }
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -15,8 +15,20 @@ dependencies = [
|
|
|
15
15
|
"katalyst-engine>=5,<6",
|
|
16
16
|
"click>=8.1",
|
|
17
17
|
"katalyst-core>=0.1,<1",
|
|
18
|
+
# The plugins that ship with kata: `kata tax context|harness|add`. Bounded to the
|
|
19
|
+
# config-rooted runtime, like the plugins' own floors on this package (a cycle pip
|
|
20
|
+
# and uv both resolve: a plugin's command imports this package's context and options).
|
|
21
|
+
"katalyst-plugin-context>=0.2,<1",
|
|
22
|
+
"katalyst-plugin-harness>=0.1,<1",
|
|
23
|
+
"katalyst-plugin-scaffolding>=1,<2",
|
|
18
24
|
]
|
|
19
25
|
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
# `kata tax bard`: the web UI plugin.
|
|
28
|
+
bard = ["katalyst-plugin-bard>=0.1,<1"]
|
|
29
|
+
# `kata tui`: the terminal UI.
|
|
30
|
+
tui = ["katalyst-taxonomy-tui>=2,<3"]
|
|
31
|
+
|
|
20
32
|
[project.scripts]
|
|
21
33
|
kata = "katalyst_taxonomy_cli.__main__:main"
|
|
22
34
|
|
{katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/_wiring.py
RENAMED
|
@@ -123,7 +123,9 @@ def _named(registry: BackendRegistry, target: str | None, address: str) -> tuple
|
|
|
123
123
|
A prefix and ``--backend`` that disagree is an error rather than one silently
|
|
124
124
|
winning: the command was told two different stores.
|
|
125
125
|
"""
|
|
126
|
-
|
|
126
|
+
from katalyst_taxonomy.config.routing import split_backend_prefix
|
|
127
|
+
|
|
128
|
+
if address and (target is None or split_backend_prefix(address)[0] is not None):
|
|
127
129
|
parsed = registry.address(address)
|
|
128
130
|
if target and target != parsed.backend:
|
|
129
131
|
raise click.ClickException(
|
|
@@ -135,7 +137,7 @@ def _named(registry: BackendRegistry, target: str | None, address: str) -> tuple
|
|
|
135
137
|
from katalyst_taxonomy.config.routing import UnknownBackendError
|
|
136
138
|
|
|
137
139
|
raise UnknownBackendError(target, registry.profile_backends())
|
|
138
|
-
return target,
|
|
140
|
+
return target, address
|
|
139
141
|
return None, ""
|
|
140
142
|
|
|
141
143
|
|
{katalyst_taxonomy_cli-2.0.1a2 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/app.py
RENAMED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import importlib.util
|
|
5
6
|
import subprocess
|
|
6
7
|
import sys
|
|
7
8
|
from pathlib import Path
|
|
@@ -77,6 +78,42 @@ class KataGroup(click.Group):
|
|
|
77
78
|
raise click.ClickException(str(exc)) from exc
|
|
78
79
|
|
|
79
80
|
|
|
81
|
+
def _missing_extra(name: str) -> str | None:
|
|
82
|
+
"""The extra of this package named *name* whose requirements are not installed.
|
|
83
|
+
|
|
84
|
+
A command group a plugin contributes is offered as an extra of the same name
|
|
85
|
+
(``katalyst-taxonomy-cli[<name>]``). Read from this distribution's own metadata, so
|
|
86
|
+
the CLI names no plugin: ``None`` when there is no such extra, or it is installed.
|
|
87
|
+
"""
|
|
88
|
+
import importlib.metadata as metadata
|
|
89
|
+
import re
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
requirements = metadata.requires("katalyst-taxonomy-cli") or []
|
|
93
|
+
except metadata.PackageNotFoundError:
|
|
94
|
+
return None
|
|
95
|
+
marker = re.compile(rf"""extra\s*==\s*['"]{re.escape(name)}['"]""")
|
|
96
|
+
wanted = [r for r in requirements if marker.search(r)]
|
|
97
|
+
if not wanted:
|
|
98
|
+
return None
|
|
99
|
+
for requirement in wanted:
|
|
100
|
+
distribution = re.split(r"[\s;<>=!~\[(]", requirement, maxsplit=1)[0]
|
|
101
|
+
try:
|
|
102
|
+
metadata.distribution(distribution)
|
|
103
|
+
except metadata.PackageNotFoundError:
|
|
104
|
+
return name
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _not_installed_message(extra: str, label: str) -> str:
|
|
109
|
+
return (
|
|
110
|
+
f"{label} is not installed. Install it with:\n"
|
|
111
|
+
f" pip install 'katalyst-taxonomy-cli[{extra}]'\n"
|
|
112
|
+
f"or, for a uv tool install:\n"
|
|
113
|
+
f" uv tool install 'katalyst-taxonomy-cli[{extra}]'"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
80
117
|
class TaxGroup(click.Group):
|
|
81
118
|
"""The ``tax`` group, which resolves plugin-contributed commands **lazily**.
|
|
82
119
|
|
|
@@ -114,6 +151,16 @@ class TaxGroup(click.Group):
|
|
|
114
151
|
self._ensure_plugin_commands(ctx)
|
|
115
152
|
return super().get_command(ctx, cmd_name)
|
|
116
153
|
|
|
154
|
+
def resolve_command(
|
|
155
|
+
self, ctx: click.Context, args: list[str]
|
|
156
|
+
) -> tuple[str | None, click.Command | None, list[str]]:
|
|
157
|
+
name = args[0] if args else None
|
|
158
|
+
if name and self.get_command(ctx, name) is None:
|
|
159
|
+
extra = _missing_extra(name)
|
|
160
|
+
if extra is not None:
|
|
161
|
+
raise click.ClickException(_not_installed_message(extra, f"`kata tax {name}`"))
|
|
162
|
+
return super().resolve_command(ctx, args)
|
|
163
|
+
|
|
117
164
|
def _ensure_plugin_commands(self, ctx: click.Context) -> None:
|
|
118
165
|
if self._plugin_commands_resolved:
|
|
119
166
|
return
|
|
@@ -159,6 +206,13 @@ def _installed_katalyst_packages() -> list[tuple[str, str]]:
|
|
|
159
206
|
return sorted(found.items())
|
|
160
207
|
|
|
161
208
|
|
|
209
|
+
def _schema_versions() -> list[tuple[str, str]]:
|
|
210
|
+
"""Every schema installed (katalyst-core, and each bundle), with its version."""
|
|
211
|
+
from katalyst_engine.evolution import installed_releases
|
|
212
|
+
|
|
213
|
+
return [(release.name, str(release.version)) for release in installed_releases()]
|
|
214
|
+
|
|
215
|
+
|
|
162
216
|
def _print_version(ctx: click.Context, _param: click.Parameter, value: bool) -> None:
|
|
163
217
|
if not value or ctx.resilient_parsing:
|
|
164
218
|
return
|
|
@@ -170,6 +224,12 @@ def _print_version(ctx: click.Context, _param: click.Parameter, value: bool) ->
|
|
|
170
224
|
click.echo(f"kata {__version__}")
|
|
171
225
|
for name, version in _installed_katalyst_packages():
|
|
172
226
|
click.echo(f" {name} {version}")
|
|
227
|
+
click.echo("schemas:")
|
|
228
|
+
try:
|
|
229
|
+
for name, version in _schema_versions():
|
|
230
|
+
click.echo(f" {name} {version}")
|
|
231
|
+
except Exception as exc: # a bundle that does not load must not hide the versions
|
|
232
|
+
click.echo(f" unavailable: {exc}")
|
|
173
233
|
for line in install_lines():
|
|
174
234
|
click.echo(line)
|
|
175
235
|
try:
|
|
@@ -227,8 +287,8 @@ def _select_home(ctx: click.Context, _param: click.Parameter, value: str | None)
|
|
|
227
287
|
# Not eager, so it runs after --home (which is) and reports the home chosen.
|
|
228
288
|
callback=_print_version,
|
|
229
289
|
help=(
|
|
230
|
-
"Show the kata version, every installed Katalyst package, the
|
|
231
|
-
"was built from) and the home, then exit."
|
|
290
|
+
"Show the kata version, every installed Katalyst package, the schema bundles with "
|
|
291
|
+
"their versions, the install (and what it was built from) and the home, then exit."
|
|
232
292
|
),
|
|
233
293
|
)
|
|
234
294
|
@click.option(
|
|
@@ -295,7 +355,12 @@ def _configure_logging() -> None:
|
|
|
295
355
|
|
|
296
356
|
@cli.command("tui")
|
|
297
357
|
def tui_cmd() -> None:
|
|
298
|
-
"""Launch the interactive terminal UI.
|
|
358
|
+
"""Launch the interactive terminal UI.
|
|
359
|
+
|
|
360
|
+
The UI is an extra: ``pip install 'katalyst-taxonomy-cli[tui]'``.
|
|
361
|
+
"""
|
|
362
|
+
if importlib.util.find_spec("katalyst_taxonomy_tui") is None:
|
|
363
|
+
raise click.ClickException(_not_installed_message("tui", "the terminal UI"))
|
|
299
364
|
try:
|
|
300
365
|
subprocess.run(
|
|
301
366
|
[sys.executable, "-m", "katalyst_taxonomy_tui"],
|
|
@@ -466,12 +531,14 @@ def _register_core_commands() -> None:
|
|
|
466
531
|
kinds,
|
|
467
532
|
lifecycle,
|
|
468
533
|
lint,
|
|
534
|
+
migrate,
|
|
469
535
|
plugin,
|
|
470
536
|
promote,
|
|
471
537
|
query,
|
|
472
538
|
relate,
|
|
473
539
|
relocate,
|
|
474
540
|
remove,
|
|
541
|
+
rescaffold,
|
|
475
542
|
resolve,
|
|
476
543
|
rollback,
|
|
477
544
|
self_update,
|
|
@@ -505,6 +572,7 @@ def _register_core_commands() -> None:
|
|
|
505
572
|
tax.add_command(show.show)
|
|
506
573
|
tax.add_command(kinds.kinds)
|
|
507
574
|
tax.add_command(lint.lint)
|
|
575
|
+
tax.add_command(migrate.migrate)
|
|
508
576
|
tax.add_command(deps.deps)
|
|
509
577
|
tax.add_command(status.status)
|
|
510
578
|
tax.add_command(rollback.rollback)
|
|
@@ -528,6 +596,7 @@ def _register_core_commands() -> None:
|
|
|
528
596
|
tax.add_command(export.export)
|
|
529
597
|
tax.add_command(backend.backend)
|
|
530
598
|
tax.add_command(remove.remove)
|
|
599
|
+
tax.add_command(rescaffold.rescaffold)
|
|
531
600
|
|
|
532
601
|
|
|
533
602
|
def resolve_plugin_commands() -> None:
|
|
@@ -60,7 +60,11 @@ def build_schemas_payload(
|
|
|
60
60
|
from katalyst_taxonomy.config.schemas import selection_from_config
|
|
61
61
|
from katalyst_taxonomy.engine import bridge
|
|
62
62
|
from katalyst_taxonomy.services.schema_gate import check_declared_bundles
|
|
63
|
-
from katalyst_taxonomy.services.schema_report import
|
|
63
|
+
from katalyst_taxonomy.services.schema_report import (
|
|
64
|
+
core_schema,
|
|
65
|
+
schema_versions,
|
|
66
|
+
schemas_payload,
|
|
67
|
+
)
|
|
64
68
|
from katalyst_taxonomy.sources.template_resolver import TemplateResolver
|
|
65
69
|
|
|
66
70
|
effective = config_resolver().resolve(profile=profile)
|
|
@@ -90,6 +94,8 @@ def build_schemas_payload(
|
|
|
90
94
|
if candidate.distribution
|
|
91
95
|
},
|
|
92
96
|
declared=declared,
|
|
97
|
+
schema_versions=schema_versions(candidates),
|
|
98
|
+
core_schema=core_schema(),
|
|
93
99
|
scaffold_providers=resolver.scaffold_template_providers(),
|
|
94
100
|
repo_root=None,
|
|
95
101
|
filesystem=RealFilesystem(),
|
|
@@ -155,19 +161,24 @@ def _render_text(out: OutputPort, payload: dict[str, Any]) -> None:
|
|
|
155
161
|
]
|
|
156
162
|
)
|
|
157
163
|
|
|
164
|
+
core = payload.get("core")
|
|
165
|
+
if core:
|
|
166
|
+
out.key_value([("katalyst-core schema", _schema_text(core))])
|
|
167
|
+
|
|
158
168
|
out.heading("Bundles")
|
|
159
169
|
bundles: list[dict[str, Any]] = payload["bundles"]
|
|
160
170
|
if not bundles:
|
|
161
171
|
out.info("No schema bundles discovered.")
|
|
162
172
|
else:
|
|
163
173
|
out.table(
|
|
164
|
-
["", "bundle", "distribution", "version", "kinds", "note"],
|
|
174
|
+
["", "bundle", "distribution", "version", "schema", "kinds", "note"],
|
|
165
175
|
[
|
|
166
176
|
[
|
|
167
177
|
"*" if entry["enabled"] else "x",
|
|
168
178
|
str(entry["name"]),
|
|
169
179
|
str(entry.get("distribution") or "-"),
|
|
170
180
|
str(entry.get("resolvedVersion") or entry["version"] or "-"),
|
|
181
|
+
_schema_text(entry.get("schema")),
|
|
171
182
|
str(len(entry["kinds"])),
|
|
172
183
|
str(
|
|
173
184
|
entry["reason"] or entry.get("sourceWarning") or entry["entryPoint"] or "-"
|
|
@@ -239,6 +250,19 @@ def _render_text(out: OutputPort, payload: dict[str, Any]) -> None:
|
|
|
239
250
|
)
|
|
240
251
|
|
|
241
252
|
|
|
253
|
+
def _schema_text(schema: dict[str, Any] | None) -> str:
|
|
254
|
+
"""``9.0.0 (unreleased; latest archived 8.0.0)``: a schema's version and archive state."""
|
|
255
|
+
if not schema:
|
|
256
|
+
return "-"
|
|
257
|
+
from katalyst_engine.core.version import Version
|
|
258
|
+
from katalyst_engine.evolution import ArchiveState
|
|
259
|
+
|
|
260
|
+
archive = schema["archive"]
|
|
261
|
+
latest = archive["latest"]
|
|
262
|
+
state = ArchiveState(archive["state"], Version.parse(latest) if latest else None)
|
|
263
|
+
return f"{schema['version']} ({state})"
|
|
264
|
+
|
|
265
|
+
|
|
242
266
|
def attach(group: click.Group) -> None:
|
|
243
267
|
"""Add ``schemas`` to the ``kata config`` group.
|
|
244
268
|
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
the diff
|
|
24
24
|
kata core schema archive --bundle B
|
|
25
25
|
archive the current modules as the released version
|
|
26
|
+
kata core schema docs [PATH...] [--bundle B] [--check] [--kind K]
|
|
27
|
+
write the generated field tables of the pages
|
|
28
|
+
under PATH; --check fails when any is stale
|
|
29
|
+
kata core schema changelog [--bundle B] [--from VERSION]
|
|
30
|
+
the "Schema changes" section of a release: what was
|
|
31
|
+
added, what broke, and the migration step for each
|
|
26
32
|
|
|
27
33
|
Records and graphs are read against every installed module (``katalyst_core.modules``).
|
|
28
34
|
"""
|
|
@@ -466,3 +472,118 @@ def schema_archive(bundle: str) -> None:
|
|
|
466
472
|
except (FileExistsError, SchemaVersionMismatchError) as exc:
|
|
467
473
|
raise click.ClickException(str(exc)) from exc
|
|
468
474
|
click.echo(f"Archived {release.name} {release.version} in {target}")
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
# Named from the function (``docs``), so no command-name literal reads as a bundle name.
|
|
478
|
+
@schema.command()
|
|
479
|
+
@click.argument("paths", nargs=-1, type=click.Path(exists=True, path_type=Path))
|
|
480
|
+
@click.option(
|
|
481
|
+
"--bundle",
|
|
482
|
+
"bundle",
|
|
483
|
+
default=None,
|
|
484
|
+
help="Read kinds from this schema only (needed when two define the same kind).",
|
|
485
|
+
)
|
|
486
|
+
@click.option(
|
|
487
|
+
"--check",
|
|
488
|
+
is_flag=True,
|
|
489
|
+
help="Write nothing; fail when a generated region is stale (for CI).",
|
|
490
|
+
)
|
|
491
|
+
@click.option(
|
|
492
|
+
"--kind",
|
|
493
|
+
"kind",
|
|
494
|
+
default=None,
|
|
495
|
+
help="Print the markdown for one kind (to start a page), and write nothing.",
|
|
496
|
+
)
|
|
497
|
+
def docs(paths: tuple[Path, ...], bundle: str | None, check: bool, kind: str | None) -> None:
|
|
498
|
+
"""Write the generated field tables of the markdown pages under PATH.
|
|
499
|
+
|
|
500
|
+
A page marks where a table goes with a line of its own, ``<!-- generated:kind:NAME -->``
|
|
501
|
+
(a kind's parent and fields: type, required, allowed values, references and their
|
|
502
|
+
relationship types, descriptions) or ``<!-- generated:wing:NAME -->`` (a wing's kinds:
|
|
503
|
+
where each is stored and its parent), and closes it with ``<!-- /generated -->``. The
|
|
504
|
+
text between is rewritten from the schemas' LinkML modules; everything else on the page
|
|
505
|
+
is yours.
|
|
506
|
+
"""
|
|
507
|
+
from katalyst_engine.schema_docs import (
|
|
508
|
+
DocsError,
|
|
509
|
+
RegionError,
|
|
510
|
+
catalog_from_releases,
|
|
511
|
+
page_files,
|
|
512
|
+
refresh_page,
|
|
513
|
+
render_kind,
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
if kind is None and not paths:
|
|
517
|
+
raise click.UsageError("name the pages to write: one or more files or directories")
|
|
518
|
+
catalog = catalog_from_releases(_releases(bundle))
|
|
519
|
+
try:
|
|
520
|
+
if kind is not None:
|
|
521
|
+
click.echo(render_kind(catalog.kind(kind, bundle=bundle)))
|
|
522
|
+
return
|
|
523
|
+
stale_total = 0
|
|
524
|
+
for page in page_files(paths):
|
|
525
|
+
text = page.read_text(encoding="utf-8")
|
|
526
|
+
try:
|
|
527
|
+
new_text, stale = refresh_page(text, catalog, bundle=bundle)
|
|
528
|
+
except (RegionError, DocsError) as exc:
|
|
529
|
+
raise click.ClickException(f"{page}: {exc}") from exc
|
|
530
|
+
stale_total += len(stale)
|
|
531
|
+
for region in stale:
|
|
532
|
+
click.echo(f"{page}:{region.line}: generated:{region.label} is stale")
|
|
533
|
+
if stale and not check:
|
|
534
|
+
page.write_text(new_text, encoding="utf-8")
|
|
535
|
+
except DocsError as exc:
|
|
536
|
+
raise click.ClickException(str(exc)) from exc
|
|
537
|
+
if check:
|
|
538
|
+
if stale_total:
|
|
539
|
+
raise click.ClickException(
|
|
540
|
+
f"{stale_total} generated region(s) are stale; "
|
|
541
|
+
f"run kata core schema docs {' '.join(str(p) for p in paths)}"
|
|
542
|
+
)
|
|
543
|
+
click.echo("generated regions are current")
|
|
544
|
+
else:
|
|
545
|
+
click.echo(f"Updated {stale_total} generated region(s)")
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
@schema.command("changelog")
|
|
549
|
+
@_BUNDLE
|
|
550
|
+
@click.option(
|
|
551
|
+
"--from",
|
|
552
|
+
"since",
|
|
553
|
+
default=None,
|
|
554
|
+
help="The archived version to compare with (default: the latest).",
|
|
555
|
+
)
|
|
556
|
+
@click.option(
|
|
557
|
+
"--heading-level",
|
|
558
|
+
"level",
|
|
559
|
+
type=click.IntRange(1, 5),
|
|
560
|
+
default=2,
|
|
561
|
+
show_default=True,
|
|
562
|
+
help="The level of the section's heading (2: ##).",
|
|
563
|
+
)
|
|
564
|
+
def schema_changelog(bundle: str | None, since: str | None, level: int) -> None:
|
|
565
|
+
"""The "Schema changes" section of a release, from the classified diff and the migration.
|
|
566
|
+
|
|
567
|
+
For each schema whose modules changed since its last archived version: what was added,
|
|
568
|
+
what broke, and for each breaking change the `kata tax migrate` step that handles it
|
|
569
|
+
(or the reason it is accepted). Markdown on stdout, for the release notes.
|
|
570
|
+
"""
|
|
571
|
+
from katalyst_engine.core.version import Version
|
|
572
|
+
from katalyst_engine.evolution import build_changelog, render_changelog
|
|
573
|
+
|
|
574
|
+
logs: list[Any] = []
|
|
575
|
+
for release in _releases(bundle):
|
|
576
|
+
try:
|
|
577
|
+
old = Version.parse(since) if since else None
|
|
578
|
+
log = build_changelog(release, old)
|
|
579
|
+
except (ValueError, FileNotFoundError) as exc:
|
|
580
|
+
raise click.ClickException(str(exc)) from exc
|
|
581
|
+
if log is None:
|
|
582
|
+
click.echo(f"{release.name}: no released version is archived", err=True)
|
|
583
|
+
else:
|
|
584
|
+
logs.append(log)
|
|
585
|
+
text = render_changelog(logs, level=level)
|
|
586
|
+
if not text:
|
|
587
|
+
click.echo("No schema changes since the last released version.", err=True)
|
|
588
|
+
return
|
|
589
|
+
click.echo(text, nl=False)
|
|
@@ -41,7 +41,24 @@ from katalyst_taxonomy_cli.context import CliContext
|
|
|
41
41
|
"from_file",
|
|
42
42
|
default=None,
|
|
43
43
|
metavar="PATH",
|
|
44
|
-
help=
|
|
44
|
+
help=(
|
|
45
|
+
"Spec fields from a YAML or JSON file (- for stdin), or a whole document's spec, "
|
|
46
|
+
"labels and annotations; --set, --label and --annotation apply over them."
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
@click.option(
|
|
50
|
+
"--label",
|
|
51
|
+
"label_pairs",
|
|
52
|
+
multiple=True,
|
|
53
|
+
metavar="KEY=VALUE",
|
|
54
|
+
help="Set a label (metadata.labels), which filters and permission grants match. Repeat.",
|
|
55
|
+
)
|
|
56
|
+
@click.option(
|
|
57
|
+
"--annotation",
|
|
58
|
+
"annotation_pairs",
|
|
59
|
+
multiple=True,
|
|
60
|
+
metavar="KEY=VALUE",
|
|
61
|
+
help="Set an annotation (metadata.annotations), a record nothing matches on. Repeat.",
|
|
45
62
|
)
|
|
46
63
|
@click.option("--target-dir", default=None, help="Override output directory.")
|
|
47
64
|
@click.option(
|
|
@@ -75,6 +92,8 @@ def create(
|
|
|
75
92
|
scaffold_variant: str | None,
|
|
76
93
|
set_fields: tuple[str, ...],
|
|
77
94
|
from_file: str | None,
|
|
95
|
+
label_pairs: tuple[str, ...],
|
|
96
|
+
annotation_pairs: tuple[str, ...],
|
|
78
97
|
target_dir: str | None,
|
|
79
98
|
backend_target: str | None,
|
|
80
99
|
dry_run: bool,
|
|
@@ -96,24 +115,34 @@ def create(
|
|
|
96
115
|
a value of its type (boolean, integer, list, map) and allowed values, a reference
|
|
97
116
|
naming a node of the referenced kind.
|
|
98
117
|
|
|
118
|
+
--label KEY=VALUE and --annotation KEY=VALUE go in the node's metadata, on any kind.
|
|
119
|
+
A key is a name of letters, digits, '-', '_' and '.' (at most 63), optionally after a
|
|
120
|
+
DNS prefix and '/' (example.org/team); a label's value is such a name (or empty), an
|
|
121
|
+
annotation's any text.
|
|
122
|
+
|
|
99
123
|
Examples:
|
|
100
124
|
|
|
101
|
-
kata tax create
|
|
125
|
+
kata tax create layer api --under shop.backend --set audited=false --set tier=gold
|
|
126
|
+
|
|
127
|
+
kata tax create layer worker --under shop.backend --set priority=10 --set limits.region=eu-west
|
|
102
128
|
|
|
103
|
-
kata tax create
|
|
129
|
+
kata tax create capability checkout --under shop --set 'hooks=[{"on":"push","branches":["main"]}]'
|
|
104
130
|
|
|
105
|
-
kata tax create
|
|
131
|
+
kata tax create stack web --under shop --set limits.log_level=debug
|
|
106
132
|
|
|
107
|
-
kata tax create
|
|
133
|
+
kata tax create layer api --under shop.backend --label team=payments --annotation source=docs/api.md
|
|
108
134
|
"""
|
|
109
135
|
from katalyst_taxonomy.adapters.orchestration import run_create
|
|
110
136
|
from katalyst_taxonomy.config.routing import split_backend_prefix
|
|
111
137
|
|
|
112
|
-
from katalyst_taxonomy_cli.fields import parse_set,
|
|
138
|
+
from katalyst_taxonomy_cli.fields import parse_pairs, parse_set, read_node_file
|
|
113
139
|
|
|
114
140
|
from .._wiring import resolve_backend
|
|
115
141
|
|
|
116
142
|
resolved_parent = parent or parent_alt
|
|
143
|
+
node_file = read_node_file(from_file)
|
|
144
|
+
labels = {**node_file.labels, **parse_pairs(label_pairs, option="--label")}
|
|
145
|
+
annotations = {**node_file.annotations, **parse_pairs(annotation_pairs, option="--annotation")}
|
|
117
146
|
|
|
118
147
|
# The backend is `--backend`, or the `backend:` prefix of NAME. Without either, the
|
|
119
148
|
# repository is the one the current directory is inside — even with one repository,
|
|
@@ -134,7 +163,9 @@ def create(
|
|
|
134
163
|
description=description,
|
|
135
164
|
owners=owners or (),
|
|
136
165
|
fields=parse_set(set_fields),
|
|
137
|
-
spec=
|
|
166
|
+
spec=node_file.spec,
|
|
167
|
+
labels=labels,
|
|
168
|
+
annotations=annotations,
|
|
138
169
|
parent=resolved_parent,
|
|
139
170
|
scaffold_variant=scaffold_variant,
|
|
140
171
|
target_dir=target_dir,
|
|
@@ -66,7 +66,7 @@ def init(
|
|
|
66
66
|
|
|
67
67
|
Writes the taxonomy root (.global/taxonomy/) and version.yaml — together the
|
|
68
68
|
"initialized" marker — and registers the repository as a filesystem backend of a
|
|
69
|
-
profile in the
|
|
69
|
+
profile in the active home's config (`kata config path` prints it): the --profile
|
|
70
70
|
profile (created when it is not declared), else the active one; a file with no
|
|
71
71
|
profiles gets a new `local`, made active. The backend is named --backend, else for
|
|
72
72
|
the directory; a name the profile already declares for another directory is refused
|
|
@@ -118,10 +118,12 @@ def import_(ctx: CliContext, file: Path, backend_target: str) -> None:
|
|
|
118
118
|
resolved.backend,
|
|
119
119
|
[cast("dict[str, Any]", d) for d in documents],
|
|
120
120
|
create=lambda request: create_node(resolved.backend, request),
|
|
121
|
+
source=file,
|
|
121
122
|
)
|
|
122
123
|
except ImportError_ as exc:
|
|
123
124
|
ctx.output.echo(f"Error: {exc}")
|
|
124
|
-
|
|
125
|
+
if not exc.left_behind:
|
|
126
|
+
ctx.output.echo("Error: nothing was imported.")
|
|
125
127
|
raise SystemExit(1) from exc
|
|
126
128
|
for fqtn in created:
|
|
127
129
|
ctx.output.echo(f"Imported: {resolved.name}:{fqtn}")
|
|
@@ -16,21 +16,39 @@ from katalyst_taxonomy_cli.context import CliContext
|
|
|
16
16
|
is_flag=True,
|
|
17
17
|
help=(
|
|
18
18
|
"Treat advisory findings (governance id collisions, duplicate names, undeclared "
|
|
19
|
-
"spec keys, misplaced documents) as errors."
|
|
19
|
+
"spec keys, misplaced documents, shape findings and shape warnings) as errors."
|
|
20
|
+
),
|
|
21
|
+
)
|
|
22
|
+
@click.option(
|
|
23
|
+
"--backend",
|
|
24
|
+
default=None,
|
|
25
|
+
help=(
|
|
26
|
+
"Report only this backend's findings. Its documents are still checked against "
|
|
27
|
+
"the graph of every backend the profile reads."
|
|
20
28
|
),
|
|
21
29
|
)
|
|
22
30
|
@click.pass_obj
|
|
23
|
-
def lint(ctx: CliContext, verbose: bool, strict: bool) -> None:
|
|
31
|
+
def lint(ctx: CliContext, verbose: bool, strict: bool, backend: str | None) -> None:
|
|
24
32
|
"""Validate taxonomy against schema — everything the active profile reads.
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
Every check runs over one graph: the documents of every backend the profile reads,
|
|
35
|
+
so a reference into another backend (other:shop.storefront.checkout) is checked
|
|
36
|
+
against the node it names. Each finding is labelled [backend] when the profile
|
|
37
|
+
reads more than one. Repository-local checks (plugin rules, plugin-document schemas,
|
|
38
|
+
version constraints) run once per filesystem backend. A backend that cannot be read
|
|
39
|
+
is an error naming it.
|
|
29
40
|
"""
|
|
30
41
|
from katalyst_taxonomy.adapters.orchestration import run_lint
|
|
42
|
+
from katalyst_taxonomy.config.routing import BackendUnavailableError
|
|
31
43
|
|
|
32
44
|
try:
|
|
33
|
-
result = run_lint(profile=ctx.profile)
|
|
45
|
+
result = run_lint(profile=ctx.profile, backend=backend)
|
|
46
|
+
except BackendUnavailableError as exc:
|
|
47
|
+
raise click.ClickException(
|
|
48
|
+
f"{exc}\nLint checks the graph of every backend the profile reads, so each "
|
|
49
|
+
"must be readable: make it reachable, or lint with a profile that does not "
|
|
50
|
+
"read it."
|
|
51
|
+
) from exc
|
|
34
52
|
except ValueError as exc:
|
|
35
53
|
# A `filters` block that cannot be honoured — `include: []`, a clause
|
|
36
54
|
# declaring `match.backend` — is a user error (#217), and lint does not go
|
|
@@ -39,6 +57,17 @@ def lint(ctx: CliContext, verbose: bool, strict: bool) -> None:
|
|
|
39
57
|
# it still reaches the root group and still exits 7.
|
|
40
58
|
raise click.ClickException(str(exc)) from exc
|
|
41
59
|
|
|
60
|
+
labelled = len(result.graph_backends) > 1
|
|
61
|
+
|
|
62
|
+
def _where(path: object) -> str:
|
|
63
|
+
"""``[backend] path: `` for a finding about a file, as much as is known."""
|
|
64
|
+
label = ""
|
|
65
|
+
if labelled and isinstance(path, Path):
|
|
66
|
+
owner = result.backend_of(path)
|
|
67
|
+
label = f"[{owner}] " if owner else ""
|
|
68
|
+
shown = f"{path}: " if path is not None and str(path) not in ("", ".") else ""
|
|
69
|
+
return f"{label}{shown}"
|
|
70
|
+
|
|
42
71
|
# Version constraint warnings are advisory (they do not make the taxonomy
|
|
43
72
|
# invalid) but must still be surfaced — previously they were unreachable
|
|
44
73
|
# when the taxonomy was otherwise valid, and only shown under --verbose.
|
|
@@ -99,21 +128,15 @@ def lint(ctx: CliContext, verbose: bool, strict: bool) -> None:
|
|
|
99
128
|
("Misplaced documents", result.misplaced_documents),
|
|
100
129
|
("Relationships no kind carries", result.uncarried_relationships),
|
|
101
130
|
("Shape findings", result.shape_findings),
|
|
131
|
+
("Shape warnings", result.shape_warnings),
|
|
102
132
|
):
|
|
103
133
|
if not found:
|
|
104
134
|
continue
|
|
105
135
|
ctx.output.echo(f"\n{title} ({len(found)}):")
|
|
106
136
|
for violation in found:
|
|
107
|
-
|
|
108
|
-
ctx.output.echo(f" {severity}: {where}{violation.message}")
|
|
137
|
+
ctx.output.echo(f" {severity}: {_where(violation.path)}{violation.message}")
|
|
109
138
|
if not strict:
|
|
110
139
|
ctx.output.echo(" Re-run with --strict to fail on these.")
|
|
111
|
-
# A shape's warnings (e.g. coverage) are a report, strict or not.
|
|
112
|
-
if result.shape_warnings:
|
|
113
|
-
ctx.output.echo(f"\nShape warnings ({len(result.shape_warnings)}):")
|
|
114
|
-
for violation in result.shape_warnings:
|
|
115
|
-
where = f"{violation.path}: " if str(violation.path) not in ("", ".") else ""
|
|
116
|
-
ctx.output.echo(f" WARNING: {where}{violation.message}")
|
|
117
140
|
|
|
118
141
|
def _print_history_notes() -> None:
|
|
119
142
|
found = result.removed_history_targets
|
|
@@ -121,8 +144,7 @@ def lint(ctx: CliContext, verbose: bool, strict: bool) -> None:
|
|
|
121
144
|
return
|
|
122
145
|
ctx.output.echo(f"\nHistory references to removed nodes ({len(found)}):")
|
|
123
146
|
for violation in found:
|
|
124
|
-
|
|
125
|
-
ctx.output.echo(f" INFO: {where}{violation.message}")
|
|
147
|
+
ctx.output.echo(f" INFO: {_where(violation.path)}{violation.message}")
|
|
126
148
|
|
|
127
149
|
strict_findings = (
|
|
128
150
|
len(result.governance_id_collisions)
|
|
@@ -131,6 +153,7 @@ def lint(ctx: CliContext, verbose: bool, strict: bool) -> None:
|
|
|
131
153
|
+ len(result.misplaced_documents)
|
|
132
154
|
+ len(result.uncarried_relationships)
|
|
133
155
|
+ len(result.shape_findings)
|
|
156
|
+
+ len(result.shape_warnings)
|
|
134
157
|
)
|
|
135
158
|
strict_failure = strict and strict_findings > 0
|
|
136
159
|
|
|
@@ -142,6 +165,10 @@ def lint(ctx: CliContext, verbose: bool, strict: bool) -> None:
|
|
|
142
165
|
for repo in result.repositories
|
|
143
166
|
)
|
|
144
167
|
ctx.output.echo(f" Repositories checked: {repositories or '(none)'}")
|
|
168
|
+
if result.focus is not None:
|
|
169
|
+
ctx.output.echo(
|
|
170
|
+
f" Findings for: {result.focus}; graph: {', '.join(result.graph_backends)}"
|
|
171
|
+
)
|
|
145
172
|
# Show the receipts: a gate that verified nothing must not look identical
|
|
146
173
|
# to one that verified everything.
|
|
147
174
|
ctx.output.echo(
|
|
@@ -179,14 +206,14 @@ def lint(ctx: CliContext, verbose: bool, strict: bool) -> None:
|
|
|
179
206
|
if result.document_errors:
|
|
180
207
|
ctx.output.echo(f"\nDocument errors ({len(result.document_errors)}):")
|
|
181
208
|
for err in result.document_errors:
|
|
182
|
-
ctx.output.echo(f" {err.path}
|
|
209
|
+
ctx.output.echo(f" {_where(err.path)}{err.message}")
|
|
183
210
|
|
|
184
211
|
# Positioned between document and plugin-schema errors so block order is
|
|
185
212
|
# fixed across runs and corpora.
|
|
186
213
|
if result.reference_errors:
|
|
187
214
|
ctx.output.echo(f"\nReference errors ({len(result.reference_errors)}):")
|
|
188
215
|
for ref_err in result.reference_errors:
|
|
189
|
-
ctx.output.echo(f" {ref_err.path}
|
|
216
|
+
ctx.output.echo(f" {_where(ref_err.path)}{ref_err.message}")
|
|
190
217
|
|
|
191
218
|
if result.plugin_schema_errors:
|
|
192
219
|
ctx.output.echo(f"\nSchema errors ({len(result.plugin_schema_errors)}):")
|