chore(gate): shellcheck joins the gate — every tracked shell script, the host binary held to a pin
This commit is contained in:
@@ -12,7 +12,7 @@ router-free navigation provider.
|
|||||||
|
|
||||||
## The gate — never merge or ship red
|
## The gate — never merge or ship red
|
||||||
|
|
||||||
The local gate is **ruff (`check` + `format --check`) + basedpyright (backend) + the lock check + typecheck (3 tsc projects) +
|
The local gate is **ruff (`check` + `format --check`) + shellcheck + basedpyright (backend) + the lock check + typecheck (3 tsc projects) +
|
||||||
eslint + prettier --check + knip + vitest + vite build + pytest + pip-audit + npm audit**. It runs
|
eslint + prettier --check + knip + vitest + vite build + pytest + pip-audit + npm audit**. It runs
|
||||||
before every `release` merges and on every `publish`, which **refuses to build on failure — there is deliberately no `--skip`
|
before every `release` merges and on every `publish`, which **refuses to build on failure — there is deliberately no `--skip`
|
||||||
flag**; a release also requires the Playwright suite, and the image must pass a Trivy scan before it is pushed. Run it and get it
|
flag**; a release also requires the Playwright suite, and the image must pass a Trivy scan before it is pushed. Run it and get it
|
||||||
|
|||||||
@@ -87,6 +87,13 @@ docker compose -f backend/docker-compose.test.yml run --rm --no-deps test \
|
|||||||
pip-audit -r requirements.lock --no-deps --disable-pip
|
pip-audit -r requirements.lock --no-deps --disable-pip
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Shell scripts**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# every tracked *.sh at shellcheck's default bar (info included); the version is pinned in scripts/check.sh
|
||||||
|
git ls-files -z -- '*.sh' | xargs -0 shellcheck
|
||||||
|
```
|
||||||
|
|
||||||
A missing database or ffmpeg is a hard error, not a skip — so the gate can't print green while the
|
A missing database or ffmpeg is a hard error, not a skip — so the gate can't print green while the
|
||||||
DB-backed tests were quietly not run (`SIFTLODE_TEST_REQUIRE_DB=0` is the explicit "pure-logic only"
|
DB-backed tests were quietly not run (`SIFTLODE_TEST_REQUIRE_DB=0` is the explicit "pure-logic only"
|
||||||
run for a machine without Postgres). Tests that need a database take the `db` fixture
|
run for a machine without Postgres). Tests that need a database take the `db` fixture
|
||||||
|
|||||||
@@ -1377,6 +1377,16 @@
|
|||||||
"}",
|
"}",
|
||||||
"ruff_check() { ruff_pinned && python -m ruff check --config backend/ruff.toml backend scripts; }",
|
"ruff_check() { ruff_pinned && python -m ruff check --config backend/ruff.toml backend scripts; }",
|
||||||
"ruff_format() { ruff_pinned && python -m ruff format --check --config backend/ruff.toml backend scripts; }",
|
"ruff_format() { ruff_pinned && python -m ruff format --check --config backend/ruff.toml backend scripts; }",
|
||||||
|
"SHELLCHECK_PIN=0.11.0",
|
||||||
|
"shellcheck_lane() {",
|
||||||
|
" local host",
|
||||||
|
" host=\"$(shellcheck --version 2>/dev/null | sed -n 's/^version: //p')\"",
|
||||||
|
" if [ \"$host\" != \"$SHELLCHECK_PIN\" ]; then",
|
||||||
|
" echo \"shellcheck ${host:-missing} on the host, $SHELLCHECK_PIN pinned — https://github.com/koalaman/shellcheck/releases\" >&2",
|
||||||
|
" return 1",
|
||||||
|
" fi",
|
||||||
|
" git ls-files -z -- '*.sh' | xargs -0 shellcheck",
|
||||||
|
"}",
|
||||||
"basedpyright() {",
|
"basedpyright() {",
|
||||||
" local before",
|
" local before",
|
||||||
" before=\"$(git hash-object backend/.basedpyright/baseline.json)\"",
|
" before=\"$(git hash-object backend/.basedpyright/baseline.json)\"",
|
||||||
@@ -1394,6 +1404,7 @@
|
|||||||
"gate_t0=\"$(ms)\"",
|
"gate_t0=\"$(ms)\"",
|
||||||
"lane \"ruff\" ruff_check",
|
"lane \"ruff\" ruff_check",
|
||||||
"lane \"ruff format --check\" ruff_format",
|
"lane \"ruff format --check\" ruff_format",
|
||||||
|
"lane \"shellcheck (every shell script)\" shellcheck_lane",
|
||||||
"lane \"basedpyright (new errors only)\" basedpyright",
|
"lane \"basedpyright (new errors only)\" basedpyright",
|
||||||
"lane \"lock check (frontend/node_modules == package-lock.json)\" node scripts/check-node-modules.mjs",
|
"lane \"lock check (frontend/node_modules == package-lock.json)\" node scripts/check-node-modules.mjs",
|
||||||
"lane \"tsc (src + configs + e2e)\" frontend npm run --silent typecheck",
|
"lane \"tsc (src + configs + e2e)\" frontend npm run --silent typecheck",
|
||||||
@@ -1405,7 +1416,7 @@
|
|||||||
" local rc=0",
|
" local rc=0",
|
||||||
" docker compose -f \"$TESTCOMPOSE\" up --build --abort-on-container-exit --exit-code-from test || rc=1",
|
" docker compose -f \"$TESTCOMPOSE\" up --build --abort-on-container-exit --exit-code-from test || rc=1",
|
||||||
" docker compose -f \"$TESTCOMPOSE\" down -v >/dev/null 2>&1",
|
" docker compose -f \"$TESTCOMPOSE\" down -v >/dev/null 2>&1",
|
||||||
" return $rc",
|
" return \"$rc\"",
|
||||||
"}",
|
"}",
|
||||||
"pip_audit() {",
|
"pip_audit() {",
|
||||||
" docker compose -f \"$TESTCOMPOSE\" run --rm --no-deps test \\",
|
" docker compose -f \"$TESTCOMPOSE\" run --rm --no-deps test \\",
|
||||||
|
|||||||
+17
-1
@@ -20,6 +20,7 @@
|
|||||||
# stops at the first failure hides the second until the first is fixed. The backend lanes run in the
|
# stops at the first failure hides the second until the first is fixed. The backend lanes run in the
|
||||||
# test image (backend/Dockerfile.test): requirements.lock will not install on Windows (uvloop), and a
|
# test image (backend/Dockerfile.test): requirements.lock will not install on Windows (uvloop), and a
|
||||||
# type checker or auditor without the real packages reports nonsense.
|
# type checker or auditor without the real packages reports nonsense.
|
||||||
|
# shellcheck disable=SC2329 # the lane functions below are called by name, through lane()
|
||||||
set -u
|
set -u
|
||||||
cd "$(dirname "$0")/.." || exit 2
|
cd "$(dirname "$0")/.." || exit 2
|
||||||
mode=all
|
mode=all
|
||||||
@@ -121,6 +122,20 @@ ruff_pinned() {
|
|||||||
ruff_check() { ruff_pinned && python -m ruff check --config backend/ruff.toml backend scripts; }
|
ruff_check() { ruff_pinned && python -m ruff check --config backend/ruff.toml backend scripts; }
|
||||||
ruff_format() { ruff_pinned && python -m ruff format --check --config backend/ruff.toml backend scripts; }
|
ruff_format() { ruff_pinned && python -m ruff format --check --config backend/ruff.toml backend scripts; }
|
||||||
|
|
||||||
|
# Every tracked shell script (the gate itself, backup/restore, the installer, the image's entrypoint) at
|
||||||
|
# the linter's full default bar, info included. Host shellcheck, held to this pin like ruff: a newer one
|
||||||
|
# adds checks, and a gate must not change under a green commit.
|
||||||
|
SHELLCHECK_PIN=0.11.0
|
||||||
|
shellcheck_lane() {
|
||||||
|
local host
|
||||||
|
host="$(shellcheck --version 2>/dev/null | sed -n 's/^version: //p')"
|
||||||
|
if [ "$host" != "$SHELLCHECK_PIN" ]; then
|
||||||
|
echo "shellcheck ${host:-missing} on the host, $SHELLCHECK_PIN pinned — https://github.com/koalaman/shellcheck/releases" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
git ls-files -z -- '*.sh' | xargs -0 shellcheck
|
||||||
|
}
|
||||||
|
|
||||||
# New errors only: the pre-existing ones are in backend/.basedpyright/baseline.json. basedpyright
|
# New errors only: the pre-existing ones are in backend/.basedpyright/baseline.json. basedpyright
|
||||||
# TIGHTENS that file by itself when errors go, and the tree is bind-mounted read-write, so a green run
|
# TIGHTENS that file by itself when errors go, and the tree is bind-mounted read-write, so a green run
|
||||||
# can leave it rewritten: that fails here until it is committed, or the next run's stack reads stale.
|
# can leave it rewritten: that fails here until it is committed, or the next run's stack reads stale.
|
||||||
@@ -143,6 +158,7 @@ frontend() { (cd frontend && "$@"); }
|
|||||||
gate_t0="$(ms)"
|
gate_t0="$(ms)"
|
||||||
lane "ruff" ruff_check
|
lane "ruff" ruff_check
|
||||||
lane "ruff format --check" ruff_format
|
lane "ruff format --check" ruff_format
|
||||||
|
lane "shellcheck (every shell script)" shellcheck_lane
|
||||||
lane "basedpyright (new errors only)" basedpyright
|
lane "basedpyright (new errors only)" basedpyright
|
||||||
# every frontend lane judges the HOST node_modules: this proves it is the tree the lock pins
|
# every frontend lane judges the HOST node_modules: this proves it is the tree the lock pins
|
||||||
lane "lock check (frontend/node_modules == package-lock.json)" node scripts/check-node-modules.mjs
|
lane "lock check (frontend/node_modules == package-lock.json)" node scripts/check-node-modules.mjs
|
||||||
@@ -158,7 +174,7 @@ pytest_lane() { # the full suite against a throwaway Postgres; --pull keeps the
|
|||||||
local rc=0
|
local rc=0
|
||||||
docker compose -f "$TESTCOMPOSE" up --build --abort-on-container-exit --exit-code-from test || rc=1
|
docker compose -f "$TESTCOMPOSE" up --build --abort-on-container-exit --exit-code-from test || rc=1
|
||||||
docker compose -f "$TESTCOMPOSE" down -v >/dev/null 2>&1
|
docker compose -f "$TESTCOMPOSE" down -v >/dev/null 2>&1
|
||||||
return $rc
|
return "$rc"
|
||||||
}
|
}
|
||||||
# Every pinned backend package against the advisory databases, at any severity — the lock is a full
|
# Every pinned backend package against the advisory databases, at any severity — the lock is a full
|
||||||
# `pip freeze`, so it is audited as written, without a resolver. An advisory with no fix is answered
|
# `pip freeze`, so it is audited as written, without a resolver. An advisory with no fix is answered
|
||||||
|
|||||||
Reference in New Issue
Block a user