Merge pull request #21 from kristianwind/selftest-gitea-safe
CI / Static checks (push) Successful in 32s
CI / WordPress latest + WooCommerce latest on PHP 8.3 (push) Failing after 1m56s

Make the self-test run on Gitea, where it has been failing since it landed
This commit is contained in:
Kristian Wind
2026-09-17 00:47:23 +02:00
committed by GitHub
+67 -11
View File
@@ -132,31 +132,47 @@ echo "$out" | grep -qE '^ [0-9]+ shipped PHP files examined' \
|| bad "the direct-access check printed no count — it cannot tell 13 files from 0"
# ---------------------------------------------------------------- text domain
# No docker, so these are cheap. Three zeros, three verdicts.
# 🔴 ci_php, never a bare `php`. The Gitea runner image has NO php — that is
# why ci_php exists and says so — and this section originally called `php`
# directly. Two of its four assertions then failed there, and the other two
# PASSED FOR THE WRONG REASON: they expect a non-zero exit, and `php: command
# not found` is a non-zero exit. A green tick for a check that never ran is the
# exact thing this whole script exists to prevent, reproduced inside it.
#
# ci_php falls back to a container whose filesystem comes from `git ls-files`,
# so the fixtures live INSIDE this copy of the repo and are staged. A fixture
# outside the repo, or an unstaged one, is simply not there when it matters.
cd "$work/repo"
echo
echo "→ ci/check-textdomain.php verdicts"
mkdir -p "$work/empty" "$work/nostrings"
printf '<?php\n$x = 1;\n' > "$work/nostrings/plugin.php"
php ci/check-textdomain.php --min=0 "$TEXT_DOMAIN" "$work/empty" >/dev/null 2>&1 \
# Not a literally empty directory: git cannot track one, so it would not reach
# the container at all and the check would fail for a different reason than the
# one being asserted.
mkdir -p selftest-empty selftest-nostrings selftest-dotted/includes
printf 'no php in here\n' > selftest-empty/.keep
printf '<?php\n$x = 1;\n' > selftest-nostrings/plugin.php
printf "<?php\necho __( 'x' );\n" > selftest-dotted/includes/class-db.php
git add -N selftest-empty/.keep selftest-nostrings/plugin.php \
selftest-dotted/includes/class-db.php >/dev/null
ci_php ci/check-textdomain.php --min=0 "$TEXT_DOMAIN" selftest-empty >/dev/null 2>&1 \
&& bad "an empty scan root passed — the root verdict must not be configurable away" \
|| ok "an empty scan root is fatal even with --min=0"
php ci/check-textdomain.php --min=999999 "$TEXT_DOMAIN" . >/dev/null 2>&1 \
ci_php ci/check-textdomain.php --min=999999 "$TEXT_DOMAIN" . >/dev/null 2>&1 \
&& bad "a --min above the real count passed" \
|| ok "a --min above the real count is fatal"
php ci/check-textdomain.php "$TEXT_DOMAIN" "$work/nostrings" >/dev/null 2>&1 \
ci_php ci/check-textdomain.php "$TEXT_DOMAIN" selftest-nostrings >/dev/null 2>&1 \
&& ok "zero calls without --min warns rather than failing" \
|| bad "zero calls without --min failed — a small plugin may legitimately have none"
# The annotated path must be a file that exists. str_replace( $root, '', $path )
# with the default root of "." strips every dot and yields "class-dbphp".
mkdir -p "$work/dotted/includes"
printf "<?php\necho __( 'x' );\n" > "$work/dotted/includes/class-db.php"
ann=$(cd "$work/dotted" && php "$work/repo/ci/check-textdomain.php" "$TEXT_DOMAIN" . 2>&1 | sed -n 's/.*file=\([^,]*\),.*/\1/p' | head -1)
[ -n "$ann" ] && [ -f "$work/dotted/$ann" ] \
# with a root of "." strips every dot and yields "class-dbphp".
ann=$(ci_php ci/check-textdomain.php "$TEXT_DOMAIN" selftest-dotted 2>&1 \
| sed -n 's/.*file=\([^,]*\),.*/\1/p' | head -1)
[ -n "$ann" ] && [ -f "selftest-dotted/$ann" ] \
&& ok "the annotated path exists on disk: $ann" \
|| bad "the annotated path is not a file: '$ann'"
@@ -225,6 +241,46 @@ ci_python_yaml ci/check-workflows.py >/dev/null 2>&1 \
&& bad "check-workflows.py passed having parsed no file at all" \
|| ok "parsing zero workflow files is fatal"
# ------------------------------------------------- no bare interpreter calls
# The bug that cost a green Gitea build: this script called `php` directly.
# The Gitea runner image has no php and no PyYAML, so a bare call there is
# "command not found" — which is a NON-ZERO exit, and therefore indistinguishable
# from the failure two of these assertions were written to expect. They went
# green having run nothing.
#
# ci_php and ci_python_yaml exist for this and say so in their comments. lib.sh
# is excluded because it is where the real calls live.
#
# Measured, so the rule is not uniform: the runner HAS python3 and has NO php,
# and PyYAML is the only missing module. A bare python3 over the stdlib is
# therefore correct and is not flagged.
echo
echo "→ no ci/ script may call php or python3 directly"
# Narrow on purpose. A broad "the word php appears" fires on every string
# literal that mentions php, and on the calls INSIDE the docker scripts in
# static.sh and site-test.sh, where php is exactly right because the container
# is a php image. A check that cries wolf gets commented out, which is worse
# than not having it. These two shapes are what the real bug looked like: the
# interpreter as the first word of a command, or inside $( ).
bare=$(cd "$work/repo" && for f in ci/*.sh; do
[ "$f" = ci/lib.sh ] && continue
sed 's/#.*//' "$f" \
| grep -nE '^[[:space:]]*php[[:space:]]|\$\((cd [^)]*&&[[:space:]]*)?php[[:space:]]' \
| sed "s|^|$f:|"
# python3 itself IS on the Gitea runner; only PyYAML is missing. So a bare
# python3 is fine for anything stdlib (build-zip.sh's zipfile, the JSON
# readers in publish-release.sh) and wrong only when yaml is involved.
sed 's/#.*//' "$f" \
| grep -nE '^[[:space:]]*python3[[:space:]].*(yaml|check-workflows)' \
| sed "s|^|$f:|"
done)
if [ -n "$bare" ]; then
printf '%s\n' "$bare" | sed 's/^/ /'
bad "a ci/ script calls an interpreter directly instead of ci_php / ci_python_yaml"
else
ok "every interpreter call goes through the container-capable wrapper"
fi
echo
if [ "$fail" != 0 ]; then
echo "Self-test: $fail of $((pass + fail)) assertions FAILED."