From 4a5e3d88793ddd2e78c33d5dc2b5d6ab8e60da27 Mon Sep 17 00:00:00 2001 From: kristianwind Date: Wed, 16 Sep 2026 10:40:19 +0200 Subject: [PATCH] Run the text-domain check in a container when the runner has no PHP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first Gitea run failed on `php: command not found`. GitHub's hosted runners ship PHP; the Gitea runner image does not, and the whole static job went red on one line after everything else in it had passed. ci_php() now uses the local php when there is one — a laptop, GitHub's runners — and a php:8.4-cli container otherwise. The repository goes in over a tarball and `docker cp` rather than a bind mount, because a Gitea Actions job runs inside a container talking to the HOST's docker daemon, so a host path there lands on an empty directory nobody created. Also: the include scan now says when it found nothing to check instead of reporting success. Running the suite with a deliberately crippled PATH showed it printing "0 plugin-relative include(s) checked" and then "green" — a check passing by checking nothing, which is the same failure the PHP lint step already guards against. Co-Authored-By: Claude Opus 5 (1M context) --- ci/lib.sh | 29 +++++++++++++++++++++++++++++ ci/static.sh | 11 +++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/ci/lib.sh b/ci/lib.sh index 3437f23..d34d3c1 100644 --- a/ci/lib.sh +++ b/ci/lib.sh @@ -46,6 +46,35 @@ ci_plugin_header() { "$(ci_repo_root)/$PLUGIN_MAIN" | head -1 | sed -E 's/[[:space:]]+$//' } +# Run one of this repository's PHP scripts. +# +# Uses the local php when there is one — a laptop, GitHub's hosted runners — +# and a container otherwise. The Gitea runner image has no php, and losing a +# build to "php: command not found" after everything else passed is a silly way +# to spend an afternoon. Measured: it happened on the very first Gitea run. +# +# The repository goes in over a tarball and `docker cp`, never a bind mount: a +# Gitea Actions job runs inside a container talking to the HOST's docker +# daemon, so a host path here lands on an empty directory nobody created. +ci_php() { + if command -v php >/dev/null 2>&1; then + php "$@" + return + fi + + local tarball cid code=0 + tarball=$(mktemp -t wpci-php-XXXXXX.tar) + ( cd "$(ci_repo_root)" && git ls-files -z | tar --null -T - -cf "$tarball" ) + + cid=$(docker create "php:8.4-cli" sh -c \ + "mkdir -p /src && tar -xf /tmp/src.tar -C /src && cd /src && exec php $(printf '%q ' "$@")") + docker cp "$tarball" "$cid:/tmp/src.tar" >/dev/null + docker start -a "$cid" || code=$? + docker rm -f "$cid" >/dev/null + rm -f "$tarball" + return "$code" +} + ci_step() { printf '\n\033[1m→ %s\033[0m\n' "$*"; } ci_fail() { printf '\033[31mFAIL\033[0m %s\n' "$*" >&2; } diff --git a/ci/static.sh b/ci/static.sh index 65a4079..5015d3a 100755 --- a/ci/static.sh +++ b/ci/static.sh @@ -84,7 +84,7 @@ done <<< "$(git ls-files '*.php')" [ "$fails" = "$before" ] && echo " clean" ci_step "text domain" -php ci/check-textdomain.php "$TEXT_DOMAIN" . || fails=$((fails + 1)) +ci_php ci/check-textdomain.php "$TEXT_DOMAIN" . || fails=$((fails + 1)) ci_step "every file the plugin loads is a file the release contains" # The bug this exists for: a new class file is written, required from the main @@ -107,7 +107,14 @@ for path in $referenced; do printf '%s\n' "$shipped" | grep -qx "$path" \ || note_fail "The code loads '$path', but the release zip does not contain it. Committed?" done -echo " $checked plugin-relative include(s) checked" +if [ "$checked" = 0 ]; then + # A check that verifies nothing must say so rather than reporting success. + # Not a failure: a single-file plugin, or one with an autoloader instead of + # a list of requires, legitimately has nothing here. + echo "::warning::No plugin-relative include was found to check. That is expected for a single-file plugin or an autoloader, and a broken scan otherwise." +else + echo " $checked plugin-relative include(s) checked" +fi ci_step "one version, stated once" # WordPress compares the update feed against the header and nothing else, so a