U6 — the deploy workflow itself
deploy-scp.yaml: download, back up the production database before touching anything, upload into a timestamped release, symlink the persistent website in, switch current and restart, verify /health with retries, prune old releases only once that check passes. No sudo, no container actions, one shared interface U5 will call next.
This commit is contained in:
+62
@@ -0,0 +1,62 @@
|
||||
# Code Generation Plan — U6 Deploy Workflow
|
||||
|
||||
## Unit Context
|
||||
|
||||
- **Unit**: U6 Deploy Workflow (Pipeline-type — YAML only, no C#/frontend code)
|
||||
- **Component**: C-11 Deploy transport workflows (`.gitea/workflows/`)
|
||||
- **Requirements**: FR-02, FR-03, FR-06, FR-08, FR-20
|
||||
- **Depends on**: U1, U2, U5 (per `unit-of-work-dependency.md`) — U1/U2 are already committed
|
||||
(Rounds 1–2); U5 does not exist yet, but U6 only needs to **expose** the interface U5 will call,
|
||||
not the other way around, so generating U6 first is safe
|
||||
- **Depended on by**: U5 (invokes this workflow), U7, Operations
|
||||
- **Basis**: `construction/u6-deploy-workflow/infrastructure-design/infrastructure-design.md`
|
||||
(approved) and `deployment-architecture.md`
|
||||
- **No test project applies**: `.gitea/workflows/` is pipeline configuration, not an application
|
||||
project — there is no C# test project for it, mirroring how the Slave API has none (CLAUDE.md).
|
||||
Verification for this unit is YAML validity plus logical review; real execution requires the
|
||||
actual Pi, SSH credentials and database, which is explicitly out of scope until Operations (per
|
||||
U6's Definition of Done in `unit-of-work.md`).
|
||||
|
||||
## Steps
|
||||
|
||||
- [x] Step 1: Generate `.gitea/workflows/deploy-scp.yaml` — reusable `workflow_call` workflow
|
||||
- `on.workflow_call.inputs`: `artifact_name`, `environment`, `deploy_path`, `service_name`,
|
||||
`health_check_url` (all required strings), `run_db_backup` (optional boolean, default `false`),
|
||||
`transport` (optional string, default `scp`)
|
||||
- Single `deploy` job, plain shell steps only (D-05 — no container actions)
|
||||
- Step: download artifact (`actions/download-artifact`)
|
||||
- Step: production-only DB backup — `if: ${{ inputs.run_db_backup }}`, SSH into the host and
|
||||
invoke `~/scripts/backup-slpmodularcms-db.sh` (script itself is an Operations/host-setup
|
||||
artifact, not generated here)
|
||||
- Step: `sshpass` + `scp` the artifact into `${{ inputs.deploy_path }}/releases/<timestamp>/`
|
||||
(timestamp computed once, reused across steps via a job output — same pattern the reference
|
||||
project uses for the `config` job)
|
||||
- Step: ensure `${{ inputs.deploy_path }}/shared/wwwroot-web` exists (`mkdir -p`, idempotent),
|
||||
remove the publish output's own `wwwroot/web`, symlink it to the shared directory
|
||||
- Step: atomically switch `${{ inputs.deploy_path }}/current` to the new release (`ln -sfn`)
|
||||
- Step: restart `systemctl --user restart ${{ inputs.service_name }}` over SSH
|
||||
- Step: health check — retry loop, `curl -f` against `${{ inputs.health_check_url }}`, bounded
|
||||
attempts with a short sleep between tries
|
||||
- Step: prune releases beyond the retention count of 2, **conditional on the health check having
|
||||
passed** (`if: success()` on the pruning step, following the preceding step's outcome)
|
||||
- Inline comments explaining each non-obvious step (Podman container-action failure, why pruning
|
||||
is conditional, why the timestamp is a job output), matching the reference project's comment
|
||||
density for anything a future reader would otherwise have to re-derive
|
||||
|
||||
- [x] Step 2: Validate YAML
|
||||
- Parse `deploy-scp.yaml` to confirm it is syntactically valid YAML and produces the exact
|
||||
`workflow_call` input shape listed above (no test framework applies — see Unit Context)
|
||||
- Result: valid; 7 inputs (5 required, 2 optional with correct defaults), 1 job, 8 steps —
|
||||
matches plan exactly
|
||||
|
||||
- [x] Step 3: Documentation — `construction/u6-deploy-workflow/code/generation-summary.md`
|
||||
- What was generated, key decisions carried from Infrastructure Design, what is explicitly
|
||||
deferred to Operations (host script, real Pi verification, variable/secret runtime values)
|
||||
|
||||
## Story / Requirement Traceability
|
||||
|
||||
| Step | Covers |
|
||||
|---|---|
|
||||
| 1 | FR-02 (reusable workflow, minimum inputs + extensions), FR-03/FR-04 (environment-agnostic — U5 controls when each environment is called), FR-06 (atomic switch, restart), FR-08/ASM-01 (persistent website symlink), FR-20 (production DB backup) |
|
||||
| 2 | Definition of Done: "workflow YAML valid" |
|
||||
| 3 | Definition of Done: "deployment sequence documented step by step including the rollback path" (rollback path itself is documented in `infrastructure-design.md` § 4 and carried to Operations — this summary cross-references it, not duplicates it) |
|
||||
Reference in New Issue
Block a user