From 754add31e2c3def6c93683e3fda0b819cd7907b8 Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Wed, 23 Sep 2026 23:32:54 -0700 Subject: [PATCH] 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) Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze --- .github/workflows/ci.yml | 53 +++---------------------- context/code/tooling.md | 45 ++++++++++++++------- context/test-evidence/tooling.md | 36 +++++++++++++++-- flake.nix | 9 ++--- scripts/checkers-test.sh | 67 ++------------------------------ scripts/checkers.sh | 49 +++++------------------ scripts/kb | 2 +- 7 files changed, 88 insertions(+), 173 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fdaca1d..e4d51add 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/context/code/tooling.md b/context/code/tooling.md index bed33ec1..0edc9366 100644 --- a/context/code/tooling.md +++ b/context/code/tooling.md @@ -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 diff --git a/context/test-evidence/tooling.md b/context/test-evidence/tooling.md index 5cffd844..23328d4e 100644 --- a/context/test-evidence/tooling.md +++ b/context/test-evidence/tooling.md @@ -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`. diff --git a/flake.nix b/flake.nix index 7f06ffe0..47dfc078 100644 --- a/flake.nix +++ b/flake.nix @@ -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 diff --git a/scripts/checkers-test.sh b/scripts/checkers-test.sh index 67a9b846..564ccacf 100755 --- a/scripts/checkers-test.sh +++ b/scripts/checkers-test.sh @@ -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 diff --git a/scripts/checkers.sh b/scripts/checkers.sh index 7cf099d7..e98e9e1e 100755 --- a/scripts/checkers.sh +++ b/scripts/checkers.sh @@ -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 } diff --git a/scripts/kb b/scripts/kb index f4465e6f..dcc571ff 100755 --- a/scripts/kb +++ b/scripts/kb @@ -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")" ]; }