Run tests as a workflow and gate builds on them #9

Merged
geusebio merged 14 commits from ci/run-tests-before-build into main 2026-07-25 17:15:26 +00:00
Owner

What

  • New reusable Test workflow (.github/workflows/test.yml) that runs the docker-compose test suite via ./test (workflow_call + workflow_dispatch).
  • Build now calls it as its first job; build-base (and therefore the whole build chain) needs: [test], so nothing is baked or pushed unless the full suite passes. The old commented-out test step is gone.
  • ./test accepts a PROGRESS env override (default unchanged: none) so CI gets full --progress plain build logs on failure.
  • Runner-only steps (Docker Hub login, disk-space purge) are guarded with if: ${{ !env.ACT }}, so the workflow is testable locally with act.

Verification

  • YAML parses (anchors included); job graph verified with act -n.
  • Full live run under act: suite passed in 301s, job succeeded, exit 0.
  • Remaining unknown: cold-cache duration/disk on a real runner — Test has timeout-minutes: 60 and frees ~30GB of preinstalled toolchains first.

🤖 Generated with Claude Code

## What - New reusable **Test** workflow (`.github/workflows/test.yml`) that runs the docker-compose test suite via `./test` (`workflow_call` + `workflow_dispatch`). - **Build** now calls it as its first job; `build-base` (and therefore the whole build chain) `needs: [test]`, so nothing is baked or pushed unless the full suite passes. The old commented-out test step is gone. - `./test` accepts a `PROGRESS` env override (default unchanged: `none`) so CI gets full `--progress plain` build logs on failure. - Runner-only steps (Docker Hub login, disk-space purge) are guarded with `if: ${{ !env.ACT }}`, so the workflow is testable locally with act. ## Verification - YAML parses (anchors included); job graph verified with `act -n`. - Full live run under act: suite passed in 301s, job succeeded, exit 0. - Remaining unknown: cold-cache duration/disk on a real runner — Test has `timeout-minutes: 60` and frees ~30GB of preinstalled toolchains first. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds a reusable Test workflow that runs the docker-compose test suite
via ./test, triggered on PRs, manual dispatch, and workflow_call. The
Build workflow now calls it and build-base needs it, so nothing is
baked or pushed unless the full suite passes.

The test script's compose progress mode is now overridable via
PROGRESS (still defaults to none) so CI can get full build logs, and
runner-only steps are guarded with !env.ACT so the workflow runs
locally under act. Verified end-to-end with act: suite passed in 301s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merge branch 'main' of https://git.grey.ooo/Grey.ooo/docker into ci/run-tests-before-build
Some checks failed
Build / Test (push) Has been cancelled
Build / Build base-alpine (push) Has been cancelled
Build / Build php (push) Has been cancelled
Build / Build magento (push) Has been cancelled
Build / Build frankenphp (push) Has been cancelled
Build / Build node (push) Has been cancelled
Build / Build dotnet (push) Has been cancelled
Build / Build java (push) Has been cancelled
Build / Build java-tomcat (push) Has been cancelled
Build / Build java-war-machine (push) Has been cancelled
Build / Run container tests (push) Has been cancelled
6eeceaa380
Drop standalone pull_request trigger from Test workflow
Some checks failed
Build / Test (push) Failing after 0s
Build / Build base-alpine (push) Has been skipped
Build / Build php (push) Has been skipped
Build / Build frankenphp (push) Has been skipped
Build / Build node (push) Has been skipped
Build / Build dotnet (push) Has been skipped
Build / Build java (push) Has been skipped
Build / Build magento (push) Has been skipped
Build / Build java-tomcat (push) Has been skipped
Build / Build java-war-machine (push) Has been skipped
Build / Run container tests (push) Failing after 0s
cb4e19ac7b
Build now runs on every push and calls Test via workflow_call, so a
pull_request trigger would run the suite twice per branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Inline test job: Forgejo runner rejects workflow_call
Some checks failed
Build / Run container tests (push) Failing after 10m15s
Build / Build base-alpine (push) Has been skipped
Build / Build php (push) Has been skipped
Build / Build frankenphp (push) Has been skipped
Build / Build node (push) Has been skipped
Build / Build dotnet (push) Has been skipped
Build / Build java (push) Has been skipped
Build / Build magento (push) Has been skipped
Build / Build java-tomcat (push) Has been skipped
Build / Build java-war-machine (push) Has been skipped
1621d2110a
swarm-runner v11.3.1 fails schema validation on the __metadata block
Forgejo 15 injects when synthesizing a called workflow, so the Test
job died before its first step. Inline the job into Build instead and
drop test.yml. Also drop the disk-space purge — this is a self-hosted
runner, not a GitHub-hosted VM.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
Owner

CI failed on the first push: swarm-runner v11.3.1 rejects the __metadata block Forgejo injects when synthesizing a workflow_call workflow (schema validation: "Unknown Property __metadata / the workflow file is not usable"). Reworked in 1621d21: the test job is now inlined into Build instead of a reusable workflow, test.yml is gone, and the GitHub-hosted-runner disk purge step is dropped since this runs on a self-hosted runner. Gating is unchanged: build-base (and the whole chain) still needs the test job. Note: upgrading the runner would re-enable reusable workflows if wanted later.

CI failed on the first push: swarm-runner v11.3.1 rejects the `__metadata` block Forgejo injects when synthesizing a `workflow_call` workflow (schema validation: "Unknown Property __metadata / the workflow file is not usable"). Reworked in 1621d21: the test job is now inlined into Build instead of a reusable workflow, test.yml is gone, and the GitHub-hosted-runner disk purge step is dropped since this runs on a self-hosted runner. Gating is unchanged: build-base (and the whole chain) still needs the test job. Note: upgrading the runner would re-enable reusable workflows if wanted later.
The php-zts packages from pkg.henderkes.com crash with Illegal
instruction on the runner's amd64 CPU (they work on native aarch64).
Follows the existing pattern of commented-out known-broken targets;
re-enable once upstream targets baseline x86-64 or the runner CPU
is upgraded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fix Docker Hub login guard; pin job image from ghcr.io
Some checks failed
build.yml / Fix Docker Hub login guard; pin job image from ghcr.io (push) Failing after 0s
e242289197
The !env.ACT guard skipped Docker Hub login in real CI too, because
the Forgejo runner is act-based and sets ACT=true. Replace the step
condition with a runtime check on the secret (act and swarm-runner
both also reject the secrets context in step-level if expressions).

Also pin ghcr.io/catthehacker/ubuntu:act-latest as the job container
on every job: the runner's default ubuntu-latest label maps to
git.grey.ooo/mirrors/act-runner:ubuntu-latest, whose pull 504ed and
killed a run. The mirror workflow especially must not depend on the
mirror registry it maintains.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
Owner

Three more fixes after watching runs 191/192: (1) run 191 died pulling the runner's default job image from git.grey.ooo/mirrors (504 from the registry) — all jobs now pin ghcr.io/catthehacker/ubuntu:act-latest directly via container:, including mirror-baseimages.yml which must not depend on the registry it maintains. (2) The !env.ACT login guard skipped Docker Hub login in real CI (swarm-runner is act-based and sets ACT=true) — replaced with a runtime secret check. (3) Run 192 was a genuine catch by the new gate: frankenphp images crash with Illegal instruction on the runner's amd64 CPU (pkg.henderkes.com php-zts builds; same crash class as the aarch64 igbinary note in FrankenPHP.Dockerfile). Disabled those test deps with a comment, following the existing pattern for known-broken targets — needs a separate fix upstream or a runner CPU change.

Three more fixes after watching runs 191/192: (1) run 191 died pulling the runner's default job image from git.grey.ooo/mirrors (504 from the registry) — all jobs now pin ghcr.io/catthehacker/ubuntu:act-latest directly via `container:`, including mirror-baseimages.yml which must not depend on the registry it maintains. (2) The `!env.ACT` login guard skipped Docker Hub login in real CI (swarm-runner is act-based and sets ACT=true) — replaced with a runtime secret check. (3) Run 192 was a genuine catch by the new gate: frankenphp images crash with Illegal instruction on the runner's amd64 CPU (pkg.henderkes.com php-zts builds; same crash class as the aarch64 igbinary note in FrankenPHP.Dockerfile). Disabled those test deps with a comment, following the existing pattern for known-broken targets — needs a separate fix upstream or a runner CPU change.
Inline container image per job: Forgejo can't anchor container
Some checks failed
Build / Build base-alpine (push) Has been cancelled
Build / Build php (push) Has been cancelled
Build / Build magento (push) Has been cancelled
Build / Build frankenphp (push) Has been cancelled
Build / Build node (push) Has been cancelled
Build / Build dotnet (push) Has been cancelled
Build / Build java (push) Has been cancelled
Build / Build java-tomcat (push) Has been cancelled
Build / Build java-war-machine (push) Has been cancelled
Build / Run container tests (push) Has been cancelled
46d589ff5a
The job parser expands YAML anchors on some job fields (name,
permissions, steps) but not container, failing with 'unknown anchor
job-container referenced' before the run could start.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add CLAUDE.md: mandate background CI monitoring, document Forgejo quirks
Some checks failed
Build / Build base-alpine (push) Has been cancelled
Build / Build php (push) Has been cancelled
Build / Build magento (push) Has been cancelled
Build / Build frankenphp (push) Has been cancelled
Build / Build node (push) Has been cancelled
Build / Build dotnet (push) Has been cancelled
Build / Build java (push) Has been cancelled
Build / Build java-tomcat (push) Has been cancelled
Build / Build java-war-machine (push) Has been cancelled
Build / Run container tests (push) Has been cancelled
77de057903
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fix merge conflict in .github/workflows/build.yml - merge PR 9 with main
Some checks failed
Build / Build base-alpine (push) Has been skipped
Build / Run container tests (push) Failing after 10m35s
Build / Build php (push) Has been skipped
Build / Build frankenphp (push) Has been skipped
Build / Build dotnet (push) Has been skipped
Build / Build magento (push) Has been skipped
Build / Build java (push) Has been skipped
Build / Build node (push) Has been skipped
Build / Build java-tomcat (push) Has been skipped
Build / Build java-war-machine (push) Has been skipped
35c462c832
Resolve conflict by including both:
- fail-fast: false (from main)
- needs: [ test ] (from PR 9)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Re-pull test service image at up time
Some checks failed
Build / Run container tests (push) Successful in 7m18s
Build / Build base-alpine (push) Successful in 1m40s
Build / Build java (push) Successful in 3m30s
Build / Build node (push) Successful in 3m38s
Build / Build dotnet (push) Successful in 6m7s
Build / Build java-war-machine (push) Successful in 2m52s
Build / Build java-tomcat (push) Successful in 3m15s
Build / Build frankenphp (push) Failing after 8m28s
Build / Build php (push) Successful in 9m55s
Build / Build magento (push) Has been cancelled
0c07322345
Run 202: alpine:3.23 was pulled at 09:02, but by 09:10 - after all
~40 targets had built and validated - creating the test container
failed with 'No such image'. Something on the shared runner daemon
(likely a prune from a concurrent job) removed it mid-run. pull_policy
always shrinks the stale window from minutes to seconds; the built
images are created immediately after building so they don't have this
exposure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
Owner

Run 202 got tantalisingly close: all ~40 targets built and validated in 8 minutes, then creating the final test orchestrator container failed with 'No such image: alpine:3.23' — the image was pulled at the start of the run but vanished from the daemon 8 minutes later, most likely pruned by a concurrent job on the shared runner daemon (main's runs 200/201 were failing on the same host at the time). Fixed in 0c07322 with pull_policy: always on the test service so it re-pulls seconds before create instead of relying on a stale pull. Worth knowing for the runner host: if something there prunes images while jobs are running, any long job on that daemon can lose images mid-run — the built compose images are equally unreferenced until their containers start.

Run 202 got tantalisingly close: all ~40 targets built and validated in 8 minutes, then creating the final `test` orchestrator container failed with 'No such image: alpine:3.23' — the image was pulled at the start of the run but vanished from the daemon 8 minutes later, most likely pruned by a concurrent job on the shared runner daemon (main's runs 200/201 were failing on the same host at the time). Fixed in 0c07322 with `pull_policy: always` on the test service so it re-pulls seconds before create instead of relying on a stale pull. Worth knowing for the runner host: if something there prunes images while jobs are running, any long job on that daemon can lose images mid-run — the built compose images are equally unreferenced until their containers start.
Disable build-frankenphp until #10
All checks were successful
Build / Run container tests (push) Successful in 7m8s
Build / Build base-alpine (push) Successful in 1m37s
Build / Build java (push) Successful in 3m0s
Build / Build node (push) Successful in 3m13s
Build / Build dotnet (push) Successful in 5m28s
Build / Build java-war-machine (push) Successful in 2m35s
Build / Build java-tomcat (push) Successful in 2m50s
Build / Build php (push) Successful in 9m20s
Build / Build magento (push) Successful in 10m50s
ffb3939139
The Dockerfile runs validate during the image build, and the php-zts
binaries crash with Illegal instruction on the runner's amd64 CPU, so
the target can never build in CI. Keeping a permanently red job would
leave every run failing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
Owner

Run 204: the test gate passed for the first time (full suite green in ~8 min) and the build fan-out worked — base-alpine, php, node, dotnet, java, java-tomcat, java-war-machine all succeeded. Build frankenphp failed: FrankenPHP.Dockerfile runs validate during the image build itself, so the Illegal-instruction crash (#10) makes the target unbuildable on this runner regardless of the disabled tests. Disabled the build-frankenphp job in ffb3939 with a pointer to #10 — a job that can never pass would keep every run red. Watching the fresh run for a fully green board.

Run 204: the test gate passed for the first time (full suite green in ~8 min) and the build fan-out worked — base-alpine, php, node, dotnet, java, java-tomcat, java-war-machine all succeeded. Build frankenphp failed: FrankenPHP.Dockerfile runs validate during the image build itself, so the Illegal-instruction crash (#10) makes the target unbuildable on this runner regardless of the disabled tests. Disabled the build-frankenphp job in ffb3939 with a pointer to #10 — a job that can never pass would keep every run red. Watching the fresh run for a fully green board.
Author
Owner

Fully green — the run for ffb3939 finished with the test gate passing (second consecutive green suite, ~8 min) and all eight build jobs succeeding behind it. The PR now does what it says end-to-end: no image gets baked or pushed unless the full test suite passes first. Outstanding, tracked separately: #10 (frankenphp Illegal instruction on the runner's amd64 CPU — tests and build job both parked pending that fix) and the runner-host image-prune race noted earlier (mitigated repo-side with pull_policy: always).

Fully green ✅ — the run for ffb3939 finished with the test gate passing (second consecutive green suite, ~8 min) and all eight build jobs succeeding behind it. The PR now does what it says end-to-end: no image gets baked or pushed unless the full test suite passes first. Outstanding, tracked separately: #10 (frankenphp Illegal instruction on the runner's amd64 CPU — tests and build job both parked pending that fix) and the runner-host image-prune race noted earlier (mitigated repo-side with pull_policy: always).
Merge branch 'main' into ci/run-tests-before-build
Some checks failed
build.yml / Merge branch 'main' into ci/run-tests-before-build (push) Failing after 0s
build.yml / Merge branch 'main' into ci/run-tests-before-build (pull_request) Failing after 0s
805ff790e3
# Conflicts:
#	.github/workflows/build.yml
geusebio force-pushed ci/run-tests-before-build from 805ff790e3
Some checks failed
build.yml / Merge branch 'main' into ci/run-tests-before-build (push) Failing after 0s
build.yml / Merge branch 'main' into ci/run-tests-before-build (pull_request) Failing after 0s
to d51daa5485
Some checks failed
Build / Build base-alpine (push) Failing after 5m23s
Build / Build php (push) Has been skipped
Build / Build frankenphp (push) Has been skipped
Build / Build node (push) Has been skipped
Build / Build dotnet (push) Has been skipped
Build / Build java (push) Has been skipped
Build / Build magento (push) Has been skipped
Build / Build java-tomcat (push) Has been skipped
Build / Build java-war-machine (push) Has been skipped
Build / Run container tests (push) Successful in 6m56s
2026-07-25 15:32:17 +00:00
Compare
Retrigger CI: runner binfmt_misc was unmounted, now fixed
Some checks failed
Build / Build php (push) Successful in 21m42s
Build / Build java (push) Successful in 34m36s
Build / Build node (push) Successful in 8m48s
Build / Build magento (push) Successful in 11m5s
Build / Build dotnet (push) Successful in 37m42s
Build / Run container tests (push) Successful in 6m6s
Build / Build base-alpine (push) Successful in 3m50s
Build / Build frankenphp (push) Successful in 4m37s
Build / Build java-tomcat (push) Has been cancelled
Build / Build java-war-machine (push) Has been cancelled
75febaccd6
Merge remote-tracking branch 'origin/main' into ci/run-tests-before-build
Some checks failed
Build / Run container tests (push) Failing after 11m36s
Build / Build (push) Has been cancelled
47c7cd9ec9
# Conflicts:
#	.github/workflows/build.yml
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Grey.ooo/docker!9
No description provided.