docs: two-stage deploy model (staging auto, production manual)
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 9m36s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 10s
PR Checks / Build Frontend (no push) (pull_request) Successful in 43s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Failing after 2m10s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
Tudor
2026-07-13 08:38:37 +01:00
co-authored by Claude Fable 5
parent 75e92dc7f5
commit 2b563cc0bf
2 changed files with 44 additions and 19 deletions
+8 -4
View File
@@ -112,11 +112,15 @@ Full details in `docs/DEPLOY.md`. The short version:
- **Never push to `main` directly.** Work on a feature branch and open a PR; - **Never push to `main` directly.** Work on a feature branch and open a PR;
branch protection requires the PR checks (typecheck, tests, builds, AI review) branch protection requires the PR checks (typecheck, tests, builds, AI review)
to pass before merge. to pass before merge.
- Merging to `main` deploys automatically: images are built once, deployed to - Merging to `main` deploys automatically **to staging only**: images are
the **staging** Portainer stack, verified by the Playwright journeys in built once, deployed to the staging Portainer stack, and verified by the
`e2e/`, and only then retagged `:prod` and rolled out to production. Playwright journeys in `e2e/`. Production is a second, manual approval:
the "Promote to Production (manual)" workflow in Gitea Actions, run after
testing the feature on staging. It refuses commits whose staging E2E gate
isn't green. Never trigger it yourself — promotion is the human's call.
- If you change user-facing behaviour, update or extend the `e2e/` journey - If you change user-facing behaviour, update or extend the `e2e/` journey
tests in the same PR — they are the promotion gate. tests in the same PR — they gate whether staging is fit for human testing
and whether a commit is promotable.
## Recent Changes ## Recent Changes
+36 -15
View File
@@ -1,41 +1,61 @@
# SDLC & Deployment Pipeline # SDLC & Deployment Pipeline
SchoolCompare uses a fully automated staging → production pipeline on Gitea SchoolCompare uses a two-stage deploy model on Gitea Actions with two human
Actions. AI writes the code on feature branches; the pipeline verifies every approvals. AI writes the code on feature branches; the first approval merges
change on a staging environment before promoting the exact same images to the PR, which deploys to staging and runs the E2E gate; the second approval —
production. Human input is directional only: feature requests, PR review if after manual testing on staging — promotes the exact same images to
desired, and intervention when a gate fails. production via a manual workflow.
## The flow ## The flow
``` ```
feature branch (AI-authored) feature branch (AI-authored)
│ PR to main │ PR to main ← approval #1
PR checks (.gitea/workflows/pr-checks.yml) PR checks (.gitea/workflows/pr-checks.yml)
typecheck + unit tests + backend smoke + image builds (no push) typecheck + unit tests + backend smoke + image builds (no push)
+ Claude code review posted as a PR comment (severe findings fail the check) + Claude code review posted as a PR comment (severe findings fail the check)
│ merge (branch protection requires green checks) │ merge (branch protection requires green checks)
Deploy pipeline (.gitea/workflows/deploy.yml) Stage pipeline (.gitea/workflows/deploy.yml) — automatic
1. build & push images → tags sha-<sha>, staging 1. build & push images → tags sha-<sha>, staging
2. staging Portainer webhook → wait for staging health 2. staging Portainer webhook → wait for staging health
3. Playwright E2E journeys against staging 3. Playwright E2E journeys against staging ← gate before human testing
4. retag sha-<sha> → :prod (same bytes — build once, promote the image)
Manual testing on staging (stx.schoolcompare.co.uk)
│ Actions → "Promote to Production (manual)" ← approval #2
Promote pipeline (.gitea/workflows/promote.yml) — manual dispatch
1. resolve target sha (input, or latest main if empty)
2. REFUSE unless that commit's "E2E Journeys against Staging" status is green
3. retag sha-<sha> → :prod (same bytes — build once, promote the image)
previous :prod saved as :prod-previous previous :prod saved as :prod-previous
5. prod Portainer webhook → wait for prod health 4. prod Portainer webhook → wait for prod health
``` ```
Key principle: **build once, promote the exact image**. Production pins `:prod`, Key principle: **build once, promote the exact image**. Production pins `:prod`,
which only moves after the E2E gate passes on staging. Nothing tags `:latest` which only moves when a human runs the promote workflow — and the workflow
only accepts commits that passed the staging E2E gate. Nothing tags `:latest`
anymore. anymore.
## Branch & PR workflow ## Branch & PR workflow
- `main` is protected: no direct pushes, PRs require green status checks. - `main` is protected: no direct pushes, PRs require green status checks.
- All work (human or AI) happens on feature branches → PR to `main`. - All work (human or AI) happens on feature branches → PR to `main`.
- Merging to `main` **is** the release action. If staging or the E2E gate - Merging to `main` releases **to staging only**. Production moves only on
fails, production is untouched. the second approval. If staging or the E2E gate fails, fix forward —
production is untouched either way.
## Promotion granularity
Staging always runs the latest `main`. Promoting approves a *state of main*,
not a single PR — if two PRs merged since the last promotion, they ship
together. Test staging accordingly. To promote an older state, pass its
commit SHA to the promote workflow (its images must still exist in the
registry).
Staging quirk for manual testing: external `/api` is broken at the staging
proxy — exercise API endpoints from the host, not via the public staging URL.
## Environments ## Environments
@@ -92,8 +112,9 @@ fail the E2E gate. That's the point: staging absorbs the risk.
## Rollback ## Rollback
Every promotion first re-points `:prod-previous` at the outgoing `:prod`. Re-run "Promote to Production (manual)" with the SHA of the last good commit
To roll back: (fastest, fully gated), or manually re-point the tags — every promotion first
saves the outgoing `:prod` as `:prod-previous`:
```bash ```bash
for img in backend frontend pipeline; do for img in backend frontend pipeline; do