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 <noreply@anthropic.com>
70 lines
3.4 KiB
Markdown
70 lines
3.4 KiB
Markdown
# 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/<name>` 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.
|