Add supabase stack + general compose runner
Adds "a stack whose default is a substrate": a shared self-hosted Supabase backend plus a `--stack supabase` that scaffolds per-app projects (migrations + edge functions + static UI) which deploy against it. Apps own their code and stay repo-durable; only their rows/blobs live on the shared substrate. - compose runner: new RunCompose supervises a multi-container stack as one systemd unit (ExecStart=`compose up`, generated ExecStop=`compose down`); secrets via EnvironmentFile. Reusable beyond Supabase. Deployment.stop_cmd carries the teardown command through the registry. - supabase stack: CLI --stack choice, STACK_DEFAULTS→frontend with build.outputs=[public], _scaffold_supabase() (migrations/RLS/functions/ static UI/app manifest), and SupabaseHandler with a forward-only idempotent migration runner (plan_migrations + psql build) and deno dev-verbs. - docs: docs/stacks/supabase.md, registered in CLAUDE.md; compose runner documented in registry.md. - tests: compose run/stop cmd, systemd ExecStop, compose host-route TLS, migration planner, supabase verb resolution, create --stack supabase.
This commit is contained in:
@@ -224,6 +224,7 @@ Discriminated union on `runner`:
|
||||
| `python` | *(none — `uv run` self-syncs)* | `uv run --project <source> --no-dev <program>` | `program`, `args` |
|
||||
| `command` | *(none)* | `which(argv[0])` → resolved path | `argv` |
|
||||
| `container` | *(none)* | `docker`/`podman` `run` | `image`, `command`, `ports`, `volumes` |
|
||||
| `compose` | *(none)* | `docker compose -p <project> -f <file> up` (+ `ExecStop=down`) | `file`, `project_name` |
|
||||
| `node` | `package_manager install` | `package_manager run script` | `script`, `package_manager` |
|
||||
| `remote` | *(none)* | *(none — no local process)* | `base_url`, `health_url` |
|
||||
|
||||
@@ -242,6 +243,23 @@ run:
|
||||
program: my-service # name in [project.scripts]
|
||||
```
|
||||
|
||||
A `compose` service supervises a **whole multi-container stack as one systemd
|
||||
unit** — `ExecStart` runs `docker compose … up` attached (`Type=simple`) and a
|
||||
generated `ExecStop` runs `… down` so networks/anonymous volumes are reclaimed on
|
||||
stop. Unlike the single-container `container` runner, compose owns the stack's own
|
||||
networking, startup ordering, and per-service health — Castle delegates rather
|
||||
than reinventing orchestration. Secrets/env reach compose through the unit's
|
||||
`Environment=`/`EnvironmentFile=` (from `defaults.env`), which compose interpolates
|
||||
from the process environment. This is what runs the shared **Supabase substrate**
|
||||
(see @docs/stacks/supabase.md).
|
||||
|
||||
```yaml
|
||||
run:
|
||||
runner: compose
|
||||
file: docker-compose.yml # resolved under the program source
|
||||
# project_name: castle-my-stack # optional; defaults to castle-<name>
|
||||
```
|
||||
|
||||
### `expose` — What it exposes
|
||||
|
||||
```yaml
|
||||
@@ -599,7 +617,7 @@ The Pydantic models live in `core/src/castle_core/manifest.py`. Key classes:
|
||||
- `ProgramSpec` — software catalog entry (source, behavior, stack, build, system_dependencies)
|
||||
- `ServiceSpec` — long-running daemon (run, expose, proxy, manage, defaults)
|
||||
- `JobSpec` — scheduled task (run, schedule, manage, defaults)
|
||||
- `RunSpec` — discriminated union (RunPython, RunCommand, RunContainer, RunNode, RunRemote)
|
||||
- `RunSpec` — discriminated union (RunPython, RunCommand, RunContainer, RunCompose, RunNode, RunRemote)
|
||||
- `ExposeSpec`, `ProxySpec`, `ManageSpec`, `BuildSpec`
|
||||
- `CaddySpec`, `SystemdSpec`, `HttpExposeSpec`, `HttpInternal`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user