refactor: Update runner specifications from 'python_uv_tool' to 'python' across components and documentation

This commit is contained in:
2026-02-22 23:56:37 -08:00
parent 73e4a2ba00
commit 5e3e01a5b6
23 changed files with 539 additions and 124 deletions

View File

@@ -88,20 +88,13 @@ def manifest_to_exec_start(manifest: ComponentManifest, root: Path) -> str:
raise ValueError(f"Component '{manifest.id}' has no run spec")
match run.runner:
case "python_uv_tool":
case "python":
uv_path = shutil.which("uv") or "uv"
args_str = " ".join(run.args) if run.args else ""
cmd = f"{uv_path} run {run.tool}"
if args_str:
cmd += f" {args_str}"
return cmd
case "python_module":
python = run.python or shutil.which("python3") or "python3"
args_str = " ".join(run.args) if run.args else ""
cmd = f"{python} -m {run.module}"
if args_str:
cmd += f" {args_str}"
return cmd
case "command":
argv = list(run.argv)
resolved = shutil.which(argv[0])

View File

@@ -46,15 +46,8 @@ class RunCommand(RunBase):
argv: list[str] = Field(min_length=1)
class RunPythonModule(RunBase):
runner: Literal["python_module"]
module: str
args: list[str] = Field(default_factory=list)
python: str | None = None
class RunPythonUvTool(RunBase):
runner: Literal["python_uv_tool"]
class RunPython(RunBase):
runner: Literal["python"]
tool: str
args: list[str] = Field(default_factory=list)
@@ -84,9 +77,7 @@ class RunRemote(RunBase):
RunSpec = Annotated[
Union[
RunCommand, RunPythonModule, RunPythonUvTool, RunContainer, RunNode, RunRemote
],
Union[RunCommand, RunPython, RunContainer, RunNode, RunRemote],
Field(discriminator="runner"),
]

View File

@@ -20,7 +20,7 @@ def castle_root(tmp_path: Path) -> Generator[Path, None, None]:
"description": "Test service",
"source": "test-svc",
"run": {
"runner": "python_uv_tool",
"runner": "python",
"tool": "test-svc",
},
"defaults": {

View File

@@ -60,7 +60,7 @@ class TestLoadConfig:
"""Service has correct RunSpec."""
config = load_config(castle_root)
svc = config.components["test-svc"]
assert svc.run.runner == "python_uv_tool"
assert svc.run.runner == "python"
assert svc.run.tool == "test-svc"
assert svc.source == "test-svc"

View File

@@ -17,7 +17,7 @@ from castle_core.manifest import (
Role,
RunCommand,
RunContainer,
RunPythonUvTool,
RunPython,
RunRemote,
SystemdSpec,
ToolSpec,
@@ -32,7 +32,7 @@ class TestRoleDerivation:
"""Component with expose.http gets SERVICE role."""
m = ComponentManifest(
id="svc",
run=RunPythonUvTool(runner="python_uv_tool", tool="svc"),
run=RunPython(runner="python", tool="svc"),
expose=ExposeSpec(http=HttpExposeSpec(internal=HttpInternal(port=8000))),
)
assert Role.SERVICE in m.roles
@@ -114,7 +114,7 @@ class TestRoleDerivation:
"""Component can have multiple roles."""
m = ComponentManifest(
id="multi",
run=RunPythonUvTool(runner="python_uv_tool", tool="multi"),
run=RunPython(runner="python", tool="multi"),
expose=ExposeSpec(http=HttpExposeSpec(internal=HttpInternal(port=8000))),
install=InstallSpec(path=PathInstallSpec(alias="multi")),
)
@@ -125,7 +125,7 @@ class TestRoleDerivation:
"""Systemd + HTTP = SERVICE, not WORKER."""
m = ComponentManifest(
id="svc",
run=RunPythonUvTool(runner="python_uv_tool", tool="svc"),
run=RunPython(runner="python", tool="svc"),
expose=ExposeSpec(http=HttpExposeSpec(internal=HttpInternal(port=8000))),
manage=ManageSpec(systemd=SystemdSpec()),
)
@@ -170,7 +170,7 @@ class TestModelSerialization:
m = ComponentManifest(
id="svc",
description="A service",
run=RunPythonUvTool(runner="python_uv_tool", tool="svc", cwd="svc"),
run=RunPython(runner="python", tool="svc", cwd="svc"),
expose=ExposeSpec(
http=HttpExposeSpec(
internal=HttpInternal(port=9001), health_path="/health"
@@ -180,6 +180,6 @@ class TestModelSerialization:
manage=ManageSpec(systemd=SystemdSpec()),
)
data = m.model_dump(exclude_none=True, exclude={"id", "roles"})
assert data["run"]["runner"] == "python_uv_tool"
assert data["run"]["runner"] == "python"
assert data["expose"]["http"]["internal"]["port"] == 9001
assert data["proxy"]["caddy"]["path_prefix"] == "/svc"

View File

@@ -55,7 +55,7 @@ class TestUnitGeneration:
assert "Restart=on-failure" in unit
def test_uses_uv_run(self, castle_root: Path) -> None:
"""Unit file ExecStart uses uv run for python_uv_tool."""
"""Unit file ExecStart uses uv run for python runner."""
config = load_config(castle_root)
manifest = config.components["test-svc"]
unit = generate_unit(config, "test-svc", manifest)
@@ -68,7 +68,7 @@ class TestUnitFromDeployed:
def test_basic_service(self) -> None:
"""Generate a unit from a deployed component."""
deployed = DeployedComponent(
runner="python_uv_tool",
runner="python",
run_cmd=["/home/user/.local/bin/uv", "run", "my-svc"],
env={"MY_SVC_PORT": "9001", "MY_SVC_DATA_DIR": "/data/castle/my-svc"},
description="My service",
@@ -97,7 +97,7 @@ class TestUnitFromDeployed:
def test_no_repo_paths(self) -> None:
"""Generated units must not reference repo paths."""
deployed = DeployedComponent(
runner="python_uv_tool",
runner="python",
run_cmd=["/home/user/.local/bin/uv", "run", "my-svc"],
env={"DATA_DIR": "/data/castle/my-svc"},
description="Test",