Files
unsupervised-scheduler/docs/ci.md
T
thatguygriffandClaude Opus 5 f8762e1095
CI Images / Build CI image (PHP 8.2) (pull_request) Successful in 3s
CI Images / Build CI image (PHP 8.5) (pull_request) Successful in 8s
CI / Tests (PHP 8.3) (pull_request) Successful in 30s
CI Images / Build CI image (PHP 8.3) (pull_request) Successful in 4s
CI / Tests (PHP 8.2) (pull_request) Successful in 23s
CI / Tests (PHP 8.1) (pull_request) Successful in 28s
CI / No Debug Code (pull_request) Successful in 4s
CI / Coding Standards & Static Analysis (pull_request) Successful in 43s
CI / Tests (PHP 8.5) (pull_request) Successful in 23s
CI / Build Plugin Zip (pull_request) Skipped
CI Images / Build CI image (PHP 8.1) (pull_request) Successful in 1m3s
Correct the registry auth docs: the Actions token does not work
docs/ci.md and the workflow comment both described secrets.GITHUB_TOKEN as
the working credential with REGISTRY_TOKEN as a fallback. That is backwards:
the task token is rejected by Gitea's container registry (go-gitea/gitea#23642)
and the first publish attempt failed on exactly that. REGISTRY_TOKEN is
required.

Part of #187

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
2026-08-24 22:35:10 -03:00

3.5 KiB

CI images

CI and release jobs do not install PHP. They run inside prebuilt images published to the Gitea container registry:

git.unsupervised.ca/unsupervised/ci-php:8.1
git.unsupervised.ca/unsupervised/ci-php:8.2
git.unsupervised.ca/unsupervised/ci-php:8.3
git.unsupervised.ca/unsupervised/ci-php:8.5

The Unsupervised org is public, so the packages pull anonymously — jobs need no registry credentials to use them.

Why

shivammathur/setup-php installs PHP 8.3+ from apt/the ondrej PPA on these arm64 runners. That was a ~145s floor against ~35s for 8.1 and 8.2, with a tail that twice ran past the step timeout and failed the run outright (#178). Caching the .debs helped, but the apt step itself remained, and PHP 8.5 has the same shape of problem. Pulling a 67MB image from a registry inside the cluster replaces the whole thing (#187).

What is in the image

.gitea/ci/Dockerfile builds on php:<version>-cli-alpine and adds:

  • bash and nodejs — act_runner runs JavaScript actions (actions/checkout, actions/cache, actions/upload-artifact) inside the job container and shells run: steps through bash. Without these, the first step of every job fails.
  • coreutils, gawk, grep, sed, tar — GNU versions, because the workflow scripts use tac and grep --include, which busybox does not provide. GNU tar matters most: actions/cache shells out to tar --posix -P, and busybox rejects those flags, so every cache step fails without it. zstd is what actions/cache reaches for over gzip when it is installed.
  • curl, jq, git, zip, unzip — used by release.yml and bin/build-zip.sh.
  • intl and zip PHP extensions, plus Composer 2. mbstring is already compiled into the official images.

Publishing

.gitea/workflows/ci-images.yml builds and pushes them. It runs when the Dockerfile changes on main, weekly (so PHP patch releases and Alpine security updates land on their own), and on workflow_dispatch. On a pull request it builds without pushing, so a broken Dockerfile is caught before it reaches main.

Adding or dropping a PHP version

  1. Add the version to the php matrix in .gitea/workflows/ci-images.yml.
  2. Merge to main, or dispatch the workflow, and wait for the tag to appear.
  3. Add the version to the test matrix in .gitea/workflows/ci.yml.

Steps 2 and 3 cannot be one commit: a job cannot run in an image that has not been published yet.

Architecture

The images are built natively on whichever runner picks the job, so they carry that runner's architecture only. Every runner in the pool is arm64 today. If one of a different architecture ever joins, it will overwrite these tags with its own arch and the rest will fail to pull — at which point the build needs docker buildx and a multi-arch manifest.

Registry authentication

The build pushes with the REGISTRY_TOKEN secret, set at the organisation level. This is required, not optional. Gitea's Actions task token (secrets.GITHUB_TOKEN) is rejected by the container registry — docker login fails with Get "https://git.unsupervised.ca/v2/": unauthorized. That is go-gitea/gitea#23642, open since 2023.

REGISTRY_TOKEN is a personal access token with the package scope, Read and Write. The workflow logs in as github.actor, which must be the account that owns the token; if it ever needs to differ, set a REGISTRY_USER variable and the workflow will prefer it.