From 67bf5a19bcae5894b5f82600e7aa05e59a83edb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BCrk?= Date: Fri, 31 Jul 2026 17:08:10 +0200 Subject: [PATCH 1/4] [TASK] DPL-203: Run MySQL functional jobs on MySQL The four workflow steps named "Functional MySQL 8.0 mysqli" and "Functional MySQL 8.0 pdo_mysql" passed "-d mariadb", so they ran the same database as the two MariaDB steps directly above them and only duplicated their coverage under a MySQL name. No workflow invoked "-d mysql" at all, leaving MySQL untested on both core versions. They run "-d mysql" now. The harness needs nothing else - the "mysql" branch, the "mysql-func" container and "IMAGE_MYSQL" have always been there and were simply never reached from CI. The readiness budget covers the slower startup: "waitFor" allows 60 seconds since the docker adoption, and mysql:8.0 needs 12-13 seconds under docker to initialise a fresh data directory. No version is pinned here. "handleDbmsOptions" defaults "-i" to 8.0 for MySQL, which is what the step names promise, so this commit changes the database and nothing else. The explicit "-i" that makes every label verifiable follows in its own commit. The MariaDB steps are left alone. They claim 10.5 while passing no "-i" and therefore run the 10.4 default, which is that same label defect and not part of this change. --- .github/workflows/testcore12.yml | 4 ++-- .github/workflows/testcore13.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/testcore12.yml b/.github/workflows/testcore12.yml index cd1a281..bed8750 100644 --- a/.github/workflows/testcore12.yml +++ b/.github/workflows/testcore12.yml @@ -95,11 +95,11 @@ jobs: - name: "Functional MySQL 8.0 mysqli" run: | - Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli + Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mysql -a mysqli - name: "Functional MySQL 8.0 pdo_mysql" run: | - Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql + Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mysql -a pdo_mysql - name: "Functional PostgresSQL 10" run: | diff --git a/.github/workflows/testcore13.yml b/.github/workflows/testcore13.yml index 909461d..03f5b01 100644 --- a/.github/workflows/testcore13.yml +++ b/.github/workflows/testcore13.yml @@ -95,11 +95,11 @@ jobs: - name: "Functional MySQL 8.0 mysqli" run: | - Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli + Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mysql -a mysqli - name: "Functional MySQL 8.0 pdo_mysql" run: | - Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql + Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mysql -a pdo_mysql - name: "Functional PostgresSQL 10" run: | From a5b56eb0e0451148c34cb3cf3d56602dad843bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BCrk?= Date: Fri, 31 Jul 2026 17:09:00 +0200 Subject: [PATCH 2/4] [TASK] DPL-203: Own the sqlite tmpfs from the host "${USERSET}" passes "--user ${HOST_UID}" without a group, so a docker container runs as "uid=${HOST_UID} gid=0(root)". A "--tmpfs" is created "root:root" and inherits the mode of its host mountpoint, which is 0755 at a CI umask of 0022 - group 0 gets "r-x" only, and the functional sqlite suite fails with "unable to open database file". The docker adoption worked around this by mounting the sqlite tmpfs "mode=1777". That is correct and umask independent, but it treats the one mount rather than the missing group. The mount options move into "TMPFS_MOUNT_OPTIONS", assigned next to the container parameters they belong to, so the two container binaries can state what they actually need: docker adds "uid" and "gid" and keeps "mode=1777", rootless podman maps the container root to the host user and needs neither, keeping "mode=1777" for the rootful case. "${USERSET}" is deliberately left alone. It is evaluated for both container binaries, and appending a group there would change which host group rootless podman maps the container to, which is a different question from who owns a tmpfs. The comment moves along with the options and now names the umask that makes this visible: at the 0002 of a typical workstation the mountpoint comes up 0775 and the defect cannot be reproduced at all. --- Build/Scripts/runTests.sh | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 26c8603..32e2c15 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -461,6 +461,7 @@ handleDbmsOptions COMPOSER_ROOT_VERSION="1.0.8-dev" CONTAINER_INTERACTIVE="-it --init" HOST_UID=$(id -u) +HOST_GID=$(id -g) USERSET="" if [ $(uname) != "Darwin" ]; then USERSET="--user $HOST_UID" @@ -526,9 +527,25 @@ if [ "${CONTAINER_BIN}" == "docker" ]; then CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host ${CONTAINER_HOST}:host-gateway ${USERSET} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" CONTAINER_SIMPLE_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host ${CONTAINER_HOST}:host-gateway ${USERSET} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" DOCUMENTATION_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm ${USERSET} -v ${ROOT_DIR}:/project" + # docker creates a tmpfs owned by "root:root", inheriting the mode of its host + # mountpoint, while "${USERSET}" above passes a uid but no group and therefore runs + # the container as "uid=${HOST_UID} gid=0". At a CI umask of 0022 the mountpoint is + # 0755, so group 0 gets "r-x" and no test database can be created. + # + # "uid"/"gid" address that at the source: the mount is owned by the user the container + # runs as, whatever the umask of the host mountpoint. "mode=1777" is the workaround the + # docker adoption introduced instead, and is kept next to them - it is what has been + # proven on a GitHub hosted runner, and it costs nothing to leave in place. + # + # None of this reproduces at the 0002 umask of a typical workstation, where the + # mountpoint comes up 0775 and the group bit already grants access. Use "umask 0022". + TMPFS_MOUNT_OPTIONS="rw,noexec,nosuid,uid=${HOST_UID},gid=${HOST_GID},mode=1777" else # podman CONTAINER_HOST="host.containers.internal" + # Rootless podman maps the container root to the host user, so the tmpfs is writable + # without an explicit owner. "mode=1777" is kept for the rootful case. + TMPFS_MOUNT_OPTIONS="rw,noexec,nosuid,mode=1777" CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" CONTAINER_SIMPLE_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" DOCUMENTATION_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm -v ${ROOT_DIR}:${ROOT_DIR} -v ${ROOT_DIR}:/project" @@ -646,13 +663,11 @@ 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/" - # "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" + # "${TMPFS_MOUNT_OPTIONS}" carries the owner and mode the mount needs, which + # differ per container binary - see where it is assigned. Without them the + # test databases cannot be created and every test fails with "unable to open + # database file". + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${ROOT_DIR}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/:${TMPFS_MOUNT_OPTIONS} -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=$? ;; From 077f0ccdb19370e24a56d7d1fae44be68e2795d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BCrk?= Date: Fri, 31 Jul 2026 17:10:10 +0200 Subject: [PATCH 3/4] [TASK] DPL-203: Render docs with own parameters Three defects sat on the single "renderDocumentation" run line, so they are fixed together rather than by editing that line three times. It hardcoded "-it" on top of "${CONTAINER_INTERACTIVE}", which is already emptied when "CI" is "true". docker rejects "-t" without a terminal and aborts with "cannot attach stdin to a TTY-enabled container because stdin is not a terminal". No workflow on this branch invokes the suite, so nothing is red today, but it is a hard failure for anyone rendering locally with "-b docker" and it would bite the moment a documentation workflow is added, as "main" already has one. It inlined "${CONTAINER_INTERACTIVE}" while "DOCUMENTATION_COMMON_PARAMS" was assigned for both container binaries and never used. That cost the container "--rm", so every render left a stopped container behind, and under docker it cost "${USERSET}", so the rendered output was written as root into a bind mounted project. The suite renders documentation, it does not talk to a database container, so it now uses the parameters built for it. The bind mount the run line repeated is part of them and is dropped from the line. "CI_PARAMS" is assigned as well. It is expanded into the podman container parameters and into the mock server container but was never assigned, and the mock server line sits outside the podman branch, so the empty expansion reaches the docker path too. It is not a leftover: the harnesses this one is modelled on treat it as an escape hatch a caller can export to inject additional container flags, so the assignment is added rather than the references removed. The working directory is unchanged: neither the old line nor the parameters set one, so the image keeps using its own default. --- Build/Scripts/runTests.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 32e2c15..32e7fe1 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -462,6 +462,9 @@ COMPOSER_ROOT_VERSION="1.0.8-dev" CONTAINER_INTERACTIVE="-it --init" HOST_UID=$(id -u) HOST_GID=$(id -g) +# Additional container parameters, provided by the environment. Empty unless the caller +# exports it, which is how the portfolio harnesses inject CI specific flags. +CI_PARAMS="${CI_PARAMS:-}" USERSET="" if [ $(uname) != "Darwin" ]; then USERSET="--user $HOST_UID" @@ -684,7 +687,7 @@ case ${TEST_SUITE} in SUITE_EXIT_CODE=$? ;; renderDocumentation) - ${CONTAINER_BIN} run ${CONTAINER_INTERACTIVE} --pull always -v ${ROOT_DIR}:/project -it ghcr.io/typo3-documentation/render-guides:latest --config=Documentation + ${CONTAINER_BIN} run ${DOCUMENTATION_COMMON_PARAMS} --pull always ghcr.io/typo3-documentation/render-guides:latest --config=Documentation SUITE_EXIT_CODE=$? ;; phpstan) From 954728649b955101af5c80aba632b9798d904159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BCrk?= Date: Fri, 31 Jul 2026 17:11:19 +0200 Subject: [PATCH 4/4] [TASK] DPL-213: Pin the database versions in CI Ten functional workflow steps named a database version in their label and then passed no "-i", so they ran whatever "handleDbmsOptions" defaults to. The four MariaDB steps claimed 10.5 and ran 10.4. The four MySQL and the two PostgresSQL steps happened to match their label, because 8.0 and 10 are the defaults, but only by coincidence: a changed default would have silently moved them too. Each of the ten now passes the version its name promises, so the label is the contract and no longer a comment that has to be checked against the harness. The values are unchanged in effect except for MariaDB, which moves 10.4 -> 10.5 and is the point of the change. Every version is inside the allowed list of this branch's own "handleDbmsOptions": mariadb 10.4-11.4, mysql 8.0-8.4, postgres 10-16. Nothing had to be approximated. The two "Functional SQLite" steps stay as they are. The "sqlite" arm rejects "-i" outright, and no version is named in their label. The matrix is deliberately not widened. "main" runs its "pdo_mysql" rows on MariaDB 10.11 and MySQL 8.4 while this branch labels them 10.5 and 8.0; making these labels true keeps the narrower matrix this branch already has. Aligning the two branches would add and remove matrix entries, which the follow-ups explicitly rule out, and is left as a separate decision. --- .github/workflows/testcore12.yml | 10 +++++----- .github/workflows/testcore13.yml | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/testcore12.yml b/.github/workflows/testcore12.yml index bed8750..04736fc 100644 --- a/.github/workflows/testcore12.yml +++ b/.github/workflows/testcore12.yml @@ -87,20 +87,20 @@ jobs: - name: "Functional MariaDB 10.5 mysqli" run: | - Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli + Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli -i 10.5 - name: "Functional MariaDB 10.5 pdo_mysql" run: | - Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql + Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql -i 10.5 - name: "Functional MySQL 8.0 mysqli" run: | - Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mysql -a mysqli + Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mysql -a mysqli -i 8.0 - name: "Functional MySQL 8.0 pdo_mysql" run: | - Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mysql -a pdo_mysql + Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d mysql -a pdo_mysql -i 8.0 - name: "Functional PostgresSQL 10" run: | - Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d postgres + Build/Scripts/runTests.sh -b docker -t 12 -p ${{ matrix.php-version }} -s functional -d postgres -i 10 diff --git a/.github/workflows/testcore13.yml b/.github/workflows/testcore13.yml index 03f5b01..c974f38 100644 --- a/.github/workflows/testcore13.yml +++ b/.github/workflows/testcore13.yml @@ -87,20 +87,20 @@ jobs: - name: "Functional MariaDB 10.5 mysqli" run: | - Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli + Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a mysqli -i 10.5 - name: "Functional MariaDB 10.5 pdo_mysql" run: | - Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql + Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mariadb -a pdo_mysql -i 10.5 - name: "Functional MySQL 8.0 mysqli" run: | - Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mysql -a mysqli + Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mysql -a mysqli -i 8.0 - name: "Functional MySQL 8.0 pdo_mysql" run: | - Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mysql -a pdo_mysql + Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d mysql -a pdo_mysql -i 8.0 - name: "Functional PostgresSQL 10" run: | - Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d postgres + Build/Scripts/runTests.sh -b docker -t 13 -p ${{ matrix.php-version }} -s functional -d postgres -i 10