katalyst-taxonomy-cli 2.0.1a4__tar.gz → 2.0.1a6__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.
Files changed (102) hide show
  1. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/PKG-INFO +1 -1
  2. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/pyproject.toml +1 -1
  3. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/app.py +4 -3
  4. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/config_home.py +49 -10
  5. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/config_profile.py +2 -1
  6. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/config_sources.py +9 -3
  7. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/lifecycle.py +19 -5
  8. katalyst_taxonomy_cli-2.0.1a6/src/katalyst_taxonomy_cli/commands/self_update.py +623 -0
  9. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/install.py +64 -73
  10. katalyst_taxonomy_cli-2.0.1a6/src/katalyst_taxonomy_cli/installer.py +407 -0
  11. katalyst_taxonomy_cli-2.0.1a6/src/katalyst_taxonomy_cli/releases.py +273 -0
  12. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_config_home.py +59 -0
  13. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_deps.py +9 -9
  14. katalyst_taxonomy_cli-2.0.1a6/tests/test_import_change_set.py +171 -0
  15. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_install.py +2 -2
  16. katalyst_taxonomy_cli-2.0.1a6/tests/test_installer.py +190 -0
  17. katalyst_taxonomy_cli-2.0.1a6/tests/test_releases.py +128 -0
  18. katalyst_taxonomy_cli-2.0.1a6/tests/test_self_channels.py +484 -0
  19. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_self_update.py +4 -180
  20. katalyst_taxonomy_cli-2.0.1a6/tests/test_update_in_place.py +93 -0
  21. katalyst_taxonomy_cli-2.0.1a4/src/katalyst_taxonomy_cli/commands/self_update.py +0 -530
  22. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/.gitignore +0 -0
  23. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/__init__.py +0 -0
  24. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/__main__.py +0 -0
  25. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/_wiring.py +0 -0
  26. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/__init__.py +0 -0
  27. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/api.py +0 -0
  28. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/backend.py +0 -0
  29. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/bundle.py +0 -0
  30. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/clone.py +0 -0
  31. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/config.py +0 -0
  32. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/config_filters.py +0 -0
  33. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/config_plugins.py +0 -0
  34. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/config_schemas.py +0 -0
  35. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/core.py +0 -0
  36. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/create.py +0 -0
  37. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/deps.py +0 -0
  38. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/diff.py +0 -0
  39. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/drafts.py +0 -0
  40. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/export.py +0 -0
  41. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/init.py +0 -0
  42. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/kinds.py +0 -0
  43. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/lint.py +0 -0
  44. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/migrate.py +0 -0
  45. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/plugin.py +0 -0
  46. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/promote.py +0 -0
  47. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/query.py +0 -0
  48. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/relate.py +0 -0
  49. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/relocate.py +0 -0
  50. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/remove.py +0 -0
  51. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/rescaffold.py +0 -0
  52. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/resolve.py +0 -0
  53. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/rollback.py +0 -0
  54. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/show.py +0 -0
  55. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/status.py +0 -0
  56. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/sync.py +0 -0
  57. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/update.py +0 -0
  58. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/commands/versions.py +0 -0
  59. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/context.py +0 -0
  60. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/discovery.py +0 -0
  61. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/fields.py +0 -0
  62. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/options.py +0 -0
  63. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/output.py +0 -0
  64. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/src/katalyst_taxonomy_cli/py.typed +0 -0
  65. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/conftest.py +0 -0
  66. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_add.py +0 -0
  67. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_add_bundle.py +0 -0
  68. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_api_command.py +0 -0
  69. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_backend_option.py +0 -0
  70. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_cli_version.py +0 -0
  71. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_clone.py +0 -0
  72. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_command_signatures.py +0 -0
  73. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_config_command.py +0 -0
  74. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_config_explain.py +0 -0
  75. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_config_plugins_command.py +0 -0
  76. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_config_profiles.py +0 -0
  77. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_config_schemas_command.py +0 -0
  78. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_config_sources.py +0 -0
  79. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_core_schema.py +0 -0
  80. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_create.py +0 -0
  81. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_create_owners.py +0 -0
  82. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_create_typed_and_variants.py +0 -0
  83. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_git_backend_cli.py +0 -0
  84. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_github_backend_cli.py +0 -0
  85. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_import_inside_repo.py +0 -0
  86. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_kinds.py +0 -0
  87. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_kinds_plugin_settings.py +0 -0
  88. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_labels_annotations.py +0 -0
  89. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_lifecycle_kind_selector.py +0 -0
  90. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_lint_output.py +0 -0
  91. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_lint_profile_graph.py +0 -0
  92. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_lint_strict.py +0 -0
  93. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_migrate.py +0 -0
  94. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_optional_extras.py +0 -0
  95. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_output.py +0 -0
  96. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_plugin_command.py +0 -0
  97. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_plugin_gating.py +0 -0
  98. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_relate_update.py +0 -0
  99. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_relocate.py +0 -0
  100. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_remove.py +0 -0
  101. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_repository_targets.py +0 -0
  102. {katalyst_taxonomy_cli-2.0.1a4 → katalyst_taxonomy_cli-2.0.1a6}/tests/test_template_merge.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: katalyst-taxonomy-cli
3
- Version: 2.0.1a4
3
+ Version: 2.0.1a6
4
4
  Summary: CLI for katalyst-taxonomy — the kata command
5
5
  Author: eSimplicity
6
6
  License: Proprietary
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "katalyst-taxonomy-cli"
3
- version = "2.0.1a4"
3
+ version = "2.0.1a6"
4
4
  description = "CLI for katalyst-taxonomy — the kata command"
5
5
  license = { text = "Proprietary" }
6
6
  requires-python = ">=3.12"
@@ -311,14 +311,15 @@ def cli(ctx: click.Context, profile: str | None) -> None:
311
311
  ctx.meta[ROOT_PROFILE_META_KEY] = profile
312
312
  _configure_logging()
313
313
  # A KATALYST_HOME that names no home fails every command here, before any of them
314
- # reads or writes anything (`--home` was checked as it was parsed).
315
- from katalyst_taxonomy.adapters.filesystem.homes import resolve_existing_home
314
+ # reads or writes anything (`--home` was checked as it was parsed) — unless
315
+ # KATALYST_HOME_SOURCE says what to create it from.
316
+ from katalyst_taxonomy.adapters.config_sources.homes import resolve_home_on_demand
316
317
  from katalyst_taxonomy.domain.homes import HomeError
317
318
 
318
319
  from .install import refuse_default_home_for_development
319
320
 
320
321
  try:
321
- home = resolve_existing_home()
322
+ home = resolve_home_on_demand(announce=lambda m: click.echo(m, err=True))
322
323
  except HomeError as exc:
323
324
  raise click.ClickException(str(exc)) from exc
324
325
  # `kata config` decides for itself: `kata config home …` manages homes, not uses one.
@@ -4,7 +4,7 @@ A home is the config, state and cache a process uses (:mod:`katalyst_taxonomy.do
4
4
  ``kata --home NAME`` or ``KATALYST_HOME=NAME`` selects one; a named home must exist first::
5
5
 
6
6
  kata config home list
7
- kata config home create NAME [--from HOME]
7
+ kata config home create NAME [--from HOME | --source URI [--source-profile P]]
8
8
  kata config home rm NAME [--yes]
9
9
 
10
10
  Path homes (an absolute ``KATALYST_HOME``, for tests and CI) are never listed: they are
@@ -14,7 +14,6 @@ wherever their caller put them.
14
14
  from __future__ import annotations
15
15
 
16
16
  import json
17
- import os
18
17
  import shutil
19
18
  from typing import Any
20
19
 
@@ -32,6 +31,7 @@ from katalyst_taxonomy.domain.homes import (
32
31
  resolve_home,
33
32
  )
34
33
  from katalyst_taxonomy.domain.paths import CONFIG_FILE_NAME
34
+ from katalyst_taxonomy.processes import is_process_running
35
35
 
36
36
  from ..output import ConsoleOutput
37
37
 
@@ -52,9 +52,7 @@ def _running_api(home: Home) -> dict[str, Any] | None:
52
52
  pid, host, port = int(record["pid"]), str(record["host"]), int(record["port"])
53
53
  except (OSError, ValueError, KeyError, TypeError):
54
54
  return None
55
- try:
56
- os.kill(pid, 0)
57
- except OSError:
55
+ if not is_process_running(pid):
58
56
  return None
59
57
  return {"pid": pid, "url": f"http://{host}:{port}"}
60
58
 
@@ -121,15 +119,56 @@ def home_list(fmt: str) -> None:
121
119
  @click.argument("name")
122
120
  @click.option(
123
121
  "--from",
124
- "source",
122
+ "copy_from",
125
123
  default=None,
126
124
  metavar="HOME",
127
125
  help="Copy config.yaml (only) from this home: 'default', a name, or an absolute path.",
128
126
  )
129
- def home_create(name: str, source: str | None) -> None:
130
- """Create the named home NAME, empty or with another home's config.yaml."""
127
+ @click.option(
128
+ "--source",
129
+ "profile_source",
130
+ default=None,
131
+ metavar="URI",
132
+ help=(
133
+ "Give the home one read-only profile sourced from URI, such as home:default "
134
+ "(another home: the same repositories), then sync it."
135
+ ),
136
+ )
137
+ @click.option(
138
+ "--source-profile",
139
+ default=None,
140
+ metavar="PROFILE",
141
+ help="The profile in the source's configuration (default: @active, whichever is active there).",
142
+ )
143
+ def home_create(
144
+ name: str, copy_from: str | None, profile_source: str | None, source_profile: str | None
145
+ ) -> None:
146
+ """Create the named home NAME: empty, with another home's config.yaml, or sourced.
147
+
148
+ With --source, NAME's config.yaml holds one profile sourced from URI, and nothing
149
+ else. By default it follows the source's active profile (@active), at every sync. A
150
+ home: source's profile reads the other home's repositories; its state (the API's
151
+ record and token) and cache are never read.
152
+ """
153
+ if copy_from is not None and profile_source is not None:
154
+ raise click.ClickException("give --from or --source, not both")
155
+ if source_profile is not None and profile_source is None:
156
+ raise click.ClickException("--source-profile needs --source")
157
+ out = ConsoleOutput()
158
+ if profile_source is not None:
159
+ from katalyst_taxonomy.adapters.config_sources.homes import create_sourced_home
160
+ from katalyst_taxonomy.config.profile_sources import ACTIVE_PROFILE
161
+
162
+ wanted = source_profile or ACTIVE_PROFILE
163
+ try:
164
+ created, pin = create_sourced_home(name, profile_source, source_profile=wanted)
165
+ except HomeError as exc:
166
+ raise click.ClickException(str(exc)) from exc
167
+ out.success(f"Created home {created.label}, with profile {wanted!r} of {profile_source}")
168
+ out.info(f"Synced it: pinned {pin}")
169
+ return
131
170
  try:
132
- origin = home_for(source) if source is not None else None
171
+ origin = home_for(copy_from) if copy_from is not None else None
133
172
  created = create_home(name)
134
173
  except HomeError as exc:
135
174
  raise click.ClickException(str(exc)) from exc
@@ -139,7 +178,7 @@ def home_create(name: str, source: str | None) -> None:
139
178
  if config_file.is_file():
140
179
  shutil.copy2(config_file, created.config_dir / CONFIG_FILE_NAME)
141
180
  copied = f", with config.yaml from {origin.name}"
142
- ConsoleOutput().success(f"Created home {created.label}{copied}")
181
+ out.success(f"Created home {created.label}{copied}")
143
182
 
144
183
 
145
184
  @home.command("rm")
@@ -318,7 +318,8 @@ def profile_show(name: str, fmt: str) -> None:
318
318
  metavar="URI",
319
319
  help=(
320
320
  "Read the profile from elsewhere: git:<url>[@<ref>][#path=<file>], "
321
- "github:<owner>/<repo>[@<ref>][#path=<file>], or a kata API's https:// URL."
321
+ "github:<owner>/<repo>[@<ref>][#path=<file>], a kata API's https:// URL, or "
322
+ "home:<home> (another home on this machine)."
322
323
  ),
323
324
  )
324
325
  @click.option(
@@ -109,9 +109,7 @@ def config_status(offline: bool, fmt: str) -> None:
109
109
  ["profile", "source", "pin", "fetched", "upstream"],
110
110
  [
111
111
  [
112
- row.profile
113
- if row.source_profile == row.profile
114
- else f"{row.profile} ({row.source_profile})",
112
+ _profile_label(row),
115
113
  row.source,
116
114
  _short(row.pin) if row.pin else "never fetched",
117
115
  row.fetched_at or "-",
@@ -125,6 +123,14 @@ def config_status(offline: bool, fmt: str) -> None:
125
123
  out.warning(f"{row.profile}: {row.error}")
126
124
 
127
125
 
126
+ def _profile_label(row: Any) -> str:
127
+ if row.source_profile == row.profile:
128
+ return str(row.profile)
129
+ if row.pinned_profile and row.pinned_profile != row.source_profile:
130
+ return f"{row.profile} ({row.source_profile}: {row.pinned_profile})"
131
+ return f"{row.profile} ({row.source_profile})"
132
+
133
+
128
134
  def _upstream(row: Any, offline: bool) -> str:
129
135
  if offline:
130
136
  return "not checked"
@@ -101,24 +101,38 @@ def restore(ctx: CliContext, address: str, backend_target: str | None) -> None:
101
101
  def import_(ctx: CliContext, file: Path, backend_target: str) -> None:
102
102
  """Create every node document of FILE (a YAML stream) in the backend, or none of them.
103
103
 
104
- The whole change set passes the local shapes first, with the nodes it refers to;
105
- then the nodes are created parents first, each as ``kata tax create`` creates it.
106
- If one fails, the ones already created are removed again.
104
+ The stream is one change set: a reference resolves against every node in it as well
105
+ as the backend, and the whole set passes the reference checks and the local shapes
106
+ first. Then the nodes are created, each as ``kata tax create`` creates it, after the
107
+ nodes it needs (its parent, the nodes it refers to). If one fails, the ones already
108
+ created are removed again; so are those of an import that was killed part-way, by
109
+ the next one.
107
110
  """
108
111
  import yaml
109
- from katalyst_taxonomy.adapters.orchestration import create_node
110
- from katalyst_taxonomy.services.change_import import ImportError_, import_documents
112
+ from katalyst_taxonomy.adapters.orchestration import create_node, import_journal
113
+ from katalyst_taxonomy.services.change_import import (
114
+ ImportError_,
115
+ import_documents,
116
+ recover_import,
117
+ )
111
118
 
112
119
  from .._wiring import resolve_backend
113
120
 
114
121
  resolved = resolve_backend(ctx, backend_target)
115
122
  documents = [d for d in yaml.safe_load_all(file.read_text(encoding="utf-8")) if d]
123
+ journal = import_journal(resolved.backend)
116
124
  try:
125
+ rolled_back = recover_import(resolved.backend, journal)
126
+ if rolled_back:
127
+ ctx.output.echo(
128
+ "Rolled back an import that stopped part-way; removed: " + ", ".join(rolled_back)
129
+ )
117
130
  created = import_documents(
118
131
  resolved.backend,
119
132
  [cast("dict[str, Any]", d) for d in documents],
120
133
  create=lambda request: create_node(resolved.backend, request),
121
134
  source=file,
135
+ journal=journal,
122
136
  )
123
137
  except ImportError_ as exc:
124
138
  ctx.output.echo(f"Error: {exc}")