mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
Git exports repository-local variables (`GIT_DIR`, `GIT_INDEX_FILE`, `GIT_WORK_TREE`, ...) into hook processes, and in linked worktrees `GIT_DIR` is absolute. Pre-push lanes inherited that environment unchanged, so any test fixture that spawns `git` in a temp dir (`git init --bare <tmp>`, `git config user.name ...`, `git commit`, `git push`) actually operated on the checkout being pushed. `current_dir(tmp)` and `git -C tmp` don't escape an inherited `GIT_DIR`. Because `.git/config` is shared by every worktree, one push from any worktree could silently rewrite the repo for all of them. In practice this set `core.bare=true` (breaking the main checkout with `fatal: this operation must be run in a work tree`), replaced the committer identity with a fixture `user.name=Agent`, disabled `commit.gpgSign`, and left fixture aliases behind. The fixtures that did this are the git-identity enforcement tests in #6177, which run in the `rust-tests` lane via `just test-unit`. The pre-push lanes that run test suites or builds now go through `scripts/without-git-env.sh`, which unsets the variables listed by `git rev-parse --local-env-vars` (git's own definition of repo-local state, the same set `git submodule` clears) and then `exec`s the lane, so each subprocess rediscovers the repository from its working directory. Transport, credential, and TLS settings (`GIT_SSH_COMMAND`, `GIT_ASKPASS`, `GIT_TERMINAL_PROMPT`, `GIT_SSL_*`) pass through untouched. Putting this at the hook boundary covers every current and future test without each suite having to defend itself. The `branch-skew` and `push-head-scope` guards run unwrapped: they spawn no fixtures, and `branch-skew`'s `git fetch` needs the caller's transport environment. `pre-commit` isn't wrapped either, since its `stage_fixed: true` lanes need the hook's `GIT_INDEX_FILE` to restage into the temporary index during `git commit <paths>`. Related: #6177 --------- Signed-off-by: Will Pfleger <wpfleger@block.xyz>
131 lines
6.8 KiB
YAML
131 lines
6.8 KiB
YAML
# Glob patterns below mirror the `changes` job's dorny/paths-filter groups in
|
|
# .github/workflows/ci.yml — keep the two in sync. Each globbed pre-push lane
|
|
# also sets `files: git diff --name-only origin/main...HEAD` so its file set is
|
|
# the branch's merge-base diff against origin/main — the same semantics as CI's
|
|
# dorny/paths-filter and `scripts/check-file-sizes-core.mjs` `resolveBaseRef`.
|
|
# Without it lefthook derives files from `git diff HEAD @{push}`, which after a
|
|
# rebase or `git merge origin/main` includes everything main changed since the
|
|
# branch's last push (firing mobile/desktop/rust lanes on unrelated pushes), and
|
|
# on a brand-new branch falls back to a stale remote's tip (a full-suite run).
|
|
# The three-dot diff scopes every lane to what this branch actually changed.
|
|
# Both the three-dot diff and lefthook's stock discovery inspect the checked-out
|
|
# HEAD, so pushing a non-HEAD ref (explicit refspec, `--all`) validates HEAD's
|
|
# tree, not the pushed commit — the always-on `push-head-scope` lane warns when
|
|
# they diverge and CI remains the authoritative gate for that ref.
|
|
# Deliberate deviations:
|
|
# - The `.github/workflows/ci.yml` path CI adds to its `rust`/`mobile` filters
|
|
# is omitted; a CI-workflow-only edit doesn't need a local test run.
|
|
# - `desktop-check`/`desktop-typecheck`/`desktop-test` don't trigger on `rust`
|
|
# changes, though CI's Desktop Core job does. Those commands are pure TS
|
|
# (biome + tsc + node:test) with no Rust dependency, so the extra trigger
|
|
# would be spurious locally.
|
|
# - The repository-wide `file-size-check` is deliberately unfiltered. Its own
|
|
# merge-base diff is the path filter; duplicating its governed roots here is
|
|
# the coverage drift this gate is intended to prevent.
|
|
# - Deletion-only surface changes do not trigger local hooks: lefthook 2.1.x
|
|
# drops deleted paths from push-file discovery (`extractFiles` existence
|
|
# check, repository.go). CI's dorny/paths-filter catches deletions.
|
|
# Deliberate — accepted, not worked around.
|
|
# - `commit-msg` has no glob: it rewrites the commit message, not files. Note
|
|
# Git only runs it for `git commit` and `git merge` — other flows need their own
|
|
# flag: `git rebase --signoff`, `git cherry-pick -s`.
|
|
#
|
|
# `rc` points `lefthook install` at `bin/.lefthookrc`, which the generated
|
|
# `.git/hooks/*` dispatchers source before their `$LEFTHOOK_BIN`-first lookup. It
|
|
# (1) sets `LEFTHOOK_BIN` to the Hermit-pinned `bin/lefthook` (2.1.3) so a push
|
|
# from any worktree runs the pinned binary even when a different lefthook is on
|
|
# PATH, and (2) prepends the repo's Hermit `bin/` to `PATH` so every lane
|
|
# subprocess (`just mobile-check` -> flutter/dart, etc.) resolves the pinned
|
|
# toolchain regardless of the invoking shell — the hook self-pins, so a Homebrew
|
|
# flutter/dart ahead on PATH can no longer cause false lane failures.
|
|
rc: bin/.lefthookrc
|
|
|
|
pre-commit:
|
|
parallel: true
|
|
commands:
|
|
rust-fmt:
|
|
glob: ["crates/**", "examples/countdown-bot/**"]
|
|
run: just fmt
|
|
stage_fixed: true
|
|
desktop-tauri-fmt:
|
|
glob: ["desktop/src-tauri/**"]
|
|
run: just desktop-tauri-fmt
|
|
stage_fixed: true
|
|
desktop-fix:
|
|
# A lockfile-only change has no desktop source for Biome to rewrite.
|
|
glob: ["desktop/**"]
|
|
exclude: ["desktop/src-tauri/**"]
|
|
run: just desktop-fix
|
|
stage_fixed: true
|
|
web-fix:
|
|
# A lockfile-only change has no web source for Biome to rewrite.
|
|
glob: ["web/**"]
|
|
run: just web-fix
|
|
stage_fixed: true
|
|
mobile-fmt:
|
|
glob: ["mobile/**"]
|
|
run: just mobile-fmt
|
|
stage_fixed: true
|
|
|
|
# Appends the DCO Signed-off-by trailer the required "DCO Check" enforces.
|
|
# `--if-exists doNothing` makes it idempotent and preserves an existing
|
|
# sign-off (including `git commit -s` and a different signer's trailer).
|
|
commit-msg:
|
|
commands:
|
|
signoff:
|
|
run: 'git interpret-trailers --if-exists doNothing --trailer "Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed ''s/ [0-9]* [+-][0-9]*$//'')" --in-place {1}'
|
|
|
|
# Lanes below that run test suites or builds go through
|
|
# scripts/without-git-env.sh, which strips git's repository-local variables
|
|
# (per `git rev-parse --local-env-vars`) while preserving transport/credential
|
|
# settings. The branch-skew and push-head-scope guards run no test fixtures
|
|
# and need the caller's transport env, so they run unwrapped.
|
|
pre-push:
|
|
parallel: true
|
|
commands:
|
|
branch-skew:
|
|
run: ./scripts/check-branch-skew.sh
|
|
push-head-scope:
|
|
# Warn-only: the scoped lanes below validate the checked-out HEAD, so a
|
|
# non-HEAD ref push (explicit refspec, --all) relies on CI for its
|
|
# path-scoped checks. This lane reads the pushed refs from stdin and
|
|
# warns when any pushed commit is not HEAD. It never fails the push.
|
|
use_stdin: true
|
|
run: ./scripts/check-push-head-scope.sh
|
|
file-size-check:
|
|
# The ratchet computes its own merge-base diff, so path filtering here
|
|
# would only duplicate policy and create another place for coverage drift.
|
|
run: ./scripts/without-git-env.sh just file-size-check
|
|
rust-tests:
|
|
files: git diff --name-only origin/main...HEAD
|
|
glob: ["crates/**", "migrations/**", "schema/**", "Cargo.toml", "Cargo.lock", "rust-toolchain.toml", "deny.toml", "scripts/run-tests.sh", "Justfile"]
|
|
run: ./scripts/without-git-env.sh just test-unit
|
|
desktop-check:
|
|
files: git diff --name-only origin/main...HEAD
|
|
glob: ["desktop/**", "pnpm-lock.yaml"]
|
|
exclude: ["desktop/src-tauri/**"]
|
|
run: ./scripts/without-git-env.sh just desktop-check
|
|
desktop-typecheck:
|
|
files: git diff --name-only origin/main...HEAD
|
|
glob: ["desktop/**", "pnpm-lock.yaml"]
|
|
exclude: ["desktop/src-tauri/**"]
|
|
run: ./scripts/without-git-env.sh just desktop-typecheck
|
|
desktop-test:
|
|
files: git diff --name-only origin/main...HEAD
|
|
glob: ["desktop/**", "pnpm-lock.yaml"]
|
|
exclude: ["desktop/src-tauri/**"]
|
|
run: ./scripts/without-git-env.sh just desktop-test
|
|
desktop-tauri-checks:
|
|
# Keep local lint parity with Desktop Core CI for every path that can
|
|
# affect the Tauri crate or its path dependencies. Run clippy and tests
|
|
# serially so parallel pre-push hooks do not contend for Cargo's lock.
|
|
files: git diff --name-only origin/main...HEAD
|
|
glob: ["desktop/src-tauri/**", "crates/**", "migrations/**", "schema/**", "Cargo.toml", "Cargo.lock", "rust-toolchain.toml", "deny.toml", "scripts/run-tests.sh", "Justfile"]
|
|
run: ./scripts/without-git-env.sh sh -c 'just desktop-tauri-clippy && just desktop-tauri-test'
|
|
mobile-checks:
|
|
# Run analysis and tests serially so parallel pre-push hooks do not
|
|
# contend for shared Flutter state (.dart_tool, shader bundle).
|
|
files: git diff --name-only origin/main...HEAD
|
|
glob: ["mobile/**"]
|
|
run: ./scripts/without-git-env.sh sh -c 'just mobile-check && just mobile-test'
|