Manager abstraction + manager-dispatched lifecycle; frontends create static services

- manifest: `manager_for(runner)` — the single source of truth mapping a runner to
  its manager (systemd | caddy | path | none). deploy's `managed` now derives from it.
- lifecycle: is_active/activate/deactivate dispatch by manager instead of behavior
  (removed the stale `_is_static_frontend`, which broke once frontends became
  services). caddy → gateway reload; path → install/uninstall; none → no-op.
- cli/service: start/stop/restart and enable/disable route through the manager
  (systemctl only for systemd; static reloads the gateway; remote is a no-op);
  platform restart skips non-systemd runners.
- create: a frontend stack (react-vite/supabase) now emits a `runner: static`
  service, not a bare frontend program.
This commit is contained in:
2026-07-01 06:51:31 -07:00
parent 5f6afbf007
commit 57861e58c1
8 changed files with 206 additions and 78 deletions

View File

@@ -102,12 +102,14 @@ class TestCreateCommand:
assert (project_dir / "public" / "index.html").exists()
assert (project_dir / "supabase.app.yaml").exists()
# Registered as a static frontend, no service, build output = public/
# Registered as a program + a `static` service serving public/
comp = config.programs["guestbook"]
assert comp.behavior == "frontend"
assert comp.stack == "supabase"
assert comp.build is not None and comp.build.outputs == ["public"]
assert "guestbook" not in config.services
svc = config.services["guestbook"]
assert svc.run.runner == "static"
assert svc.run.root == "public"
def test_create_duplicate_fails(self, castle_root: Path, capsys: object) -> None:
"""Creating a project with existing name fails."""