From 4757a174c9f2218c4e94a06fdf7f19512c7743d5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 2 Jun 2026 19:24:20 +0200 Subject: [PATCH] docs(adr): add ADR-029 composite actions for cross-workflow deploy logic Records the decision to extract the shared obs-deploy/reload-caddy/smoke-test logic into three composite actions instead of a reusable workflow or shared shell script. Numbered 029 (028 was taken by the pdf.js wasm ADR on main since the issue was filed). Co-Authored-By: Claude Opus 4.8 --- ...actions-for-cross-workflow-deploy-logic.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 docs/adr/029-composite-actions-for-cross-workflow-deploy-logic.md diff --git a/docs/adr/029-composite-actions-for-cross-workflow-deploy-logic.md b/docs/adr/029-composite-actions-for-cross-workflow-deploy-logic.md new file mode 100644 index 00000000..25e0192e --- /dev/null +++ b/docs/adr/029-composite-actions-for-cross-workflow-deploy-logic.md @@ -0,0 +1,69 @@ +# ADR-029: Composite actions for cross-workflow deploy logic + +## Status + +Accepted + +## Context + +The `nightly.yml` (staging) and `release.yml` (production) workflows shared three +blocks of deploy logic verbatim: the four observability-stack steps (deploy configs, +validate, start, assert health), the Caddy reload step, and the public-surface smoke +test. The only per-environment differences were secret names (`STAGING_*` vs `PROD_*`), +the `POSTGRES_HOST` value, and the smoke-test hostname. + +This duplication was held together by `# Keep in sync with nightly.yml` comments — an +honour-system invariant. Any change (a new healthchecked service, a different rsync flag, +a new secret) had to be applied in two places, and nothing enforced that it was. Issue #603 +documents a real instance: the obs secret set had grown to five keys while a refactor draft +listed only four. + +### Decision drivers + +1. Cross-workflow deploy logic must have a single definition, enforced — not a + discipline-based "keep in sync" promise. +2. Per-environment variation must be expressed as explicit, typed inputs, not by forking + the whole step block. +3. The mechanism must work on the existing single-tenant self-hosted Gitea runner with no + new infrastructure. + +### Alternatives considered + +**A: Reusable workflow (`workflow_call`)** — Gitea supports called workflows. Rejected for +this case: reusable workflows run as a separate job with their own runner context, which +breaks the in-job, sequential `deploy → reload → smoke` ordering these steps rely on and +complicates passing the already-checked-out workspace. Composite actions run inline in the +calling job, preserving step order and the workspace. + +**B: Shared shell script invoked from both workflows** — e.g. `scripts/deploy-obs.sh`. +Rejected: loses the typed-input contract and per-step CI log sections, and reintroduces +manual argument threading that is as error-prone as the duplication it replaces. + +**C: Keep the `# Keep in sync` comments** — status quo. Rejected: unenforced; issue #603 +is direct evidence it fails. + +## Decision + +Extract the shared logic into three single-responsibility Gitea composite actions under +`.gitea/actions/`: `deploy-obs` (five inputs), `reload-caddy` (no inputs), and `smoke-test` +(`host` input). Both workflows invoke each via a single `uses: ./.gitea/actions/` call, +passing per-environment values as `with:` inputs. This is the repository's first composite +action and sets the convention; `docs/infrastructure/ci-gitea.md` documents it. + +## Consequences + +**Positive:** +- Shared deploy logic has one enforced definition; a change lands once and both + environments get it. The `# Keep in sync` comments are deleted. +- Per-environment variation is a typed input contract, not a forked block. +- Runs inline on the existing runner — no reusable-workflow job context, no new + infrastructure. + +**Negative / constraints:** +- Workflows now depend on a checked-out `.gitea/actions/` tree: `actions/checkout` MUST run + before the first `uses: ./…` (a local action does not exist on disk until checkout). +- Secrets cannot be read from the `secrets.*` context inside a composite action; they must + be passed as inputs and mapped to `env:`. The `obs-secrets.env` heredoc therefore uses an + unquoted delimiter so `$VAR` expands at the shell layer. +- The `reload-caddy` pinned alpine digest now lives in the action, not the workflow file — + it must be added to Renovate's watch list so it does not go stale.