cli: resolve deployments by (name, kind), not bare name

Thread kind through every CLI resolver so a tool, service, and job can
share a name. Reads go through config.deployment(kind,name) /
deployments_named(name) / all_deployments(); writes target the per-kind
store (config.services/jobs/tools/statics). delete cascades over
(kind,name) tuples; run/info resolve via registry.named().
This commit is contained in:
2026-07-06 02:44:25 -07:00
parent 315afe3f5f
commit 0cb41851cf
16 changed files with 106 additions and 126 deletions

View File

@@ -50,7 +50,7 @@ class TestAdd:
def test_adopt_rust_declares_commands(self, castle_root: Path, tmp_path: Path) -> None:
repo = tmp_path / "rusty"
repo.mkdir()
(repo / "Cargo.toml").write_text("[package]\nname = \"rusty\"\n")
(repo / "Cargo.toml").write_text('[package]\nname = "rusty"\n')
rc, config = _run_add(castle_root, target=str(repo))
assert rc == 0
prog = config.programs["rusty"]
@@ -62,9 +62,7 @@ class TestAdd:
assert prog.commands.run == [["cargo", "run"]]
def test_adopt_git_url_records_repo(self, castle_root: Path) -> None:
rc, config = _run_add(
castle_root, target="https://github.com/someone/widget.git"
)
rc, config = _run_add(castle_root, target="https://github.com/someone/widget.git")
assert rc == 0
prog = config.programs["widget"]
assert prog.repo == "https://github.com/someone/widget.git"

View File

@@ -74,7 +74,7 @@ class TestCreateCommand:
assert (project_dir / "CLAUDE.md").exists()
assert "my-tool2" in config.programs
# A tool is a PATH deployment: manager=path, derived kind=tool.
assert config.deployments["my-tool2"].manager == "path"
assert config.tools["my-tool2"].manager == "path"
assert config.deployments_of("my-tool2") == [("my-tool2", "tool")]
def test_create_supabase_app(self, castle_root: Path, tmp_path: Path) -> None:
@@ -91,9 +91,7 @@ class TestCreateCommand:
from castle_cli.commands.create import run_create
args = Namespace(
name="guestbook", stack="supabase", description="Guestbook", port=None
)
args = Namespace(name="guestbook", stack="supabase", description="Guestbook", port=None)
result = run_create(args)
assert result == 0
@@ -110,7 +108,7 @@ class TestCreateCommand:
# The supabase stack seeds a `requires` on the substrate so the graph shows
# the dependency (stack stays uncoupled — it just declares the edge once).
assert [(r.kind, r.ref) for r in comp.requires] == [("deployment", "supabase")]
dep = config.deployments["guestbook"]
dep = config.statics["guestbook"]
assert dep.manager == "caddy"
assert dep.root == "public"
assert config.deployments_of("guestbook") == [("guestbook", "static")]