fix(release): replace declare -A with a bash-3.2-portable workload dedup - #1328
Open
justinjoy wants to merge 1 commit into
Open
fix(release): replace declare -A with a bash-3.2-portable workload dedup#1328justinjoy wants to merge 1 commit into
justinjoy wants to merge 1 commit into
Conversation
`declare -A` is bash 4.0+. run-downstream-matrix.sh is reachable from
scripts/ci/test-downstream-matrix.sh, which meson registers as
downstream_matrix_contract in suite abi with no platform gate, so it runs on the
macOS runners under bash 3.2.57.
No macOS invocation reaches line 202 today: every call the contract test makes
exits at or before archive validation, so that suite is green on 3.2.57 with or
without this change. The hazard is latent. What makes it worth removing rather
than commenting is the failure mode when some future call does reach it: bash
3.2 aborts there with `declare: -A: invalid option` and status 2, which is also
this script's status for bad usage and exactly what test-downstream-matrix.sh:42
asserts. The abort would read as a normal usage exit -- a green contract test
whose oracle loop never ran, on macOS only. Verified by hoisting the declaration
above the argument validation: the probe returned 2 from `declare`, satisfying
the oracle by the wrong cause.
The replacement is a newline-delimited string. The workload field arrives via
IFS=$'\t' read, so it cannot contain a newline; a quoted needle in [[ ]] is
literal in 3.2, so a workload named `a*b` matches literally rather than as a
pattern -- which the associative-array form did not guarantee, since a
non-associative subscript arithmetic-evaluates. Behaviour verified identical on
3.2.57 and 5.3 across 40 adversarial names including prefix/suffix collisions,
glob metacharacters, backslashes and whitespace, and the duplicate guard proven
live against a synthetic oracle carrying a real duplicate.
Also record `bash --version` in the two macOS jobs of ci-pr.yml. meson resolves
the interpreter with find_program('bash') -- PATH, not /bin/bash -- so if the
runner image ever puts a newer bash ahead of it, the 3.2 runtime coverage that
several scripts depend on disappears with nothing to say so.
This closes out #1321 without building the gate it proposed; see the issue for
why, and the successor issue for the narrower form that survives review.
Refs #1321
This was referenced Sep 2, 2026
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.
Closes #1321. Based on
main— independent of every other open PR.This PR does not build the gate #1321 proposed. Implementing that issue's corrected
acceptance criteria falsified its own premise, so the outcome is: close the issue, do the two
things worth doing, and file a narrow successor (#1327).
Why the gate died
#1321's design argument was that scoping a bash-4.0+ denylist to the transitive closure of
meson-run scripts avoids the exemption list a blanket scan would need. Measured:
scripts/**.sh+bench/**.shThe closure — the entire novel part of the proposal — finds the same two lines as the blanket
denylist it was designed to beat, at higher complexity.
And one of those two was an artifact of my own closure implementation:
scripts/upgrade/run-upgrade-matrix.shentered it through a comment atscripts/ci/test-check-shared-library-closure.sh:5. Excluding comment lines: 33 → 30 files,2 → 1 violation. Its real invoker is
release-tag.yml:220onubuntu-latest. It is tier 2under every correct reading and is untouched here.
1.
declare -A→ portable dedupThe remaining violation, and a latent false pass rather than a break.
run-downstream-matrix.shis reachable fromtest-downstream-matrix.sh, registered asdownstream_matrix_contractin suiteabiwith no platform gate — so macOS, bash 3.2.57.No macOS invocation reaches line 202 today; every call the contract test makes exits at or
before archive validation, and the suite is green on 3.2.57 with or without this change.
What makes it worth removing rather than commenting: on 3.2
declare -Aaborts withdeclare: -A: invalid optionand status 2, which is also this script's status for badusage and exactly what
test-downstream-matrix.sh:42asserts. Verified by hoisting thedeclaration above the argument validation — the probe returned 2 from
declare, satisfyingthe oracle by the wrong cause. A green contract test whose oracle loop never ran, macOS
only.
The replacement is a newline-delimited string. The workload field arrives via
IFS=$'\t' readso it cannot contain a newline; a quoted needle in
[[ ]]is literal in 3.2, so a workloadnamed
a*bmatches literally — which the associative form did not guarantee, since anon-associative subscript arithmetic-evaluates.
Verified on real bash 3.2.57 (built from the tarball with all 57 official patches, the
macOS version) and 5.3: identical behaviour across 40 adversarial names including
prefix/suffix collisions (
zxing/zxin/xing), glob metacharacters, backslashes, bracketsand whitespace; duplicate guard proven live against a synthetic oracle carrying a real
duplicate; contract test passing on both shells.
2.
bash --versionin the two macOS jobstests/meson.build:709isfind_program('bash')— PATH, not/bin/bash. If the runner imageever puts a newer bash ahead of it, the 3.2 runtime coverage several scripts depend on
disappears with nothing to say so. Diagnostic only; fails nothing.
Scoped to
ci-pr.ymldeliberately:ci-main.ymlandtier1-sanitizers.ymlhave four moremacOS jobs without it, and one observation point on the per-PR workflow is enough for a line
that fails nothing.
Review
Reviewer and Architect disagreed, and the disagreement improved the outcome.
The Architect ruled "close #1321, don't build it" on the table above. The Reviewer dissented:
the ruling compares file counts, not yield. The meson-direct scope (0 violations) does not
contain
run-downstream-matrix.shat all, and macOS runtime demonstrably did not catch thisdefect — runtime coverage is line-bounded while a static scan is file-bounded.
Then it supplied the fact that settles it against its own dissent: #1320 and #1324, the two
bash-3.2 defects that actually shipped, are
set -ubehaviour differences, not 4.0+constructs. A construct denylist catches neither. Same conclusion as the Architect, by a
route that explains why. #1327 carries both halves so the next person knows what the ratchet
does and does not buy.
It also blocked an earlier candidate on four false claims in my own comments — a wrong line
number, a mechanism described in the present tense that has never occurred, a backwards
attribution of #1320/#1324, and an overstated scope claim. All corrected; the code never
changed.
Local: 304 Ok / 0 Fail / 12 Skipped, serialized, dedicated build dir. Contract test passes
under both 3.2.57 and 5.3.