Skip to content

fix(release): replace declare -A with a bash-3.2-portable workload dedup - #1328

Open
justinjoy wants to merge 1 commit into
mainfrom
feat/1321-bash-portability-gate
Open

fix(release): replace declare -A with a bash-3.2-portable workload dedup#1328
justinjoy wants to merge 1 commit into
mainfrom
feat/1321-bash-portability-gate

Conversation

@justinjoy

Copy link
Copy Markdown
Collaborator

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:

scope files violations
blanket scripts/**.sh + bench/**.sh 53 2
transitive closure 33 2
meson direct invocations only 21 0

The 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.sh entered it through a comment at
scripts/ci/test-check-shared-library-closure.sh:5. Excluding comment lines: 33 → 30 files,
2 → 1 violation. Its real invoker is release-tag.yml:220 on ubuntu-latest. It is tier 2
under every correct reading and is untouched here.

1. declare -A → portable dedup

The remaining violation, and a latent false pass rather than a break.

run-downstream-matrix.sh is reachable from test-downstream-matrix.sh, registered as
downstream_matrix_contract in suite abi with 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 -A aborts 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. Verified by hoisting the
declaration above the argument validation — the probe returned 2 from declare, satisfying
the 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' read
so it cannot contain a newline; a quoted needle in [[ ]] is literal in 3.2, so a workload
named a*b matches literally — which the associative form did not guarantee, since a
non-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, brackets
and whitespace; duplicate guard proven live against a synthetic oracle carrying a real
duplicate; contract test passing on both shells.

2. bash --version in the two macOS jobs

tests/meson.build:709 is find_program('bash') — PATH, not /bin/bash. If the runner image
ever 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.yml deliberately: ci-main.yml and tier1-sanitizers.yml have four more
macOS 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.sh at all, and macOS runtime demonstrably did not catch this
defect — 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 -u behaviour 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.

`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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gate bash 4.0+ constructs in the meson-registered shell scripts

1 participant