feat: Implement secret management for environment variables and enhance systemd unit generation

This commit is contained in:
2026-06-27 12:47:05 -07:00
parent 51fdce7eea
commit 3f1b2e6f56
9 changed files with 431 additions and 33 deletions

View File

@@ -6,7 +6,7 @@ from pathlib import Path
from unittest.mock import patch
from castle_core.deploy import _build_run_cmd
from castle_core.manifest import RunPython
from castle_core.manifest import RunContainer, RunPython
def test_python_runner_uses_uv_run_from_source(tmp_path: Path) -> None:
@@ -35,7 +35,9 @@ def test_python_runner_appends_args(tmp_path: Path) -> None:
def test_python_runner_falls_back_to_path_without_source() -> None:
"""No resolvable source → PATH lookup of the script (no uv run)."""
run = RunPython(runner="python", program="my-svc")
with patch("castle_core.deploy.shutil.which", return_value="/home/u/.local/bin/my-svc"):
with patch(
"castle_core.deploy.shutil.which", return_value="/home/u/.local/bin/my-svc"
):
cmd = _build_run_cmd("my-svc", run, {}, [], source_dir=None)
assert cmd == ["/home/u/.local/bin/my-svc"]
@@ -48,3 +50,25 @@ def test_python_runner_warns_when_unresolvable() -> None:
cmd = _build_run_cmd("my-svc", run, {}, messages, source_dir=None)
assert cmd == ["my-svc"]
assert any("my-svc" in m for m in messages)
def test_container_secrets_use_env_file_not_argv() -> None:
"""Secrets go through --env-file; plain vars stay as -e; no secret in argv."""
run = RunContainer(runner="container", image="img:latest", env={"PLAIN": "1"})
env_file = Path("/home/u/.castle/secrets/env/castle-svc.service.env")
with patch("castle_core.deploy.shutil.which", return_value="/usr/bin/docker"):
cmd = _build_run_cmd("svc", run, {"PORT": "9001"}, [], secret_env_file=env_file)
joined = " ".join(cmd)
assert "--env-file" in cmd
assert str(env_file) in cmd
# plain (non-secret) vars are still inlined as -e
assert "-e" in cmd and "PORT=9001" in joined and "PLAIN=1" in joined
# no resolved secret value leaks into argv (only the file path is referenced)
assert "SECRET=" not in joined
def test_container_without_secrets_has_no_env_file() -> None:
run = RunContainer(runner="container", image="img:latest")
with patch("castle_core.deploy.shutil.which", return_value="/usr/bin/docker"):
cmd = _build_run_cmd("svc", run, {}, [], secret_env_file=None)
assert "--env-file" not in cmd