ci: take bash and tools from the .#ci dev shell; stop policing versions
This follows the user's rulings: CI should not care which bash or which standard utilities it runs, and jobs take what the ci dev shell provides. - checkers.sh: remove find_modern_bash, the 5.1 floor (improvise-fd0j) and exit 3. Suites run under $BASH with PATH unchanged. - checkers-test.sh: remove sections 2, 5 and 5b (the fake bashes and utilbin). They existed only to pin the floor. - scripts/kb: drop the BSD `stat -f` fallback. kb needs the dev shell for tpl, and that shell's stat is GNU. - ci.yml: remove every `shell: bash` override, so every step runs in the dev shell. Remove the web-toolchain version probe and the Playwright version/browser probe. The shell's PLAYWRIGHT_BROWSERS_PATH pins the browsers. npm ci stays because @playwright/test is not nix-provided. - flake.nix: update the comments that promised those CI checks. Reasoning: context/code/tooling.md section 5. Evidence: context/test-evidence/tooling.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
@@ -53,29 +53,6 @@ jobs:
|
||||
# below, so it fails under this job's name rather than another's.
|
||||
- name: The stamp gate refuses what it claims to
|
||||
run: ./scripts/checkers.sh assemble-stamp
|
||||
# scripts/web-toolchain-versions.env stays the one home for the pins, so
|
||||
# the release capture (scripts/build-web-release.sh, improvise-oujb)
|
||||
# provisions the same versions. CI no longer installs them — the dev
|
||||
# shell supplies wasm-pack and node — so this checks the shell against
|
||||
# the pins instead: a nixpkgs bump that moves either fails here, by name.
|
||||
# wasm-pack runs its own bundled `wasm-opt`, which is why
|
||||
# improvise-wasm-client's Cargo.toml names `--enable-bulk-memory`.
|
||||
- name: The dev shell supplies the pinned web toolchain
|
||||
run: |
|
||||
. scripts/web-toolchain-versions.env
|
||||
test "$(wasm-pack --version)" = "wasm-pack ${WASM_PACK_VERSION#v}"
|
||||
test "$(node -p 'process.versions.node.split(".")[0]')" = "$NODE_VERSION"
|
||||
wasm-pack --version
|
||||
node --version
|
||||
# wasm-pack uses the shell's wasm-bindgen only at Cargo.lock's exact
|
||||
# version; otherwise it cargo-installs one (71s) — so a lock bump
|
||||
# must move flake.nix's wasmBindgenCli too, and this says so.
|
||||
lock="$(awk '/^name = "wasm-bindgen"$/ {getline; gsub(/[^0-9.]/, ""); print}' Cargo.lock)"
|
||||
test "$(wasm-bindgen --version)" = "wasm-bindgen $lock" || {
|
||||
echo "flake.nix wasmBindgenCli is $(wasm-bindgen --version); Cargo.lock wants $lock" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Built by Nix (packages.web and packages.site in nix/ci.nix), which runs
|
||||
# the same harness/build.sh and web/assemble.sh a developer runs, so there
|
||||
# is still one answer to "how is the bundle built". `web` is the three
|
||||
@@ -84,14 +61,12 @@ jobs:
|
||||
# the harness tree is stamped `harness`. Dependencies are their own
|
||||
# derivations, substituted from titan's cache when Cargo.lock is unchanged.
|
||||
- name: Build the bundles, the harness tree and the site
|
||||
shell: bash
|
||||
run: |
|
||||
nix build --print-build-logs --out-link result-web .#web
|
||||
nix build --print-build-logs --out-link result-site .#site
|
||||
# The upload steps below read these paths. Copied rather than linked:
|
||||
# an artifact of a symlink into /nix/store is a dangling link elsewhere.
|
||||
- name: Lay the outputs out where the uploads read them
|
||||
shell: bash
|
||||
run: |
|
||||
rm -rf harness/serve web/pkg web/worker-pkg site
|
||||
cp -rL result-web/harness-serve harness/serve
|
||||
@@ -143,12 +118,10 @@ jobs:
|
||||
# checks.clippy and checks.test in nix/ci.nix: the same cargo commands,
|
||||
# on the dev/test profiles, with dependencies as a cached derivation.
|
||||
- name: cargo clippy --workspace --all-targets -- -D warnings
|
||||
shell: bash
|
||||
run: nix build --print-build-logs .#checks.x86_64-linux.clippy
|
||||
# Also runs the test-evidence gate (check-comment-runs.py
|
||||
# --check-evidence), which lists the tests this derivation compiled.
|
||||
- name: cargo test --workspace, and test-evidence headings name real tests
|
||||
shell: bash
|
||||
run: nix build --print-build-logs .#checks.x86_64-linux.test
|
||||
# The comment cap and the test-evidence keys (improvise-9c8c); without
|
||||
# this step the rule is advisory. See context/design-principles.md §6.
|
||||
@@ -174,9 +147,7 @@ jobs:
|
||||
runs-on: [self-hosted, nixos, microvm]
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
# Plain bash, not the dev shell: this step needs nix and nothing else.
|
||||
- name: nix build .#improvise
|
||||
shell: bash
|
||||
run: nix build --print-build-logs .#improvise
|
||||
|
||||
wasm:
|
||||
@@ -190,7 +161,6 @@ jobs:
|
||||
# depends on crossterm, which does not build for wasm — that is correct,
|
||||
# not a gap (improvise-ywd). The log names each crate as it is checked.
|
||||
- name: cargo check --target wasm32-unknown-unknown, per bundle crate
|
||||
shell: bash
|
||||
run: nix build --print-build-logs .#checks.x86_64-linux.wasm-check
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -235,9 +205,9 @@ jobs:
|
||||
# bypass also found that nothing ran this suite.
|
||||
- name: The kb tool refuses what it claims to
|
||||
run: ./scripts/checkers.sh kb
|
||||
# Every suite goes through scripts/checkers.sh, which pins a bash >= 5
|
||||
# and scrubs the environment, so the shell and the variables a suite
|
||||
# passes under here are the ones it passes under locally (improvise-f9qf).
|
||||
# Every suite goes through scripts/checkers.sh, which scrubs the
|
||||
# environment, so the variables a suite passes under here are the ones it
|
||||
# passes under locally (improvise-f9qf). Its bash is the dev shell's.
|
||||
- name: The checker runner refuses what it claims to
|
||||
run: ./scripts/checkers.sh --self-test
|
||||
# pr-watch is driven entirely from fixtures. The release preflight is the
|
||||
@@ -273,7 +243,6 @@ jobs:
|
||||
# checks.harness-lint in nix/ci.nix, run from inside harness/opfs-contract:
|
||||
# `cargo fmt -p opfs-contract -- --check` and clippy for wasm32.
|
||||
- name: cargo fmt -p opfs-contract and clippy --target wasm32
|
||||
shell: bash
|
||||
run: nix build --print-build-logs .#checks.x86_64-linux.harness-lint
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -301,19 +270,9 @@ jobs:
|
||||
- name: Install the harness
|
||||
working-directory: harness
|
||||
run: npm ci
|
||||
# No browser install step: the dev shell points PLAYWRIGHT_BROWSERS_PATH
|
||||
# at nixpkgs' Chromium, Firefox and WebKit, since Playwright's own
|
||||
# downloads cannot run on NixOS. All three, because improvise-xf1's
|
||||
# acceptance criterion names Chrome, Firefox and Safari and
|
||||
# `createSyncAccessHandle` is implemented separately by each engine.
|
||||
# WebKit's OPFS is absent today; the spec skips on a runtime probe.
|
||||
# @playwright/test must equal nixpkgs' playwright-driver version, or it
|
||||
# looks for browser builds that are not there; this says which.
|
||||
- name: Playwright matches the dev shell's browsers
|
||||
working-directory: harness
|
||||
run: |
|
||||
npx playwright --version
|
||||
ls "$PLAYWRIGHT_BROWSERS_PATH"
|
||||
# No browser install step and no version probe: the dev shell sets
|
||||
# PLAYWRIGHT_BROWSERS_PATH to nixpkgs' browsers, which pin them. `npm ci`
|
||||
# stays because @playwright/test itself is not nix-provided.
|
||||
# The tree `build` assembled, not one this job rebuilds. Same bytes as
|
||||
# the Pages artifact, modulo the cache-bust token `web/assemble.sh`
|
||||
# stamps — the harness tree is stamped `harness` and the site the commit
|
||||
|
||||
+30
-15
@@ -660,32 +660,47 @@ sixteen scripts, and the review cost is the whole diff rather than the shebang
|
||||
line. The runner also does not ship zsh, so every job would need an `apt-get`.
|
||||
Declined.
|
||||
|
||||
What was adopted instead pins the *interpreter and the environment together*, in
|
||||
What was adopted instead fixes the *environment* a suite runs in, in
|
||||
one place, with no per-invocation nix cost:
|
||||
|
||||
**`scripts/checkers.sh` is the one entry point for every check_the_checker.** CI
|
||||
calls it and the local gate calls it, so there is no second invocation to drift.
|
||||
It does three things a bare `./scripts/X-test.sh` does not:
|
||||
It runs every suite under the bash that runs it: in CI and under direnv, that
|
||||
is the dev shell's bash. It does two things a bare `./scripts/X-test.sh` does
|
||||
not:
|
||||
|
||||
1. **Pins a bash ≥ 5.1.** The ambient one is tried first, so a runner that
|
||||
already has bash 5.2 pays nothing; nix supplies one only when the ambient
|
||||
bash is older, which on macOS outside the dev shell it is. No bash ≥ 5.1 and
|
||||
no nix is **exit 3** — a refusal, never a quiet run under 3.2. The floor is
|
||||
5.1 and not 5 because `pr-reviews-test.sh` uses `wait -n -p`, and `-p`
|
||||
arrived in 5.1. Under 5.0 it is an invalid option (improvise-fd0j).
|
||||
2. **Scrubs the environment to an allowlist** (`env -i` plus `PATH`, `HOME`,
|
||||
1. **Scrubs the environment to an allowlist** (`env -i` plus `PATH`, `HOME`,
|
||||
`TMPDIR`, the `NIX_*` and `GITHUB_*` names, extensible through
|
||||
`CHECKERS_KEEP`). This is the part that closes the direnv hole above, and it
|
||||
is a *positive* list on purpose: the failure mode of a missing name is a loud
|
||||
"command not found", where the failure mode of an extra name is the silence
|
||||
this section is about.
|
||||
3. **Enforces the shebang rule**, so the `sh`/`bash` split cannot come back.
|
||||
2. **Enforces the shebang rule**, so the `sh`/`bash` split cannot come back.
|
||||
|
||||
The pinned bash's directory goes on the front of `PATH`, so a script the suite
|
||||
invokes through its *own* `#!/usr/bin/env bash` — or through `sh`, which
|
||||
nixpkgs' bash also provides — gets the pinned one too. Pinning only the outer
|
||||
shell would have left every inner one on the system bash, which is where the
|
||||
fault actually lived.
|
||||
**The runner no longer polices the bash version or where tools live.** It used
|
||||
to pin a bash of at least 5.1 (`find_modern_bash`: try the ambient bash, fall
|
||||
back to `nix shell nixpkgs#bash`, and exit 3 if neither was available). The
|
||||
floor was 5.1 because `pr-reviews-test.sh` uses `wait -n -p` (improvise-fd0j).
|
||||
All of that was removed on the user's ruling, when CI moved to the NixOS
|
||||
runners: "we shouldn't care which version of bash and other standard utilities
|
||||
are running in CI", and "most jobs should just take what comes in the ci dev
|
||||
shell".
|
||||
|
||||
So every CI step runs inside `nix develop .#ci`, which provides the bash and the
|
||||
GNU coreutils. The runner calls suites with `$BASH` and an unchanged `PATH`. A
|
||||
script that needs a newer bash feature fails where it uses it, not at a version
|
||||
gate.
|
||||
|
||||
Two things went with the floor:
|
||||
|
||||
- **Its self-tests.** `checkers-test.sh` sections 2, 5 and 5b faked old
|
||||
bashes, or stripped `PATH` down to hand-picked utilities, to prove the floor
|
||||
held. Those were the tests that broke on the NixOS runners, where `/usr/bin`
|
||||
and `/bin` hold only `env` and `sh` (#296, improvise-3uow).
|
||||
- **The hard-coded tool paths** in the suites (`/bin/sleep` and `/bin/date` in
|
||||
`pr-reviews-test.sh`) and `scripts/kb`'s BSD `stat -f` fallback. `scripts/kb`
|
||||
already needs the dev shell for `tpl`, so GNU `stat` is always present when it
|
||||
runs.
|
||||
|
||||
**A suite is `X-test.sh` where `X` or `X.sh` is present beside it.** That rule
|
||||
exists because `scripts/node-test.sh` matches `*-test.sh` and is a test
|
||||
|
||||
@@ -1106,6 +1106,36 @@ clock is a separate script.
|
||||
|
||||
**Scope sweep.** `grep -rnE '/(usr/)?bin/[a-z]'` over `scripts/`, `web/*.sh`,
|
||||
`harness/*.sh`, `harness/tests` and `.github/workflows` finds only `#!/bin/sh`
|
||||
shebangs, and `/bin/sh` exists on NixOS. It also finds the
|
||||
`${PATH:-/usr/bin:/bin}` fallback in `checkers.sh`, which is used only when
|
||||
PATH is unset.
|
||||
shebangs, and `/bin/sh` exists on NixOS. It also found a
|
||||
`${PATH:-/usr/bin:/bin}` fallback in `checkers.sh`, which is now removed (next
|
||||
entry).
|
||||
|
||||
### The bash floor and its self-tests are retired — `scripts/checkers.sh`
|
||||
|
||||
**The ruling.** The user ruled that CI should not care which version of bash or
|
||||
of the standard utilities it runs, and that CI jobs should use whatever the
|
||||
`.#ci` dev shell provides. So `checkers.sh` no longer pins a bash:
|
||||
`find_modern_bash`, the 5.1 floor (improvise-fd0j) and exit 3 are gone. Suites
|
||||
run under `$BASH`, the bash running the runner, with `PATH` unchanged. The
|
||||
reasoning is in `context/code/tooling.md` §5.
|
||||
|
||||
**Tests removed.** Three sections of `checkers-test.sh` were removed. They
|
||||
existed only to pin the floor, and the entries above that cite them describe
|
||||
cases that no longer exist:
|
||||
- Section 2, "a bash-3.2-only construct fails under the runner's pinned bash".
|
||||
It asserted a bash-5 behaviour, so its result depended on the bash version.
|
||||
- Section 5, "an old bash with no nix is exit 3".
|
||||
- Section 5b, "a bash 5.0 with no nix is exit 3, not 5.x" and "a bash 5.1 is
|
||||
accepted", together with the `utilbin` that #296 added for them.
|
||||
|
||||
The suite keeps 10 cases: environment scrubbing (with its direct-run pair), the
|
||||
shebang rule (with its control), fail and pass, unknown suite, and `--list`.
|
||||
|
||||
**Kept deliberately.** The environment allowlist. That is what the
|
||||
`set -u`/`$name` evidence above earns, and it does not depend on any version.
|
||||
|
||||
**Also removed with the ruling.** `scripts/kb`'s `stat -f` fallback: `kb`
|
||||
already requires the dev shell for `tpl`, and the dev shell's `stat` is GNU.
|
||||
From `ci.yml`, the toolchain version probes ("the dev shell supplies the pinned
|
||||
web toolchain", "Playwright matches the dev shell's browsers") and every
|
||||
`shell: bash` override, so every step runs in `nix develop .#ci`.
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
# behind the runners' TLS-auditing proxy those downloads fail (binaryen
|
||||
# with no fallback). On PATH, it downloads nothing. wasm-pack only takes
|
||||
# a PATH wasm-bindgen whose version equals Cargo.lock's `wasm-bindgen`,
|
||||
# so this pin moves with the lock; CI checks that it does.
|
||||
# so this pin moves with the lock: on a mismatch the sandboxed build fails.
|
||||
wasmBindgenCli = pkgs.buildWasmBindgenCli rec {
|
||||
src = pkgs.fetchCrate {
|
||||
pname = "wasm-bindgen-cli";
|
||||
@@ -73,14 +73,13 @@
|
||||
# `web/build.sh` — and so the Pages job's local dry run — needs this.
|
||||
# The `wasm` CI job only runs `cargo check`, which does not, so the
|
||||
# shell was missing the one tool that turns a passing check into a
|
||||
# loadable page (improvise-d31). Must match WASM_PACK_VERSION in
|
||||
# scripts/web-toolchain-versions.env; CI checks that it does.
|
||||
# loadable page (improvise-d31). CI takes whatever version this is.
|
||||
pkgs.wasm-pack
|
||||
wasmBindgenCli
|
||||
pkgs.binaryen
|
||||
|
||||
# NODE_VERSION's major, same check. The js and browser jobs ran 22
|
||||
# while `build` ran the pinned 24; one shell means one node.
|
||||
# The js and browser jobs once ran 22 while `build` ran 24; one shell
|
||||
# means one node.
|
||||
pkgs.nodejs_24
|
||||
pkgs.python3
|
||||
pkgs.jq
|
||||
|
||||
@@ -67,21 +67,7 @@ direct=$?
|
||||
unset name
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. The bash-5 `local` semantics. A multi-assignment `local` does not read
|
||||
# left to right on bash 5, so this suite passes on macOS bash 3.2 and dies
|
||||
# under `set -u` on bash 5 -- PR #133's 19/19 local against 2/19 in CI.
|
||||
d="$(sandbox bash5)"
|
||||
suite "$d" vers <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
f() { local a="$1" b="$a/x"; echo "$b"; }
|
||||
f one
|
||||
EOF
|
||||
expect "a bash-3.2-only construct fails under the runner's pinned bash" \
|
||||
1 "$d/checkers.sh" vers
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. The shebang rule. `#!/bin/sh` is dash in CI and bash-in-sh-mode on macOS.
|
||||
# 2. The shebang rule. `#!/bin/sh` is dash in CI and bash-in-sh-mode on macOS.
|
||||
d="$(sandbox shebang)"
|
||||
suite "$d" fine <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
@@ -101,7 +87,7 @@ rm -f "$d/legacy.sh"
|
||||
expect "and the same sandbox passes once it is removed" 0 "$d/checkers.sh" fine
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. A failing suite is reported, and a passing one is not.
|
||||
# 3. A failing suite is reported, and a passing one is not.
|
||||
d="$(sandbox rc)"
|
||||
suite "$d" red <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
@@ -117,52 +103,7 @@ EOF
|
||||
expect "a passing suite passes the runner" 0 "$d/checkers.sh" blue
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. No bash >= 5 and no nix is exit 3, not a silent run under whatever is
|
||||
# there. This is the arm that would have refused the macOS system bash.
|
||||
d="$(sandbox oldbash)"
|
||||
suite "$d" any <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
exit 0
|
||||
EOF
|
||||
mkdir -p "$work/fakebin"
|
||||
cat > "$work/fakebin/bash" <<'EOF'
|
||||
#!/bin/sh
|
||||
case "$*" in *BASH_VERSINFO*) echo 3; exit 0 ;; esac
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$work/fakebin/bash"
|
||||
# The runner is invoked through a REAL bash, so the fake one below is only
|
||||
# what its own `command -v bash` probe finds -- not its interpreter.
|
||||
# utilbin carries only the utilities the runner itself needs, and no nix, so
|
||||
# the fake bash is the only candidate and the nix fallback is absent. Linked
|
||||
# one by one rather than taking /usr/bin:/bin, which on NixOS hold only `env`
|
||||
# and `sh` -- the runner died on `dirname` there, exit 1 instead of 3. `head`
|
||||
# and `env` are for 5b's 5.1 control, which runs past the version check.
|
||||
mkdir -p "$work/utilbin"
|
||||
for tool in dirname head env; do
|
||||
ln -sf "$(command -v "$tool")" "$work/utilbin/$tool"
|
||||
done
|
||||
env PATH="$work/fakebin:$work/utilbin" HOME="$HOME" \
|
||||
"${BASH:-bash}" "$d/checkers.sh" any > "$work/out" 2>&1
|
||||
got=$?
|
||||
[ "$got" = 3 ] && ok "an old bash with no nix is exit 3" \
|
||||
|| bad "an old bash with no nix is exit 3" "exit $got, wanted 3"
|
||||
|
||||
# 5b. The floor is 5.1, not 5: `wait -n -p` is 5.1 (improvise-fd0j). The 5.1
|
||||
# control proves the 5.0 refusal is the version and not the fake itself.
|
||||
for v in "5 0:3:a bash 5.0 with no nix is exit 3, not 5.x" \
|
||||
"5 1:0:a bash 5.1 is accepted"; do
|
||||
printf '#!/bin/sh\ncase "$*" in *BASH_VERSINFO*) echo %s; exit 0 ;; esac\nexit 0\n' \
|
||||
"${v%%:*}" > "$work/fakebin/bash"
|
||||
want="${v#*:}"; name="${want#*:}"; want="${want%%:*}"
|
||||
env PATH="$work/fakebin:$work/utilbin" HOME="$HOME" \
|
||||
"${BASH:-bash}" "$d/checkers.sh" any > "$work/out" 2>&1
|
||||
got=$?
|
||||
[ "$got" = "$want" ] && ok "$name" || bad "$name" "exit $got, wanted $want"
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. A named suite that does not exist is a usage error, not a silent no-op.
|
||||
# 4. A named suite that does not exist is a usage error, not a silent no-op.
|
||||
d="$(sandbox missing)"
|
||||
suite "$d" present <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
@@ -171,7 +112,7 @@ EOF
|
||||
expect "an unknown suite name is exit 4" 4 "$d/checkers.sh" absent
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. --list names every suite in the directory and nothing else.
|
||||
# 5. --list names every suite in the directory and nothing else.
|
||||
d="$(sandbox listing)"
|
||||
suite "$d" alpha <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
|
||||
+10
-39
@@ -3,15 +3,14 @@
|
||||
# and so does the local gate, so a suite cannot be green under one shell and
|
||||
# red under the other. Reasoning: context/code/tooling.md section 3.
|
||||
#
|
||||
# It does three things a bare `./scripts/X-test.sh` does not:
|
||||
# It does two things a bare `./scripts/X-test.sh` does not. It runs every
|
||||
# suite under the bash running it (the dev shell's), and it:
|
||||
#
|
||||
# 1. pins a bash >= 5.1, taken from nix when the ambient one is older, so the
|
||||
# macOS system bash 3.2 can never be the shell a suite passes under;
|
||||
# 2. scrubs the environment down to an allowlist, because `nix develop`
|
||||
# 1. scrubs the environment down to an allowlist, because `nix develop`
|
||||
# exports `name`, `out`, `system`, `shell`, `builder` and two dozen more
|
||||
# generically-named variables that CI does not have -- a `set -u` fault
|
||||
# reading one of them is SILENT inside the dev shell and fatal in CI;
|
||||
# 3. refuses a shell script in scripts/ whose shebang is not
|
||||
# 2. refuses a shell script in scripts/ whose shebang is not
|
||||
# `#!/usr/bin/env bash`, so the sh/bash split cannot come back.
|
||||
set -euo pipefail
|
||||
|
||||
@@ -30,7 +29,6 @@ usage: checkers.sh [--list] [--self-test] [name ...]
|
||||
|
||||
exit 0 every suite passed
|
||||
exit 1 a suite failed, or a shebang is not `#!/usr/bin/env bash`
|
||||
exit 3 no bash >= 5.1 could be found and nix could not supply one
|
||||
exit 4 usage error
|
||||
|
||||
THIS SCRIPT'S `$?` MUST BE THE ONE YOU READ. Nothing may follow it in the same
|
||||
@@ -46,22 +44,6 @@ ALLOW="HOME TMPDIR TMP TEMP TERM LANG LC_ALL USER LOGNAME
|
||||
NIX_PATH NIX_REMOTE NIX_SSL_CERT_FILE SSL_CERT_FILE
|
||||
XDG_CACHE_HOME XDG_CONFIG_HOME XDG_DATA_HOME"
|
||||
|
||||
# Prints the absolute path of a bash >= 5.1, or nothing. The ambient one is tried
|
||||
# first so a machine that already has a modern bash pays no nix startup at all.
|
||||
find_modern_bash() {
|
||||
local candidate major minor
|
||||
candidate="$(command -v bash || true)"
|
||||
if [ -n "$candidate" ]; then
|
||||
read -r major minor < <("$candidate" -c 'echo "${BASH_VERSINFO[0]} ${BASH_VERSINFO[1]}"' \
|
||||
2>/dev/null || echo 0 0)
|
||||
if [ "$(( ${major:-0} * 100 + ${minor:-0} ))" -ge 501 ] 2>/dev/null; then
|
||||
echo "$candidate"; return 0
|
||||
fi
|
||||
fi
|
||||
command -v nix >/dev/null 2>&1 || return 0
|
||||
nix shell nixpkgs#bash --command bash -c 'command -v bash' 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Every shell script in scripts/ says `#!/usr/bin/env bash` and nothing else.
|
||||
# `#!/bin/sh` is dash in CI and bash 3.2 in sh-mode on macOS: a second platform
|
||||
# split, undetected for the same reason the first one was.
|
||||
@@ -94,19 +76,16 @@ suites() {
|
||||
done
|
||||
}
|
||||
|
||||
# $1 the pinned bash, $2 the suite file. The suite runs with the pinned bash
|
||||
# FIRST on PATH, so a script it invokes through its own `#!/usr/bin/env bash`
|
||||
# -- or through `sh` -- gets the pinned one too, not the system 3.2.
|
||||
# $1 the suite file, run by the bash running this script, with PATH unchanged.
|
||||
run_suite() {
|
||||
local pinned="$1" file="$2"
|
||||
local bindir; bindir="$(dirname "$pinned")"
|
||||
local file="$1"
|
||||
local -a env_args=(-i)
|
||||
local v
|
||||
for v in $ALLOW ${CHECKERS_KEEP:-}; do
|
||||
if [ -n "${!v+set}" ]; then env_args+=("$v=${!v}"); fi
|
||||
done
|
||||
env_args+=("PATH=$bindir:${PATH:-/usr/bin:/bin}")
|
||||
env "${env_args[@]}" "$pinned" "$file"
|
||||
env_args+=("PATH=$PATH")
|
||||
env "${env_args[@]}" "$BASH" "$file"
|
||||
}
|
||||
|
||||
main() {
|
||||
@@ -123,15 +102,7 @@ main() {
|
||||
shift
|
||||
done
|
||||
|
||||
local pinned; pinned="$(find_modern_bash)"
|
||||
if [ -z "$pinned" ]; then
|
||||
echo 'checkers: no bash >= 5.1 on PATH and nix cannot supply one.' >&2
|
||||
echo ' Enter the dev shell (direnv allow / nix develop) and retry.' >&2
|
||||
return 3
|
||||
fi
|
||||
printf 'checkers: bash %s at %s\n' \
|
||||
"$("$pinned" -c 'echo "${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}"')" "$pinned"
|
||||
|
||||
printf 'checkers: bash %s at %s\n' "$BASH_VERSION" "$BASH"
|
||||
local rc=0
|
||||
check_shebangs || rc=1
|
||||
|
||||
@@ -149,7 +120,7 @@ main() {
|
||||
rc=4; continue
|
||||
fi
|
||||
printf '\n=== %s ===\n' "$n"
|
||||
run_suite "$pinned" "$file" || rc=1
|
||||
run_suite "$file" || rc=1
|
||||
done
|
||||
return $rc
|
||||
}
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ ledger_path() {
|
||||
# first line; bytes alone are not enough, since a restore can reproduce them.
|
||||
file_stamp() {
|
||||
printf '%s %s' "$(stamp_of "$1")" \
|
||||
"$(stat -c '%i %.9Y' "$1" 2>/dev/null || stat -f '%i %Fm' "$1")"
|
||||
"$(stat -c '%i %.9Y' "$1")"
|
||||
}
|
||||
ledger_holds() { [ -f "$1" ] && [ "$(head -n 1 "$1")" = "% kb-stamp $(file_stamp "$2")" ]; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user