[TASK] Run CI containers with docker - #70
Merged
Merged
Conversation
GitHub hosted runners ship both podman and docker. Since 2026-07-29 their podman/crun combination intermittently aborts the first container start of a job with "OCI runtime error: crun: unknown version specified" (exit code 126), independent of the job, the core version or the PHP version. Neither the runner image nor the TYPO3 testing image changed, and a rerun on another host clears it. runTests.sh prefers podman whenever it is present and only falls back to docker. That default is correct for the script and is kept, since podman-only machines are exactly what it is built for. GitHub hosted runners are the single place these workflows meet the broken combination, so the override belongs in the workflows: every "runTests.sh" call passes "-b docker" now, with the reasoning noted in the workflow header so the flag can be dropped knowingly later. Selecting docker exposes a second, unrelated defect. docker runs the container as "--user $HOST_UID" with group 0, while the sqlite tmpfs inherits the mode of its host mountpoint -- 0755 and owned by root at the umask a runner uses. No test database can be created then, and every functional sqlite test fails with "unable to open database file". Rootless podman is root inside its user namespace and passes no "--user", which is why this never showed before. The tmpfs is mounted with "mode=1777" now, which docker needs, podman does not mind, and which keeps the suite independent of the umask in use. The documentation rendering container run hardcoded "-it" on top of the interactive flags the script already manages. docker rejects "-t" without a TTY, so that suite could not run once docker is selected. The redundant flag is dropped: CI mode stays non-interactive, while local runs keep the "-it --init" the script adds itself.
waitFor() capped the readiness poll at 11 iterations of "sleep 1", so roughly 11 seconds. Measured against the same images, the time from "run -d" until the port accepts connections is: mysql:8.0 podman 7.0-7.2s docker 12.2-13.2s mariadb:10.4 podman 7.8s docker 8.2s postgres:10 podman 1.3s docker 1.5s Only mysql crosses that limit, and only under docker, whose entrypoint needs about twice as long to initialise a fresh data directory. The workflows select docker now, so the functional mysql suites began to abort intermittently. The cap is 60 seconds instead, which also leaves mariadb a sensible margin. The abort also did not abort. "kill -SIGINT -$$" relies on the SIGINT trap, and that trap is only installed when CI is not "true". In CI the kill was a no-op, so the script carried on and ran the test suite against a database that was not listening, which then reported dozens of "Connection refused" errors instead of the readiness timeout that had actually occurred. waitFor() cleans up and exits directly now.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
GitHub hosted runners ship both podman and docker. Since 2026-07-29 their
podman/crun combination intermittently aborts the first container start of a
job with
exit code 126. It is independent of the job, the core version and the PHP
version, and a rerun on another host clears it. Neither the runner image nor the
TYPO3 testing image changed, so this is variance in the GitHub host fleet.
The change
Build/Scripts/runTests.shprefers podman whenever it is present and only fallsback to docker. That default is correct for the script — podman-only machines
are exactly what it is built for — so it stays. The GitHub hosted runners are
the single place these workflows meet the broken combination, so the override
belongs in the workflows.
Every
runTests.shcall now passes-b docker, with the reasoning in theworkflow header so the flag can be dropped knowingly once GitHub stops producing
the mismatch.
This mirrors what is already merged across the
deepl*extension family, andoriginates from fgtclb/academic-extensions.
Companion fixes
Selecting docker exposes defects that podman masked. Only the ones that apply to
this repository are included — see the summary below.
mode=1777— docker runs the container as--user $HOST_UIDwith group 0, while the tmpfs inherits the mode of its hostmountpoint (
0755 root:rootat a runner's umask). No test database can becreated, and every functional sqlite test fails with
unable to open database file. Rootless podman is root inside its user namespace and passes no--user, which is why this never showed.mode=1777fixes it for bothruntimes and makes the suite umask-independent.
-it— the documentation rendering run hardcoded-iton top ofthe interactivity the script already manages. docker rejects
-twithout aTTY, so that suite cannot run under docker in CI.
waitFor()readiness cap — the poll aborted after ~11 s.mysql:8.0needs 12–13 s to become ready under docker versus 7 s under podman, and
mariadb 8.2 s. The cap is 60 s now. The abort itself also never fired in CI,
because
kill -SIGINT -$$relies on a SIGINT trap that is only installed whenCIis not"true"— so the suite ran against a database that was notlistening and reported misleading
Connection refusederrors.Applied here
-b dockeron 35runTests.shcalls across.github/workflows/testcore12.yml,.github/workflows/testcore13.yml,.github/workflows/publish.yml— includingthe commented-out calls, so re-enabling a job cannot reintroduce the problem.
None carried a
-bflag before.mode=1777: yes-itremoved: yes (renderDocumentation) — release-critical here,because
publish.ymlruns that suite to build the documentation artefact thatis attached to the GitHub release
waitForcap 11s -> 60s: yes (second commit)