fix(bin): make heredoc assignments immune to bash-3.2 quote tracking#269
Open
idrishassan-ai wants to merge 4 commits into
Open
fix(bin): make heredoc assignments immune to bash-3.2 quote tracking#269idrishassan-ai wants to merge 4 commits into
idrishassan-ai wants to merge 4 commits into
Conversation
The three ship-mode Definition-of-done blocks in bin/fm-brief.sh were
assigned via DOD=$(cat <<EOF ... EOF). bash 3.2 (macOS /bin/bash 3.2.57)
tracks quote state through a heredoc body nested inside $( ) while
scanning for the closing paren, so a lone apostrophe anywhere in that
body breaks parsing of the entire script:
$ /bin/bash -n <old construct with "no-mistakes' own guidance">
unexpected EOF while looking for matching `''
syntax error: unexpected end of file
exit=2
PR kunchenguid#173 fixed the immediate failure by rewording the apostrophe away,
but left the fragile construct in place: any future apostrophe in a DOD
body re-breaks every ship brief on macOS, and Linux CI (bash 4+) parses
the construct fine, so `bash -n` there can never catch a regression.
Fix per the brief-wording contract (wording is deliberate; prefer a
structural fix over rewording): assign each DOD with
read -r -d '' DOD <<EOF || true
which bash 3.2 parses regardless of body quoting and which yields the
same value as $(cat ...) for these bodies (default IFS strips the
trailing newline exactly as command substitution does). No brief text
changed.
Evidence, verified on /bin/bash 3.2.57 (arm64-apple-darwin25):
- /bin/bash -n bin/fm-brief.sh passes before and after (already green
at HEAD since kunchenguid#173); with an apostrophe injected into a DOD body, the
old construct fails -n (exit 2, errors above) and the new one passes.
- All five generated briefs are byte-identical before vs after, built
from the same FM_HOME with the HEAD script and the fixed script:
ship no-mistakes, ship direct-PR, ship local-only, --scout, and
--secondmate. diff is empty for each; ship no-mistakes brief sha256
142a8e6620c748fd01c1053cde9cf98b6edb209480336ec8bc5e7c6509a74c68 on
both sides.
- tests/fm-brief.test.sh passes under both `bash` and /bin/bash 3.2.
- Audit of bin/*.sh, bin/backends/*.sh, tests/*.sh: every script passes
/bin/bash -n under 3.2. The remaining $(cat <<EOF) instances
(bin/fm-bootstrap.sh:298,309 and quoted-delimiter fixtures in
tests/fm-crew-state.test.sh) contain no single quotes and parse
cleanly, so they are left untouched.
tests/fm-brief.test.sh grows two guards: `bash -n` now also runs under
/bin/bash explicitly (the affected interpreter on macOS), and a
structural test refuses any non-comment $(cat <<...) in fm-brief.sh so
the trap cannot return silently on Linux CI where -n cannot see it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
macOS
/bin/bashis 3.2.57, whose parser tracks quote state through a heredoc body nested inside$( )command substitution.A lone apostrophe in such a body breaks parsing of the entire script (issue #166):
Modern bash (4+) parses the construct fine, so Linux CI never sees the failure.
PR #173 made
bash -npass by rewording the apostrophe away, but left the fragileVAR=$(cat <<EOF ...)construct in place: any future apostrophe in those bodies re-breaks every ship brief on macOS with no CI tripwire.Fix
bin/fm-brief.sh: assign the three ship-mode Definition-of-done heredocs withread -r -d '' DOD <<EOF ... EOF || trueinstead of$(cat <<EOF ...). bash 3.2 parses this regardless of body quoting. No brief wording changed.bin/fm-bootstrap.sh: convert theshim_bodyandcadence_bodyheredocs the same way (cadence_bodykeeps its quoted no-expansion delimiter). Extends the same hardening so the wholebin/surface is free of the construct.tests/fm-brief.test.sh:bash -nnow also runs explicitly under/bin/bash(the affected 3.2 interpreter on macOS), and a structural test refuses any non-comment$(cat <<acrossbin/*.shandbin/backends/*.sh, so the trap cannot silently return on Linux CI wherebash -ncannot see it.docs: the bash-3.2 heredoc convention is recorded alongside the updated test guard.Evidence (verified on /bin/bash 3.2.57, arm64-apple-darwin25)
-n(exit 2, errors above); the new construct passes.FM_HOME: ship no-mistakes, ship direct-PR, ship local-only,--scout,--secondmate. The ship no-mistakes brief hashes to the same sha256 (142a8e66…) on both sides.bin/,bin/backends/, andtests/passes/bin/bash -nunder 3.2;tests/fm-brief.test.shpasses under bothbash(5.x) and/bin/bash(3.2).🤖 Generated with Claude Code