From 1c4026646febdaf24def4435c0404a8b586b26c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BCrk?= Date: Thu, 30 Jul 2026 11:15:16 +0200 Subject: [PATCH 1/2] [TASK] Run CI containers with docker 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. --- .github/workflows/documentation.yml | 12 +++++++- .github/workflows/publish.yml | 13 +++++++- .github/workflows/testcore13.yml | 48 +++++++++++++++++------------ .github/workflows/testcore14.yml | 46 ++++++++++++++++----------- Build/Scripts/runTests.sh | 10 ++++-- 5 files changed, 88 insertions(+), 41 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 3bf9683..dfc2480 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -1,5 +1,15 @@ name: documentation +# Every "runTests.sh" call below passes "-b docker". The script prefers podman +# whenever it is present and only falls back to docker, which is the right +# default for it -- but GitHub hosted runners ship both, and since 2026-07-29 +# their podman/crun combination aborts the first container start of a job with +# "OCI runtime error: crun: unknown version specified" (exit code 126). It is +# intermittent, hits any job, and was traced to neither the runner image nor +# the TYPO3 testing image changing. Selecting docker here avoids crun entirely +# and leaves the script default and local runs untouched. Drop the flag once +# GitHub stops producing the mismatch. + on: pull_request: @@ -27,7 +37,7 @@ jobs: comment-author: 'github-actions[bot]' - name: "Render documentation" - run: "Build/Scripts/runTests.sh -b podman -s renderDocumentation" + run: "Build/Scripts/runTests.sh -b docker -s renderDocumentation" - uses: actions/upload-artifact@v6 id: documentation-artifact diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4f4f83d..37e00ab 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,4 +1,15 @@ name: publish + +# Every "runTests.sh" call below passes "-b docker". The script prefers podman +# whenever it is present and only falls back to docker, which is the right +# default for it -- but GitHub hosted runners ship both, and since 2026-07-29 +# their podman/crun combination aborts the first container start of a job with +# "OCI runtime error: crun: unknown version specified" (exit code 126). It is +# intermittent, hits any job, and was traced to neither the runner image nor +# the TYPO3 testing image changing. Selecting docker here avoids crun entirely +# and leaves the script default and local runs untouched. Drop the flag once +# GitHub stops producing the mismatch. + on: push: tags: @@ -71,7 +82,7 @@ jobs: - name: "Render documentation" run: | - Build/Scripts/runTests.sh -s renderDocumentation + Build/Scripts/runTests.sh -b docker -s renderDocumentation mkdir -p tailor-version-artefact && \ cd Documentation-GENERATED-temp && \ zip -r ../tailor-version-artefact/${{ env.DETECTED_EXTENSION_KEY }}_${{ env.version }}-documentation.zip . diff --git a/.github/workflows/testcore13.yml b/.github/workflows/testcore13.yml index 04c17d7..c339aa0 100644 --- a/.github/workflows/testcore13.yml +++ b/.github/workflows/testcore13.yml @@ -1,5 +1,15 @@ name: tests core 13 +# Every "runTests.sh" call below passes "-b docker". The script prefers podman +# whenever it is present and only falls back to docker, which is the right +# default for it -- but GitHub hosted runners ship both, and since 2026-07-29 +# their podman/crun combination aborts the first container start of a job with +# "OCI runtime error: crun: unknown version specified" (exit code 126). It is +# intermittent, hits any job, and was traced to neither the runner image nor +# the TYPO3 testing image changing. Selecting docker here avoids crun entirely +# and leaves the script default and local runs untouched. Drop the flag once +# GitHub stops producing the mismatch. + on: pull_request: workflow_dispatch: @@ -17,34 +27,34 @@ jobs: uses: actions/checkout@v6 - name: "Prepare dependencies for TYPO3 v13" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s composerUpdate" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s composerUpdate" - name: "Run TypoScript lint" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s lintTypoScript" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s lintTypoScript" - name: "Run PHP lint" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s lintPhp" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s lintPhp" - name: "Validate CGL" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s cgl -n" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s cgl -n" - name: "Ensure tests methods do not start with \"test\"" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s checkTestMethodsPrefix" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s checkTestMethodsPrefix" - name: "Ensure UTF-8 files do not contain BOM" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s checkBom" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s checkBom" - name: "Verify bundled contrib library composer constraint and lock" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s checkContribComposer" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s checkContribComposer" # - name: "Test .rst files for integrity" -# run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s checkRst" +# run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s checkRst" - name: "Find duplicate exception codes" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s checkExceptionCodes" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s checkExceptionCodes" - name: "Run PHPStan" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s phpstan" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s phpstan" testsuite: name: all tests with core v13 @@ -59,28 +69,28 @@ jobs: uses: actions/checkout@v6 - name: "Run PHP lint" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s lintPhp" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s lintPhp" - name: "Prepare dependencies for TYPO3 v13" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s composerUpdate" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s composerUpdate" - name: "Unit" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s unit" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s unit" - name: "Functional SQLite" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s functional -d sqlite" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d sqlite" - name: "Functional MariaDB 10.5 mysqli" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli" - name: "Functional MariaDB 10.5 pdo_mysql" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql" - name: "Functional MySQL 8.0 mysqli" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli" - name: "Functional MySQL 8.0 pdo_mysql" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql" - name: "Functional PostgresSQL 10" - run: "Build/Scripts/runTests.sh -t 13 -p ${{ matrix.php-version }} -s functional -d postgres" + run: "Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d postgres" diff --git a/.github/workflows/testcore14.yml b/.github/workflows/testcore14.yml index d67d7be..65500ac 100644 --- a/.github/workflows/testcore14.yml +++ b/.github/workflows/testcore14.yml @@ -1,5 +1,15 @@ name: tests core 14 +# Every "runTests.sh" call below passes "-b docker". The script prefers podman +# whenever it is present and only falls back to docker, which is the right +# default for it -- but GitHub hosted runners ship both, and since 2026-07-29 +# their podman/crun combination aborts the first container start of a job with +# "OCI runtime error: crun: unknown version specified" (exit code 126). It is +# intermittent, hits any job, and was traced to neither the runner image nor +# the TYPO3 testing image changing. Selecting docker here avoids crun entirely +# and leaves the script default and local runs untouched. Drop the flag once +# GitHub stops producing the mismatch. + on: pull_request: workflow_dispatch: @@ -17,31 +27,31 @@ jobs: uses: actions/checkout@v6 - name: "Prepare dependencies for TYPO3 v14" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s composerUpdate" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s composerUpdate" - name: "Run TypoScript lint" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s lintTypoScript" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s lintTypoScript" - name: "Run PHP lint" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s lintPhp" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s lintPhp" - name: "Validate CGL" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s cgl -n" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s cgl -n" - name: "Ensure tests methods do not start with \"test\"" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s checkTestMethodsPrefix" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s checkTestMethodsPrefix" - name: "Ensure UTF-8 files do not contain BOM" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s checkBom" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s checkBom" # - name: "Test .rst files for integrity" -# run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s checkRst" +# run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s checkRst" - name: "Find duplicate exception codes" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s checkExceptionCodes" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s checkExceptionCodes" - name: "Run PHPStan" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s phpstan" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s phpstan" testsuite: name: all tests with core v14 @@ -56,28 +66,28 @@ jobs: uses: actions/checkout@v6 - name: "Run PHP lint" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s lintPhp" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s lintPhp" - name: "Prepare dependencies for TYPO3 v14" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s composerUpdate" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s composerUpdate" - name: "Unit" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s unit" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s unit" - name: "Functional SQLite" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s functional -d sqlite" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s functional -d sqlite" - name: "Functional MariaDB 10.5 mysqli" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli" - name: "Functional MariaDB 10.5 pdo_mysql" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql" - name: "Functional MySQL 8.0 mysqli" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli" - name: "Functional MySQL 8.0 pdo_mysql" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql" - name: "Functional PostgresSQL 10" - run: "Build/Scripts/runTests.sh -t 14 -p ${{ matrix.php-version }} -s functional -d postgres" + run: "Build/Scripts/runTests.sh -b docker -t 14 -p ${{ matrix.php-version }} -s functional -d postgres" diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 7c1d4b4..7d139cf 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -555,7 +555,13 @@ case ${TEST_SUITE} in sqlite) # create sqlite tmpfs mount typo3temp/var/tests/functional-sqlite-dbs/ to avoid permission issues mkdir -p "${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/" - CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid -e DEEPL_API_KEY=mock_server -e DEEPL_HOST=deepl-func-${SUFFIX} -e DEEPL_PORT=3000 -e DEEPL_SERVER_URL=deepl-func-${SUFFIX}:3000 -e DEEPL_MOCK_SERVER_PORT=3000 -e DEEPL_SCHEME=http -e DEEPL_MOCKSERVER_USED=1" + # "mode=1777" is required for docker and harmless for podman: docker runs + # the container as "--user $HOST_UID" with group 0, while the tmpfs comes + # up owned by root with mode 0755, so the test databases cannot be created + # and every test fails with "unable to open database file". Rootless podman + # passes no "--user" (it is root inside its user namespace), which is why + # this only shows with docker. + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid,mode=1777 -e DEEPL_API_KEY=mock_server -e DEEPL_HOST=deepl-func-${SUFFIX} -e DEEPL_PORT=3000 -e DEEPL_SERVER_URL=deepl-func-${SUFFIX}:3000 -e DEEPL_MOCK_SERVER_PORT=3000 -e DEEPL_SCHEME=http -e DEEPL_MOCKSERVER_USED=1" ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" SUITE_EXIT_CODE=$? ;; @@ -572,7 +578,7 @@ case ${TEST_SUITE} in SUITE_EXIT_CODE=$? ;; renderDocumentation) - ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name rendering-documentation-${SUFFIX} --pull always -w /project -v ${ROOT_DIR}:/project -it ${IMAGE_RSTRENDERING} --fail-on-error --no-progress --config=Documentation Documentation + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name rendering-documentation-${SUFFIX} --pull always -w /project -v ${ROOT_DIR}:/project ${IMAGE_RSTRENDERING} --fail-on-error --no-progress --config=Documentation Documentation SUITE_EXIT_CODE=$? ;; phpstan) From cc137e78260ca2f2ce93e4a3094c1f662e9dc203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BCrk?= Date: Thu, 30 Jul 2026 11:15:51 +0200 Subject: [PATCH 2/2] [BUGFIX] Wait longer for database readiness 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. --- Build/Scripts/runTests.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 7d139cf..f47688a 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -10,10 +10,13 @@ fi waitFor() { local HOST=${1} local PORT=${2} + # 60 rather than 10 seconds: mysql:8.0 needs 12-13s under docker to + # initialise a fresh data directory, about twice as long as under podman, + # so an 11 second budget aborted the functional mysql suites at random. local TESTCOMMAND=" COUNT=0; while ! nc -z ${HOST} ${PORT}; do - if [ \"\${COUNT}\" -gt 10 ]; then + if [ \"\${COUNT}\" -gt 60 ]; then echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\"; exit 1; fi; @@ -23,7 +26,11 @@ waitFor() { " ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} /bin/sh -c "${TESTCOMMAND}" if [[ $? -gt 0 ]]; then - kill -SIGINT -$$ + # Not "kill -SIGINT -$$": the SIGINT trap is only installed when CI is + # not "true", so in CI the signal was a no-op, the run continued and the + # test suite connected to a database that was not listening. + cleanUp + exit 1 fi }