DEVX-157: docs: add ADR-0003 and update docs for composite actions

This commit is contained in:
2026-08-12 19:32:28 +00:00
parent ad98d76c1f
commit f5d3b72a38
12 changed files with 504 additions and 133 deletions
+155
View File
@@ -0,0 +1,155 @@
# ADR-0003: Composite Actions for CI Workflow Reuse
Date: 2026-08-12
Status: Accepted
## Context
The devx repository's Gitea Actions workflows (`.gitea/workflows/ci.yml`,
`post-merge.yml`, `build-images.yml`) repeated multi-step
sequences across jobs:
1. **Set up environment**`make setup-image` (optionally with `EXTRAS=`).
Appeared verbatim in 4 jobs across `ci.yml` and `post-merge.yml`, each
with the same `CI_GITEA_API_TOKEN` env wiring.
2. **Notify on failure**`python3 -m devx.ci.notify_failure ... --auto-login`
with venv activation, PATH export, and 5 fixed CLI args. Appeared in
4 jobs (`ci/validate`, `post-merge/detect-and-configure`,
`post-merge/release-and-maintain`, `build-images/build-and-push`),
each differing only in the `--workflow` string.
3. **Quality checks** — a 6-step sequence (lint-all, pytest-cov,
check-test-speed, devx-docs-check, check-translations, pip-audit)
with venv activation boilerplate on every step. Appeared once in
`ci.yml` validate job, but the same sequence is needed by `grm` and
`infra` (Phase 2b/2c of the cross-repo refactoring plan).
This duplication had the following costs:
- **Drift risk**: a fix to notify-failure (for example, new flag,
different env var) had to be applied to 4 places; missing one caused
inconsistent failure notifications.
- **Workflow YAML noise**: the 6-step quality block obscured the
validate job's actual structure (detect-changes, pr-review,
release-dry-run).
- **Cross-repo reuse blocked**: `grm` and `infra` could not adopt the
same quality-checks sequence without copy-pasting the inline steps,
which would amplify the drift problem across 3 repos.
- **Gitea 1.27 constraints**: every `run` step needs explicit `shell:`;
composite actions cannot access `secrets` directly (only `env:`).
These constraints had to be re-discovered and re-applied per step.
## Decision
Introduce three Gitea composite actions in `.gitea/actions/`:
### 1. `setup-env/action.yml`
Wraps the `make setup-image` call. Single input `extras` (default empty)
forwarded to `make setup-image EXTRAS=`. Reads `CI_GITEA_API_TOKEN` and
`CI_GITEA_USERNAME` from the calling workflow's `env:` context.
### 2. `notify-failure/action.yml`
Wraps the `devx.ci.notify_failure` invocation. Single required input
`workflow` (the workflow/job name for the Gitea issue title). Step is
gated by `if: failure()` so it only runs on job failure. Reads
`CI_GITEA_API_TOKEN` from the calling workflow's `env:` context.
### 3. `quality-checks/action.yml`
Wraps the 6-step quality sequence. Inputs:
- `package` (default empty) — sets `DEVX_DOC_VERSIONS_PKG` for doc
version checks (for example, `devx`, `grm`).
- `test-speed-max` (default `15`) — total test seconds threshold.
- `test-speed-max-single` (default `0.5`) — per-test seconds threshold.
- `translations-file` (default empty) — path to `translations.json`
for repos whose translations live outside `src/devx/`.
Each step activates the venv defensively
(`. .venv/bin/activate 2>/dev/null || true`) so the action works with
both pre-built CI images (which symlink `/opt/venv` to `.venv`) and
fresh `make setup-image` runs.
### Adoption Scope
- **`ci.yml` validate job**: `setup-env` + `quality-checks` +
`notify-failure`.
- **`ci.yml` auto-merge job**: `setup-env` only (no quality checks,
no notify-failure — auto-merge failure is surfaced by the validate
job's notify-failure).
- **`post-merge.yml` detect-and-configure**: `setup-env` +
`notify-failure`.
- **`post-merge.yml` release-and-maintain**: `setup-env` (with
`extras: "release"`) + `notify-failure`.
- **`build-images.yml` build-and-push**: `notify-failure` only. The
setup steps use `make setup-release` and `make setup-ci` (not
`make setup-image`), so `setup-env` does not apply. The cleanup job
has no notify-failure step (it only runs on build-and-push success).
### Same-Repo Copies (No Cross-Repo References)
Each consumer repo (`devx`, `grm`, `infra`) gets its own copy of the
composite actions under its `.gitea/actions/` directory. There is no
`uses: oblachno-oss/devx/.gitea/actions/...@vX.Y.Z` reference.
This avoids a single-point-of-failure where a bad `devx` master commit
would break all three repos' CI simultaneously. The cost is three
copies of ~30 lines of YAML each, updated manually when a composite
action changes. Given the stability of these patterns (the inline
versions were unchanged for months), this cost is acceptable.
## Consequences
### Positive
- **Workflow YAML is shorter and clearer**: the validate job's
quality block collapses from 32 lines to 5 lines. The intent
(`uses: ./.gitea/actions/quality-checks`) is more legible than
6 individually wrapped steps.
- **Drift eliminated**: a change to notify-failure (new flag, different
env var) is applied in one file. All 4 calling sites pick it up.
- **Cross-repo reuse enabled**: Phase 2b (`grm`) and Phase 2c (`infra`)
copy the same `action.yml` files and adopt the same `uses:` pattern.
The quality-checks sequence is now portable.
- **Gitea 1.27 constraints centralized**: the `shell: bash` and
`env:` (not `secrets`) patterns are encoded once per action, not
re-derived per step.
- **No release triggered**: changes to `.gitea/**` are classified as
workflow-only by `devx.ci.classify_changes`. Phase 2a does not
produce a new devx version. `grm`/`infra` bump to the Phase 1
release (v0.50.4), not a Phase 2a version.
### Negative
- **Three copies of each action**: when a composite action changes,
the change must be applied to `devx`, `grm`, and `infra`
independently. This is intentional (see Same-Repo Copies preceding)
but is a maintenance cost.
- **Composite action debugging is harder**: Gitea's log output for
composite action steps is nested under the action name. Finding the
failing step requires reading one more level of indentation.
- **`env:` propagation is implicit**: the calling workflow's top-level
`env:` block must define `CI_GITEA_API_TOKEN` for the composite
action to read it. A workflow that omits this will see an empty
token at runtime, not at lint time. actionlint does not catch this.
- **`quality-checks` is devx-shaped**: the `package` and
`translations-file` inputs exist because consumer repos (for example,
`grm`) have translations files outside the default
`src/devx/translations.json` location and need doc version checks
targeting their own package name.
A repo with a different translations path or package layout would need
a new input or a different action. This is acceptable for the current
3-repo scope.
### Neutral
- **`if: failure()` is preserved**: the `notify-failure` composite
action's step has `if: failure()`, which is evaluated in the
calling workflow's job-status context. This is the standard Gitea
Actions pattern for post-failure notification.
- **Venv activation is defensive**: `. .venv/bin/activate 2>/dev/null
|| true` does not fail if the venv is missing (pre-built image path)
or already active. This matches the inline pattern's behavior.