katalyst-taxonomy-cli 2.0.1a3__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.1a3 → katalyst_taxonomy_cli-2.0.1a4}/PKG-INFO +1 -1
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/pyproject.toml +1 -1
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/app.py +17 -2
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_schemas.py +26 -2
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/core.py +121 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/create.py +34 -3
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/lifecycle.py +3 -1
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/plugin.py +17 -2
- katalyst_taxonomy_cli-2.0.1a4/src/katalyst_taxonomy_cli/commands/rescaffold.py +149 -0
- {katalyst_taxonomy_cli-2.0.1a3 → 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.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_cli_version.py +29 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_schemas_command.py +34 -0
- katalyst_taxonomy_cli-2.0.1a4/tests/test_core_schema.py +226 -0
- {katalyst_taxonomy_cli-2.0.1a3 → 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.1a3 → 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.1a3/src/katalyst_taxonomy_cli/fields.py +0 -70
- katalyst_taxonomy_cli-2.0.1a3/tests/test_core_schema.py +0 -95
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/.gitignore +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/__init__.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/__main__.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/_wiring.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/__init__.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/api.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/backend.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/bundle.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/clone.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_filters.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_home.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_plugins.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_profile.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/config_sources.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/deps.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/diff.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/drafts.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/export.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/init.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/kinds.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/lint.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/migrate.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/promote.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/query.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/relate.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/relocate.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/remove.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/resolve.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/rollback.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/self_update.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/show.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/status.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/sync.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/commands/versions.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/context.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/discovery.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/install.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/options.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/output.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/py.typed +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/conftest.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_add.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_add_bundle.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_api_command.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_backend_option.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_clone.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_command_signatures.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_command.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_explain.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_home.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_plugins_command.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_profiles.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_config_sources.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_create_typed_and_variants.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_deps.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_git_backend_cli.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_github_backend_cli.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_install.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_kinds.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_kinds_plugin_settings.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_lint_output.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_lint_profile_graph.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_lint_strict.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_migrate.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_optional_extras.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_output.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_plugin_gating.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_relate_update.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_relocate.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_remove.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_repository_targets.py +0 -0
- {katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/tests/test_self_update.py +0 -0
{katalyst_taxonomy_cli-2.0.1a3 → katalyst_taxonomy_cli-2.0.1a4}/src/katalyst_taxonomy_cli/app.py
RENAMED
|
@@ -206,6 +206,13 @@ def _installed_katalyst_packages() -> list[tuple[str, str]]:
|
|
|
206
206
|
return sorted(found.items())
|
|
207
207
|
|
|
208
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
|
+
|
|
209
216
|
def _print_version(ctx: click.Context, _param: click.Parameter, value: bool) -> None:
|
|
210
217
|
if not value or ctx.resilient_parsing:
|
|
211
218
|
return
|
|
@@ -217,6 +224,12 @@ def _print_version(ctx: click.Context, _param: click.Parameter, value: bool) ->
|
|
|
217
224
|
click.echo(f"kata {__version__}")
|
|
218
225
|
for name, version in _installed_katalyst_packages():
|
|
219
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}")
|
|
220
233
|
for line in install_lines():
|
|
221
234
|
click.echo(line)
|
|
222
235
|
try:
|
|
@@ -274,8 +287,8 @@ def _select_home(ctx: click.Context, _param: click.Parameter, value: str | None)
|
|
|
274
287
|
# Not eager, so it runs after --home (which is) and reports the home chosen.
|
|
275
288
|
callback=_print_version,
|
|
276
289
|
help=(
|
|
277
|
-
"Show the kata version, every installed Katalyst package, the
|
|
278
|
-
"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."
|
|
279
292
|
),
|
|
280
293
|
)
|
|
281
294
|
@click.option(
|
|
@@ -525,6 +538,7 @@ def _register_core_commands() -> None:
|
|
|
525
538
|
relate,
|
|
526
539
|
relocate,
|
|
527
540
|
remove,
|
|
541
|
+
rescaffold,
|
|
528
542
|
resolve,
|
|
529
543
|
rollback,
|
|
530
544
|
self_update,
|
|
@@ -582,6 +596,7 @@ def _register_core_commands() -> None:
|
|
|
582
596
|
tax.add_command(export.export)
|
|
583
597
|
tax.add_command(backend.backend)
|
|
584
598
|
tax.add_command(remove.remove)
|
|
599
|
+
tax.add_command(rescaffold.rescaffold)
|
|
585
600
|
|
|
586
601
|
|
|
587
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,6 +115,11 @@ 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
125
|
kata tax create layer api --under shop.backend --set audited=false --set tier=gold
|
|
@@ -105,15 +129,20 @@ def create(
|
|
|
105
129
|
kata tax create capability checkout --under shop --set 'hooks=[{"on":"push","branches":["main"]}]'
|
|
106
130
|
|
|
107
131
|
kata tax create stack web --under shop --set limits.log_level=debug
|
|
132
|
+
|
|
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,
|
|
@@ -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}")
|
|
@@ -79,7 +79,11 @@ def add_plugin(ctx: CliContext, source_uri: str, yes: bool) -> None:
|
|
|
79
79
|
@click.option("--json", "as_json", is_flag=True, help="Machine-readable output.")
|
|
80
80
|
@click.pass_obj
|
|
81
81
|
def list_plugins(ctx: CliContext, as_json: bool) -> None:
|
|
82
|
-
"""List plugins: entry-point packages and source installs, with their state.
|
|
82
|
+
"""List plugins: entry-point packages and source installs, with their state.
|
|
83
|
+
|
|
84
|
+
A plugin whose declared schema ranges the loaded schemas don't satisfy is
|
|
85
|
+
refused: listed, not activated, with the range and the loaded version.
|
|
86
|
+
"""
|
|
83
87
|
from katalyst_taxonomy.plugins.activation import current_activation
|
|
84
88
|
from katalyst_taxonomy.plugins.registry import ORIGIN_SOURCE
|
|
85
89
|
from katalyst_taxonomy.services.source_providers import declared_plugin_sources
|
|
@@ -102,6 +106,8 @@ def list_plugins(ctx: CliContext, as_json: bool) -> None:
|
|
|
102
106
|
"source": declared.get(name),
|
|
103
107
|
"enabled": activation.selection.is_enabled(name),
|
|
104
108
|
"active": activation.activated(name),
|
|
109
|
+
"schemas": dict(manifest.schemas),
|
|
110
|
+
"refused": activation.refused(name) or None,
|
|
105
111
|
}
|
|
106
112
|
)
|
|
107
113
|
for name, source in declared.items():
|
|
@@ -114,6 +120,8 @@ def list_plugins(ctx: CliContext, as_json: bool) -> None:
|
|
|
114
120
|
"source": source,
|
|
115
121
|
"enabled": activation.selection.is_enabled(name) if activation else True,
|
|
116
122
|
"active": False,
|
|
123
|
+
"schemas": {},
|
|
124
|
+
"refused": None,
|
|
117
125
|
}
|
|
118
126
|
)
|
|
119
127
|
rows.sort(key=lambda row: str(row["name"]))
|
|
@@ -125,9 +133,16 @@ def list_plugins(ctx: CliContext, as_json: bool) -> None:
|
|
|
125
133
|
return
|
|
126
134
|
ctx.output.echo(f"Plugins ({len(rows)}):")
|
|
127
135
|
for row in rows:
|
|
128
|
-
|
|
136
|
+
if row["active"]:
|
|
137
|
+
state = "active"
|
|
138
|
+
elif row["refused"]:
|
|
139
|
+
state = "refused"
|
|
140
|
+
else:
|
|
141
|
+
state = "enabled" if row["enabled"] else "disabled"
|
|
129
142
|
source = f" [{row['source']}]" if row["source"] else ""
|
|
130
143
|
ctx.output.echo(f" {row['name']} v{row['version']} {row['origin']} {state}{source}")
|
|
144
|
+
if row["refused"]:
|
|
145
|
+
ctx.output.echo(f" {row['refused']}")
|
|
131
146
|
|
|
132
147
|
|
|
133
148
|
@plugin.command("remove")
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""kata tax rescaffold — re-render a node's scaffold at the installed template version.
|
|
2
|
+
|
|
3
|
+
A node made from a scaffold variant keeps the files it was scaffolded with (a layer's
|
|
4
|
+
``Dockerfile``, overlays, env files). Upgrading the bundle with ``kata tax add`` upgrades
|
|
5
|
+
the template, not those files. This re-renders the node's template, as installed now,
|
|
6
|
+
and merges it three ways with the node's files, the base being the template at the
|
|
7
|
+
version the node records it was scaffolded from
|
|
8
|
+
(:mod:`katalyst_taxonomy.services.rescaffold`).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
from katalyst_taxonomy_cli.context import CliContext
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.command("rescaffold")
|
|
19
|
+
@click.argument("address")
|
|
20
|
+
@click.option(
|
|
21
|
+
"--backend", "backend_target", default=None, help="The backend, when ADDRESS has no prefix."
|
|
22
|
+
)
|
|
23
|
+
@click.option(
|
|
24
|
+
"--dry-run",
|
|
25
|
+
is_flag=True,
|
|
26
|
+
help="Show what each file would become (the merge plan); write nothing.",
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--theirs",
|
|
30
|
+
"theirs",
|
|
31
|
+
is_flag=True,
|
|
32
|
+
help="Take the installed template's render whole, over local edits. Needs no base.",
|
|
33
|
+
)
|
|
34
|
+
@click.option(
|
|
35
|
+
"--ours",
|
|
36
|
+
"ours",
|
|
37
|
+
is_flag=True,
|
|
38
|
+
help="Keep every file as it is; only add the files the node does not have.",
|
|
39
|
+
)
|
|
40
|
+
@click.option(
|
|
41
|
+
"--base-source",
|
|
42
|
+
default="",
|
|
43
|
+
help=(
|
|
44
|
+
"A source holding the version the node was scaffolded from, when its files are "
|
|
45
|
+
"not in the home's cache."
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
@click.pass_obj
|
|
49
|
+
def rescaffold(
|
|
50
|
+
ctx: CliContext,
|
|
51
|
+
address: str,
|
|
52
|
+
backend_target: str | None,
|
|
53
|
+
dry_run: bool,
|
|
54
|
+
theirs: bool,
|
|
55
|
+
ours: bool,
|
|
56
|
+
base_source: str,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Re-render node ADDRESS's scaffold at the installed version, merged with its files.
|
|
59
|
+
|
|
60
|
+
The template is rendered as installed now (theirs) and as the version the node was
|
|
61
|
+
scaffolded from (the base: its metadata.annotations.templateBundleVersion), and each
|
|
62
|
+
file in the node's directory (ours) is merged three ways, as kata tax add merges a
|
|
63
|
+
bundle's files. Conflicts are written with markers, listed, and exit 1; the node is
|
|
64
|
+
then stamped with the installed version.
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
|
|
68
|
+
\b
|
|
69
|
+
kata tax rescaffold local:shop.api.docker --dry-run
|
|
70
|
+
kata tax rescaffold local:shop.api.docker
|
|
71
|
+
kata tax rescaffold local:shop.api.docker --theirs
|
|
72
|
+
"""
|
|
73
|
+
from katalyst_taxonomy.adapters.filesystem.real import RealFilesystem
|
|
74
|
+
from katalyst_taxonomy.adapters.orchestration import (
|
|
75
|
+
create_hooks,
|
|
76
|
+
load_taxonomy,
|
|
77
|
+
scaffold_extensions,
|
|
78
|
+
)
|
|
79
|
+
from katalyst_taxonomy.domain.template_merge import MergeMode, plan_lines
|
|
80
|
+
from katalyst_taxonomy.engine.accessors import get_source_path
|
|
81
|
+
from katalyst_taxonomy.engine.qualification import match_fqtn_in
|
|
82
|
+
from katalyst_taxonomy.ports.backend import repository_root_of
|
|
83
|
+
from katalyst_taxonomy.services.rescaffold import rescaffold_node
|
|
84
|
+
from katalyst_taxonomy.services.template_install import template_base_resolver
|
|
85
|
+
|
|
86
|
+
from .._wiring import resolve_backend
|
|
87
|
+
|
|
88
|
+
if theirs and ours:
|
|
89
|
+
raise click.UsageError("--theirs and --ours are each other's opposite; give one.")
|
|
90
|
+
mode: MergeMode = "theirs" if theirs else "ours" if ours else "merge"
|
|
91
|
+
resolved = resolve_backend(ctx, backend_target, address=address)
|
|
92
|
+
root = repository_root_of(resolved.backend)
|
|
93
|
+
if root is None:
|
|
94
|
+
raise click.ClickException(
|
|
95
|
+
f"Backend {resolved.name!r} keeps no working tree, so it has no scaffolded files."
|
|
96
|
+
)
|
|
97
|
+
resolution = match_fqtn_in(resolved.backend.load_documents(), resolved.address)
|
|
98
|
+
node = resolution.target
|
|
99
|
+
if node is None:
|
|
100
|
+
raise click.ClickException(
|
|
101
|
+
f"Node {resolved.address!r} not found in backend {resolved.name!r}."
|
|
102
|
+
if resolution.is_unresolved
|
|
103
|
+
else resolution.describe()
|
|
104
|
+
)
|
|
105
|
+
document = get_source_path(node)
|
|
106
|
+
if document is None or not document.is_file():
|
|
107
|
+
raise click.ClickException(f"{node.fqn} has no document in {root}.")
|
|
108
|
+
if not document.is_absolute():
|
|
109
|
+
document = root / document
|
|
110
|
+
|
|
111
|
+
outcome = rescaffold_node(
|
|
112
|
+
root,
|
|
113
|
+
node,
|
|
114
|
+
document,
|
|
115
|
+
filesystem=RealFilesystem(),
|
|
116
|
+
extensions=scaffold_extensions(),
|
|
117
|
+
base=template_base_resolver(ctx.templates, base_source=base_source),
|
|
118
|
+
mode=mode,
|
|
119
|
+
dry_run=dry_run,
|
|
120
|
+
create_hooks=create_hooks(),
|
|
121
|
+
load_taxonomy=lambda: load_taxonomy(root, profile=ctx.profile),
|
|
122
|
+
)
|
|
123
|
+
if not outcome.success:
|
|
124
|
+
ctx.output.echo(f"Error: {outcome.error}")
|
|
125
|
+
raise SystemExit(1)
|
|
126
|
+
change = (
|
|
127
|
+
f"{outcome.from_version} -> {outcome.to_version}"
|
|
128
|
+
if outcome.from_version
|
|
129
|
+
else outcome.to_version or "?"
|
|
130
|
+
)
|
|
131
|
+
where = f"{resolved.name}:{outcome.fqtn}"
|
|
132
|
+
heading = "Plan for" if dry_run else "Rescaffolded"
|
|
133
|
+
suffix = " (dry run)" if dry_run else ""
|
|
134
|
+
ctx.output.echo(
|
|
135
|
+
f"{heading} {where}: template {outcome.variant}, bundle {outcome.bundle or '?'} "
|
|
136
|
+
f"{change}{suffix}:"
|
|
137
|
+
)
|
|
138
|
+
plan = outcome.plan
|
|
139
|
+
rows = plan.as_dicts() if plan is not None else []
|
|
140
|
+
for line in plan_lines(rows, every=dry_run):
|
|
141
|
+
ctx.output.echo(line)
|
|
142
|
+
conflicts = plan.conflicts if plan is not None else ()
|
|
143
|
+
if conflicts and not dry_run:
|
|
144
|
+
ctx.output.echo(
|
|
145
|
+
f"{len(conflicts)} file(s) have conflicts; resolve the markers in them:\n"
|
|
146
|
+
+ "\n".join(f" {f.path}" for f in conflicts)
|
|
147
|
+
)
|
|
148
|
+
if conflicts:
|
|
149
|
+
raise SystemExit(1)
|
|
@@ -49,6 +49,24 @@ def _pairs(values: tuple[str, ...], option: str) -> dict[str, list[str]]:
|
|
|
49
49
|
help="Remove a value from a list field.",
|
|
50
50
|
)
|
|
51
51
|
@click.option("--unset", "unsets", multiple=True, metavar="FIELD", help="Remove a field.")
|
|
52
|
+
@click.option(
|
|
53
|
+
"--label",
|
|
54
|
+
"label_pairs",
|
|
55
|
+
multiple=True,
|
|
56
|
+
metavar="KEY=VALUE",
|
|
57
|
+
help="Set a label (metadata.labels). Repeat.",
|
|
58
|
+
)
|
|
59
|
+
@click.option("--unlabel", "unlabels", multiple=True, metavar="KEY", help="Remove a label.")
|
|
60
|
+
@click.option(
|
|
61
|
+
"--annotation",
|
|
62
|
+
"annotation_pairs",
|
|
63
|
+
multiple=True,
|
|
64
|
+
metavar="KEY=VALUE",
|
|
65
|
+
help="Set an annotation (metadata.annotations). Repeat.",
|
|
66
|
+
)
|
|
67
|
+
@click.option(
|
|
68
|
+
"--unannotate", "unannotates", multiple=True, metavar="KEY", help="Remove an annotation."
|
|
69
|
+
)
|
|
52
70
|
@click.pass_obj
|
|
53
71
|
def update(
|
|
54
72
|
ctx: CliContext,
|
|
@@ -59,6 +77,10 @@ def update(
|
|
|
59
77
|
removes: tuple[str, ...],
|
|
60
78
|
unsets: tuple[str, ...],
|
|
61
79
|
from_file: str | None,
|
|
80
|
+
label_pairs: tuple[str, ...],
|
|
81
|
+
unlabels: tuple[str, ...],
|
|
82
|
+
annotation_pairs: tuple[str, ...],
|
|
83
|
+
unannotates: tuple[str, ...],
|
|
62
84
|
) -> None:
|
|
63
85
|
"""Change the fields of the node ADDRESS (``backend:fqtn``) names.
|
|
64
86
|
|
|
@@ -70,6 +92,11 @@ def update(
|
|
|
70
92
|
field holds; ``--from-file`` replaces each field it names. Relationships carried by
|
|
71
93
|
a reference field can be changed here or with ``kata tax relate``.
|
|
72
94
|
|
|
95
|
+
``--label`` / ``--annotation KEY=VALUE`` set the node's labels and annotations
|
|
96
|
+
(``metadata``, any kind), checked as ``kata tax create`` checks them; ``--unlabel`` /
|
|
97
|
+
``--unannotate KEY`` remove one the node carries. A whole document given to
|
|
98
|
+
``--from-file`` sets the labels and annotations it carries too.
|
|
99
|
+
|
|
73
100
|
Examples:
|
|
74
101
|
|
|
75
102
|
kata tax update local:payments --set description="Takes payments"
|
|
@@ -79,16 +106,35 @@ def update(
|
|
|
79
106
|
kata tax update local:payments.api --unset environments
|
|
80
107
|
|
|
81
108
|
kata tax update local:qa --set template_replacements.log_level=debug
|
|
109
|
+
|
|
110
|
+
kata tax update local:payments --label cadence=daily --unlabel old --annotation source=x.md
|
|
82
111
|
"""
|
|
83
112
|
from katalyst_taxonomy.config.routing import BackendAddressError
|
|
84
113
|
from katalyst_taxonomy.engine.bridge import get_default_registry
|
|
85
|
-
from katalyst_taxonomy.services.node_update import
|
|
114
|
+
from katalyst_taxonomy.services.node_update import (
|
|
115
|
+
NodeUpdateService,
|
|
116
|
+
field_edits,
|
|
117
|
+
metadata_patch,
|
|
118
|
+
)
|
|
86
119
|
|
|
87
|
-
from katalyst_taxonomy_cli.fields import
|
|
120
|
+
from katalyst_taxonomy_cli.fields import parse_pairs, read_node_file
|
|
88
121
|
|
|
89
122
|
from .._wiring import resolve_backend
|
|
90
123
|
|
|
91
|
-
|
|
124
|
+
node_file = read_node_file(from_file)
|
|
125
|
+
fragment = node_file.spec
|
|
126
|
+
try:
|
|
127
|
+
metadata = metadata_patch(
|
|
128
|
+
labels={**node_file.labels, **parse_pairs(label_pairs, option="--label")},
|
|
129
|
+
unlabel=unlabels,
|
|
130
|
+
annotations={
|
|
131
|
+
**node_file.annotations,
|
|
132
|
+
**parse_pairs(annotation_pairs, option="--annotation"),
|
|
133
|
+
},
|
|
134
|
+
unannotate=unannotates,
|
|
135
|
+
)
|
|
136
|
+
except ValueError as exc:
|
|
137
|
+
raise click.UsageError(str(exc)) from exc
|
|
92
138
|
edits = field_edits(
|
|
93
139
|
set_values=_pairs(sets, "--set"),
|
|
94
140
|
add=_pairs(adds, "--add"),
|
|
@@ -104,7 +150,12 @@ def update(
|
|
|
104
150
|
raise click.ClickException(str(exc)) from exc
|
|
105
151
|
|
|
106
152
|
outcome = NodeUpdateService(get_default_registry()).update(
|
|
107
|
-
resolved.backend,
|
|
153
|
+
resolved.backend,
|
|
154
|
+
resolved.address,
|
|
155
|
+
edits,
|
|
156
|
+
fragment=fragment,
|
|
157
|
+
metadata=metadata,
|
|
158
|
+
other_backend=other_backend,
|
|
108
159
|
)
|
|
109
160
|
if not outcome.success:
|
|
110
161
|
ctx.output.echo(f"Error: {outcome.error}")
|