# ADR 0014: CI/CD with Gitea Actions, staged promotion - Status: accepted - Date: 2026-07-04 ## Context The project is hosted on a self-managed Gitea (`gitea.101010.cloud`). Environments (kickoff): Dev runs locally on contributors' machines; Test and Int run as separate Compose stacks on the operator's dedicated server ONE (`one.101010.cloud`, domains `test.dorfteich.cloud` / `int.dorfteich.cloud`), which also hosts Gitea itself; Prod (`dorfteich.online`) goes live on the same host at M8 — the architecture must keep any later move cheap. Contributors include AI coding sessions — gates must be automated and objective wherever possible. (Originally the stages and runner lived on a shared 4-GB VPS, `188.245.116.44`; everything moved to ONE on 2026-07-11.) ## Decision - **Gitea Actions** is the CI/CD system (GitHub-Actions-compatible syntax); an act_runner runs on ONE (`one.101010.cloud`) with Docker access. - **Images** are built once per change and promoted, never rebuilt per stage: pushed to the **Gitea container registry** (`gitea.101010.cloud/stwaidele/dorfteich-{web,api,collab}`), tagged with the git SHA plus moving tags `test`, `int`, and semver tags for releases. - **Pipeline** (details in `deployment.md`): 1. **PR / push**: lint, typecheck, unit tests, build; PRs must be green to merge into `main`. 2. **Merge to `main`**: build + push images (SHA tag) → deploy to **Test** automatically → run the Playwright e2e suite against Test. 3. **Promotion to Int**: automatic when e2e on Test is green — Int is the stable preview environment (same images, `int` tag). 4. **Promotion to Prod**: **manual gate** — creating a release tag (`vX.Y.Z`) triggers the Prod deploy after a required manual approval in the workflow. Database migrations run automatically on container start (ADR 0006); releases with breaking migrations must say so in the release notes. - **Deploy mechanism**: the runner executes `docker compose pull && docker compose up -d` in the stage's directory (`/home/DOCKER/dorfteich-/`) over SSH (deploy key per stage). Moving Prod to a dedicated host later changes only that SSH target. - **Self-hosters** consume the same release images via a published `docker-compose.yml` pinned to semver tags (update strategy in `operations.md`). ## Consequences - One objective quality bar (green e2e on Test) decides Int promotion; the only human gate is the Prod release — matching the risk profile. - The registry, runner, and repo live on the same Gitea — no third-party CI dependency. - e2e stability becomes load-bearing; flaky tests block the pipeline and must be treated as defects (stated in the contributor conventions). ## Alternatives considered - **GitHub Actions with repo mirror**: splits source of truth; rejected. - **Manual deploys per stage**: does not scale to many small stories and invites drift between stages.