fix(lsphp): stop SIGPIPE+pipefail reporting the parity extension as missing
`entrypoint-lsphp.sh` decided whether cac_path_parity was loaded with
printf '%s\n' "$LSPHP_INFO" | grep -q '^cac_path_parity support => enabled$'
under `set -euo pipefail`. `grep -q` exits on its first match; printf is still
writing the remaining ~40 KB of `lsphp -i`, takes SIGPIPE, exits 141, and
pipefail prefers 141 over grep's 0. The branch therefore evaluated FALSE
*because the extension was present* — present early enough to stop the reader —
and every affected container fell back to the auto_prepend normaliser that a
customer's own .user.ini silently displaces, i.e. the exact failure the
extension exists to remove. Measured on whp02 against the published
cac-lsphp:php83: 5/5 runs status=141 with pipefail, 0 without.
The race is decided by pipe capacity, which is why it reproduced on whp02 and
not on other daemons: while the payload fits the pipe the writer never blocks
and always finishes first. Forced over the limit it is deterministic — 3x the
same `lsphp -i` (122100 bytes) gives 141 every time in the built image.
Fixed by reading with here-strings, which are not pipelines at all, so there is
no second exit status for pipefail to adopt. Same grep/awk patterns; plumbing
only. Same class fixed everywhere it existed under pipefail:
* entrypoint-lsphp.sh parity probe, and the SCAN_DIR awk probe
* entrypoint-litespeed.sh SCAN_DIR probe (a bare assignment: 141 there does
not degrade, `set -e` kills PID 1), and ols_running
* entrypoint-shared-ols.sh ols_running
* render-shared-ols-config.sh site.meta parsing (`sed | head -1`): measured
141 at 6000 duplicate keys, which under `set -e`
aborts the whole render
* fpm-parity-check.sh the `php-fpm -m` pre-flight, whose whole job is to
stop a harness fault being blamed on the extension
Also: the fallback used to announce "cac_path_parity extension not loadable in
this image" for every reason the branch was reached, including its own plumbing
breaking — a false diagnosis that sends operators to rebuild a good image whose
build gate passed. Verdicts now carry the evidence they rest on, and a probe
that produced nothing is reported as a probe failure that establishes nothing
about the image. Fail-open posture is unchanged: no probe failure is fatal.
Adds scripts/tests/lsphp-info-probe.test.sh, which runs the shipped probes
(extracted verbatim, so they cannot drift from what runs in production) under
`set -euo pipefail` against a realistic ~40 KB phpinfo body, and statically
outlaws the shape repo-wide. Against trunk it fails, naming all 9 offending
lines. Wired into CI as a new Shell-Checks job, because no existing gate ever
executed the entrypoint's branch logic — the .phpt suite and the Dockerfile's
own `lsphp -i | grep -q` probe (which has no pipefail) were both green for the
release whose entrypoint declared that same extension missing.
Verified: PHP 8.3 --no-cache build green, 10/10 .phpt, 9/9 FPM harness; the
built image logs `path parity = extension` and reports `Rewriting => active`
with .from/.to populated; ext-removed and probe-broken variants each produce
their own honest message and still start.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,56 @@ on:
|
||||
- trunk
|
||||
|
||||
jobs:
|
||||
# Shell gate. Runs FIRST and costs seconds; the images below do not depend on
|
||||
# it (a red job here does not block a push that is otherwise fine), but it is
|
||||
# the only place the ENTRYPOINT logic is executed at all. The .phpt suite and
|
||||
# the Dockerfile's `lsphp -i` probe both test the extension, and both were
|
||||
# green for the release whose entrypoint declared that same extension
|
||||
# missing — see scripts/tests/lsphp-info-probe.test.sh for what went wrong
|
||||
# and why it needed a test outside the image build to catch it.
|
||||
Shell-Checks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Runner images differ on whether they are root and whether sudo exists.
|
||||
- name: Install shellcheck
|
||||
run: |
|
||||
if ! command -v shellcheck >/dev/null 2>&1; then
|
||||
(apt-get update && apt-get install -y shellcheck) ||
|
||||
(sudo apt-get update && sudo apt-get install -y shellcheck)
|
||||
fi
|
||||
shellcheck --version
|
||||
|
||||
- name: Syntax check every shell script
|
||||
run: |
|
||||
set -euo pipefail
|
||||
find scripts ext -name '*.sh' -print0 | xargs -0 -n1 bash -n
|
||||
|
||||
# Deliberately NOT repo-wide. The older scripts (entrypoint.sh,
|
||||
# entrypoint-fpm.sh, create-vhost.sh, create-php-config.sh,
|
||||
# detect-memory.sh) carry pre-existing SC2154/SC2027 findings that predate
|
||||
# this job; listing them here would make the gate red on arrival and
|
||||
# therefore ignored. This is the set that is clean today — the scripts
|
||||
# that run `set -o pipefail` plus the tests. Add files as they are fixed;
|
||||
# do not add one that is not yet clean.
|
||||
- name: shellcheck (warnings and above, on the clean set)
|
||||
run: |
|
||||
shellcheck -S warning \
|
||||
scripts/entrypoint-lsphp.sh \
|
||||
scripts/entrypoint-litespeed.sh \
|
||||
scripts/entrypoint-shared-ols.sh \
|
||||
scripts/render-shared-ols-config.sh \
|
||||
scripts/ols-htaccess-watcher.sh \
|
||||
scripts/create-vhost-litespeed.sh \
|
||||
scripts/install-lscache-wp.sh \
|
||||
scripts/tune-mpm.sh \
|
||||
scripts/tests/lsphp-info-probe.test.sh
|
||||
|
||||
- name: lsphp probe regression test
|
||||
run: ./scripts/tests/lsphp-info-probe.test.sh
|
||||
|
||||
Build-and-Push:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
|
||||
Reference in New Issue
Block a user