Step 1 of #187. Adds the CI images and the workflow that publishes them; nothing runs inside them yet.
shivammathur/setup-php installs PHP 8.3+ from apt/the ondrej PPA on these arm64 runners — a ~145s floor against ~35s for 8.1/8.2, with a tail that twice ran past the step timeout and failed the run (#178). The .deb cache softened that without removing the apt step, and 8.5 has the same shape of problem.
What this adds
.gitea/ci/Dockerfile — php:<version>-cli-alpine plus bash and nodejs (act_runner executes actions/checkout, actions/cache and actions/upload-artifactinside the job container), GNU coreutils/grep/sed (the workflow scripts use tac and grep --include, which busybox lacks), curl/jq/git/zip/unzip for release.yml and bin/build-zip.sh, the intl and zip extensions, and Composer 2.
.gitea/workflows/ci-images.yml — matrix build over 8.1/8.2/8.3/8.5, pushing git.unsupervised.ca/unsupervised/ci-php:<version>. Runs on Dockerfile changes to main, weekly, and on workflow_dispatch. On a pull request it builds without pushing, so this PR's own run proves the Dockerfile before it can move a tag.
docs/ci.md — what is in the image and why, how to add a PHP version, and the arch caveat.
The Unsupervised org is public, so the packages pull anonymously and the consuming jobs need no registry credentials.
Why two PRs
A job cannot run in an image that does not exist yet, so ci.yml and release.yml switch to container: only once these tags are published.
Things to watch on the first run
Docker in the job container. The build needs a usable daemon; act_runner provides one when container.docker_host is empty or set. The first step checks for it and says so plainly if it is missing.
Registry auth. It logs in with secrets.GITHUB_TOKEN. If Gitea refuses that for package writes, set a REGISTRY_TOKEN secret with a package:write PAT — the workflow already prefers it when present.
Tests
composer test — 966 tests, 2795 assertions, OK
composer lint and composer cs unaffected (no PHP source changed)
All three workflow files parse as YAML
No CHANGELOG.md entry: CI tooling only, nothing user-visible.
Step 1 of #187. Adds the CI images and the workflow that publishes them; nothing runs inside them yet.
`shivammathur/setup-php` installs PHP 8.3+ from apt/the ondrej PPA on these arm64 runners — a ~145s floor against ~35s for 8.1/8.2, with a tail that twice ran past the step timeout and failed the run (#178). The `.deb` cache softened that without removing the apt step, and 8.5 has the same shape of problem.
## What this adds
- **`.gitea/ci/Dockerfile`** — `php:<version>-cli-alpine` plus `bash` and `nodejs` (act_runner executes `actions/checkout`, `actions/cache` and `actions/upload-artifact` *inside* the job container), GNU `coreutils`/`grep`/`sed` (the workflow scripts use `tac` and `grep --include`, which busybox lacks), `curl`/`jq`/`git`/`zip`/`unzip` for `release.yml` and `bin/build-zip.sh`, the `intl` and `zip` extensions, and Composer 2.
- **`.gitea/workflows/ci-images.yml`** — matrix build over 8.1/8.2/8.3/8.5, pushing `git.unsupervised.ca/unsupervised/ci-php:<version>`. Runs on Dockerfile changes to `main`, weekly, and on `workflow_dispatch`. On a pull request it builds without pushing, so this PR's own run proves the Dockerfile before it can move a tag.
- **`docs/ci.md`** — what is in the image and why, how to add a PHP version, and the arch caveat.
The `Unsupervised` org is public, so the packages pull anonymously and the consuming jobs need no registry credentials.
## Why two PRs
A job cannot run in an image that does not exist yet, so `ci.yml` and `release.yml` switch to `container:` only once these tags are published.
## Things to watch on the first run
- **Docker in the job container.** The build needs a usable daemon; act_runner provides one when `container.docker_host` is empty or set. The first step checks for it and says so plainly if it is missing.
- **Registry auth.** It logs in with `secrets.GITHUB_TOKEN`. If Gitea refuses that for package writes, set a `REGISTRY_TOKEN` secret with a `package:write` PAT — the workflow already prefers it when present.
## Tests
- `composer test` — 966 tests, 2795 assertions, OK
- `composer lint` and `composer cs` unaffected (no PHP source changed)
- All three workflow files parse as YAML
No `CHANGELOG.md` entry: CI tooling only, nothing user-visible.
setup-php installs PHP 8.3+ from apt on these arm64 runners: a ~145s floor
against ~35s for 8.1/8.2, with a tail that twice ran past the step timeout
and failed the run (#178). Caching the .debs softened it without removing
the apt step, and 8.5 has the same problem.
Add a per-version CI image built on php:<version>-cli-alpine and a workflow
that publishes it to git.unsupervised.ca/unsupervised/ci-php:<version>. The
org is public, so the packages pull anonymously.
The image carries bash and nodejs because act_runner runs JavaScript actions
inside the job container, GNU coreutils/grep/sed because the workflow scripts
use `tac` and `grep --include`, and curl/jq/git/zip for release.yml and
bin/build-zip.sh. Composer 2 and the intl and zip extensions round it out.
Nothing consumes the images yet — ci.yml and release.yml switch over in a
follow-up, because a job cannot run in an image that has not been published.
Part of #187
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Step 1 of #187. Adds the CI images and the workflow that publishes them; nothing runs inside them yet.
shivammathur/setup-phpinstalls PHP 8.3+ from apt/the ondrej PPA on these arm64 runners — a ~145s floor against ~35s for 8.1/8.2, with a tail that twice ran past the step timeout and failed the run (#178). The.debcache softened that without removing the apt step, and 8.5 has the same shape of problem.What this adds
.gitea/ci/Dockerfile—php:<version>-cli-alpineplusbashandnodejs(act_runner executesactions/checkout,actions/cacheandactions/upload-artifactinside the job container), GNUcoreutils/grep/sed(the workflow scripts usetacandgrep --include, which busybox lacks),curl/jq/git/zip/unzipforrelease.ymlandbin/build-zip.sh, theintlandzipextensions, and Composer 2..gitea/workflows/ci-images.yml— matrix build over 8.1/8.2/8.3/8.5, pushinggit.unsupervised.ca/unsupervised/ci-php:<version>. Runs on Dockerfile changes tomain, weekly, and onworkflow_dispatch. On a pull request it builds without pushing, so this PR's own run proves the Dockerfile before it can move a tag.docs/ci.md— what is in the image and why, how to add a PHP version, and the arch caveat.The
Unsupervisedorg is public, so the packages pull anonymously and the consuming jobs need no registry credentials.Why two PRs
A job cannot run in an image that does not exist yet, so
ci.ymlandrelease.ymlswitch tocontainer:only once these tags are published.Things to watch on the first run
container.docker_hostis empty or set. The first step checks for it and says so plainly if it is missing.secrets.GITHUB_TOKEN. If Gitea refuses that for package writes, set aREGISTRY_TOKENsecret with apackage:writePAT — the workflow already prefers it when present.Tests
composer test— 966 tests, 2795 assertions, OKcomposer lintandcomposer csunaffected (no PHP source changed)No
CHANGELOG.mdentry: CI tooling only, nothing user-visible.794df2cb4fto572aaf5b49