Publish prebuilt CI images to the Gitea container registry
CI Images / Build CI image (PHP 8.2) (pull_request) Successful in 1m14s
CI Images / Build CI image (PHP 8.1) (pull_request) Successful in 1m26s
CI Images / Build CI image (PHP 8.3) (pull_request) Successful in 2m3s
CI Images / Build CI image (PHP 8.5) (pull_request) Successful in 2m9s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m7s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.3) (pull_request) Successful in 3m8s
CI / Tests (PHP 8.1) (pull_request) Successful in 5m55s
CI / Tests (PHP 8.5) (pull_request) Successful in 6m21s
CI / Coding Standards & Static Analysis (pull_request) Successful in 18m26s
CI / Build Plugin Zip (pull_request) Skipped

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
This commit is contained in:
2026-08-24 21:55:28 -03:00
co-authored by Claude Opus 5
parent 116394f2ff
commit 572aaf5b49
3 changed files with 224 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
name: CI Images
# Publishes the per-PHP-version images that ci.yml and release.yml run inside
# (#187). Nothing else consumes them, so this workflow is the only place the
# registry path is written down.
#
# Triggers:
# - the Dockerfile or this workflow changing on main, so an edit ships;
# - the same paths on a pull request, which builds but does not push, so a
# broken Dockerfile is caught before it reaches main;
# - workflow_dispatch, to rebuild on demand;
# - weekly, so PHP patch releases and Alpine security updates land without
# anyone remembering to ask.
on:
push:
branches:
- main
paths:
- '.gitea/ci/Dockerfile'
- '.gitea/workflows/ci-images.yml'
pull_request:
paths:
- '.gitea/ci/Dockerfile'
- '.gitea/workflows/ci-images.yml'
schedule:
- cron: '17 4 * * 1'
workflow_dispatch:
env:
# The instance ROOT_URL host — the container registry lives on the same host.
REGISTRY: git.unsupervised.ca
IMAGE: unsupervised/ci-php
jobs:
build:
name: Build CI image (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
strategy:
# One version failing should not hide whether the others built.
fail-fast: false
matrix:
# Keep in step with the test matrix in ci.yml.
php:
- '8.1'
- '8.2'
- '8.3'
- '8.5'
steps:
- uses: actions/checkout@v4
# The images are built natively, so they carry the runner's
# architecture only. Fine while every runner is arm64; if a runner of a
# different architecture ever joins the pool it will overwrite these
# tags with its own arch and the others will fail to pull.
- name: Check Docker is available
run: |
if ! docker info >/dev/null 2>&1; then
echo "No usable Docker daemon in the job container." >&2
echo "act_runner needs container.docker_host set (or left empty to autodetect)." >&2
exit 1
fi
docker version --format 'client {{.Client.Version}} / server {{.Server.Version}} / arch {{.Server.Arch}}'
# secrets.GITHUB_TOKEN is the Actions task token and can write packages
# for the repository owner. REGISTRY_TOKEN is an escape hatch: set it to
# a PAT with package:write if the task token is ever refused.
- name: Log in to the container registry
if: github.event_name != 'pull_request'
run: |
echo "${{ secrets.REGISTRY_TOKEN || secrets.GITHUB_TOKEN }}" \
| docker login "${REGISTRY}" -u "${{ vars.REGISTRY_USER || github.actor }}" --password-stdin
- name: Build
run: |
docker build \
--pull \
--build-arg "PHP_VERSION=${{ matrix.php }}" \
--tag "${REGISTRY}/${IMAGE}:${{ matrix.php }}" \
--file .gitea/ci/Dockerfile \
.gitea/ci
# Pull requests build only — the tags on the registry are what the other
# workflows run inside, so only main and a manual dispatch move them.
- name: Push
if: github.event_name != 'pull_request'
run: |
image="${REGISTRY}/${IMAGE}:${{ matrix.php }}"
docker push "${image}"
echo "Published ${image}"
- name: Log out
if: always() && github.event_name != 'pull_request'
run: docker logout "${REGISTRY}" || true