The local gate checked ktlint, detekt and Android lint but not shellcheck — so a new or edited .sh file was precisely the case where the hook passed and CI's Static analysis leg still went red.
That is not hypothetical. The gate is itself a new .sh file, and the first thing it could not check was itself. It got caught by hand twice on #256 before it got caught here.
The digest is read out of status_check.yml, not copied
shellcheck 0.9.0 and 0.11.0 disagree about how to report a trap handler — SC2317 on seven body lines against SC2329 once on the declaration. Same script, same directive, one red and one green. That disagreement is why CI pins by digest in the first place.
A second copy of that digest in the hook would be worse than none: when it drifts, the symptom is the gate passing while CI fails, which is the exact failure this section exists to prevent. So there is one digest in the repo and the hook reads it:
shellcheck_pin="$(grep -oE 'koalaman/shellcheck@sha256:[0-9a-f]{64}'\
.github/workflows/status_check.yml | head -1)"
Details
Runs over git ls-files '*.sh' — all tracked files, not the diff — because that is what CI does. The job is to predict that leg, not to audit the change.
podman preferred over docker for the mount's SELinux relabel (:z), which is what this host needs; docker on CI does without it.
Neither runtime present, or the digest unreadable → reported as NOT COVERED, loudly, rather than skipped quietly. A check that silently does not run is worse than one that is absent.
Verified that it bites
A probe script whose only fault was an unquoted ls $foo was staged, and the real pre-commit hook blocked on it:
[local-gate] BLOCKED shellcheck failed. CI runs the same digest over the same files,
so this is a red Static analysis leg waiting to happen.
It blocked before reaching the JVM gate. Probe removed; all tracked .sh are clean under the pinned digest.
Cost
This commit touches no app/src, so both the pre-commit and pre-push runs skipped the emulator sweep and finished in seconds — the app/src-subtree cache key doing its job. shellcheck itself adds a couple of seconds.
The local gate checked ktlint, detekt and Android lint but **not** shellcheck — so a new or edited `.sh` file was precisely the case where the hook passed and CI's Static analysis leg still went red.
That is not hypothetical. The gate is itself a new `.sh` file, and **the first thing it could not check was itself**. It got caught by hand twice on #256 before it got caught here.
## The digest is read out of `status_check.yml`, not copied
shellcheck 0.9.0 and 0.11.0 disagree about how to report a trap handler — `SC2317` on seven body lines against `SC2329` once on the declaration. Same script, same directive, one red and one green. That disagreement is why CI pins by digest in the first place.
A second copy of that digest in the hook would be worse than none: when it drifts, the symptom is **the gate passing while CI fails**, which is the exact failure this section exists to prevent. So there is one digest in the repo and the hook reads it:
```bash
shellcheck_pin="$(grep -oE 'koalaman/shellcheck@sha256:[0-9a-f]{64}' \
.github/workflows/status_check.yml | head -1)"
```
## Details
- Runs over **`git ls-files '*.sh'`** — all tracked files, not the diff — because that is what CI does. The job is to predict that leg, not to audit the change.
- **podman preferred over docker** for the mount's SELinux relabel (`:z`), which is what this host needs; docker on CI does without it.
- Neither runtime present, or the digest unreadable → reported as **NOT COVERED**, loudly, rather than skipped quietly. A check that silently does not run is worse than one that is absent.
## Verified that it bites
A probe script whose only fault was an unquoted `ls $foo` was staged, and the **real pre-commit hook** blocked on it:
```
[local-gate] BLOCKED shellcheck failed. CI runs the same digest over the same files,
so this is a red Static analysis leg waiting to happen.
```
It blocked *before* reaching the JVM gate. Probe removed; all tracked `.sh` are clean under the pinned digest.
## Cost
This commit touches no `app/src`, so both the pre-commit and pre-push runs skipped the emulator sweep and finished in seconds — the `app/src`-subtree cache key doing its job. shellcheck itself adds a couple of seconds.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The local gate checked ktlint, detekt and Android lint but not shellcheck — so a new or edited
.shfile was precisely the case where the hook passed and CI's Static analysis leg still went red.That is not hypothetical. The gate is itself a new
.shfile, and the first thing it could not check was itself. It got caught by hand twice on #256 before it got caught here.The digest is read out of
status_check.yml, not copiedshellcheck 0.9.0 and 0.11.0 disagree about how to report a trap handler —
SC2317on seven body lines againstSC2329once on the declaration. Same script, same directive, one red and one green. That disagreement is why CI pins by digest in the first place.A second copy of that digest in the hook would be worse than none: when it drifts, the symptom is the gate passing while CI fails, which is the exact failure this section exists to prevent. So there is one digest in the repo and the hook reads it:
Details
git ls-files '*.sh'— all tracked files, not the diff — because that is what CI does. The job is to predict that leg, not to audit the change.:z), which is what this host needs; docker on CI does without it.Verified that it bites
A probe script whose only fault was an unquoted
ls $foowas staged, and the real pre-commit hook blocked on it:It blocked before reaching the JVM gate. Probe removed; all tracked
.share clean under the pinned digest.Cost
This commit touches no
app/src, so both the pre-commit and pre-push runs skipped the emulator sweep and finished in seconds — theapp/src-subtree cache key doing its job. shellcheck itself adds a couple of seconds.