mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
Lefthook selected pre-push lanes from the file set `git diff --name-only
HEAD @{push}` returns, which is overinclusive two different ways:
- **Rebase/merge poisoning.** `@{push}` is a two-dot diff against the
branch's last-pushed tip. After a rebase onto newer main — or the `git
merge origin/main` that `check-branch-skew.sh` itself instructs agents
to run — that diff includes everything `main` changed since the last
push, so `mobile-checks`, the `desktop-*` lanes, and `rust-tests` fire
on branches that never touched those trees (a recent relay-only push
discovered 266 files, 26 of them `mobile/`).
- **Brand-new branch in a worktree.** When `@{push}` doesn't resolve,
lefthook reads `$GIT_DIR/refs/remotes/origin/HEAD` — which doesn't exist
in a linked worktree's git-dir — then falls back to the
alphabetically-first `HEAD ->` remote. In a clone with a stale
third-party remote sorted before `origin`, that diffs against a frozen
tip and runs the full suite. All agent work happens in worktrees.
## Fix
Add `files: git diff --name-only origin/main...HEAD` to the six globbed
pre-push lanes (`rust-tests`, `desktop-check`, `desktop-typecheck`,
`desktop-test`, `desktop-tauri-checks`, `mobile-checks`). The three-dot
form is the branch's merge-base diff against `origin/main` — the same
base resolution `scripts/check-file-sizes-core.mjs` (`resolveBaseRef`)
and CI's dorny/paths-filter already use. A lane now fires only when this
branch actually changed a file it covers; `main`-side changes never
re-trigger it, and a new branch diffs against `origin/main` (empty diff
→ all globbed lanes skip) rather than a stale remote. `branch-skew` and
`file-size-check` stay unfiltered by design.
Also pin hook dispatch to the Hermit-managed lefthook. The generated
`.git/hooks/*` dispatchers prefer `$LEFTHOOK_BIN`, then a `lefthook` on
`PATH` — so a Homebrew lefthook shadowed the pinned 2.1.3. A `rc:
bin/.lefthookrc` (a lefthook config key `lefthook install` bakes into
every dispatcher) exports `LEFTHOOK_BIN` to `bin/lefthook`, so a push
runs the pinned version regardless of `PATH`. `just hooks` already runs
`lefthook install`, so re-running it picks up the `rc` line — no recipe
change needed.
`AGENTS.md` documents that lanes are scoped by merge-base diff and that
agent shells should lead `PATH` with `./bin` so pinned tools win.
## Verification
Reproduced both bugs in a scratch repo mirroring the worktree +
stale-remote topology, with lanes stubbed to echo on selection:
- **Bug 1:** relay-only branch, `git merge origin/main` (main carried
`mobile/` + `desktop/` changes). Without the fix, `desktop-check` and
`mobile-checks` fired; with it, three-dot diff = only the relay file,
all globbed lanes skip.
- **Bug 2:** brand-new branch, no upstream, in a linked worktree with an
alphabetically-first stale remote. Confirmed `@{push}` is `fatal`,
`refs/remotes/origin/HEAD` is absent from the worktree git-dir, and the
stale remote's `HEAD ->` sorts first. Without the fix it fired lanes off
the stale tip; with it, lanes scope to `origin/main` merge-base and
skip.
- **Positive controls:** a `mobile/**` commit still fires
`mobile-checks`; a `crates/**` commit still fires `rust-tests` +
`desktop-tauri-checks`.
- **Dispatch:** with Homebrew lefthook 2.1.10 on `PATH`, running the
real generated `.git/hooks/pre-push` under `LEFTHOOK_VERBOSE=1` shows it
sourcing `bin/.lefthookrc`, exporting `LEFTHOOK_BIN`, and running
`lefthook v2.1.3`.
- This PR's own push (a brand-new branch in a linked worktree — the bug
2 case) ran only `branch-skew` and `file-size-check`; no
mobile/desktop/rust lanes fired, since the branch touches only
`lefthook.yml`, `AGENTS.md`, and `bin/.lefthookrc`.
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
43 lines
1.8 KiB
Bash
Executable File
43 lines
1.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Pre-push scope signal (warn-only, never fails the push).
|
|
#
|
|
# The scoped pre-push lanes derive their file set from `git diff origin/main...HEAD`,
|
|
# and lefthook's own stock discovery is `git diff HEAD @{push}` — both inspect the
|
|
# checked-out HEAD, not the SHAs actually being pushed. Pushing a non-checked-out
|
|
# ref (e.g. `git push origin other-branch`, an explicit refspec, or `--all`) can
|
|
# therefore run the scoped lanes against HEAD while pushing a different commit, so
|
|
# the pushed ref's path-scoped checks are only exercised by CI.
|
|
#
|
|
# This lane reads the pre-push stdin refspecs and prints a loud, non-fatal warning
|
|
# naming each pushed ref whose commit is not the checked-out HEAD. It never blocks
|
|
# the push; CI remains the authoritative gate for a non-HEAD ref.
|
|
#
|
|
# Stdin format (one line per ref, git-provided):
|
|
# <local_ref> <local_sha> <remote_ref> <remote_sha>
|
|
set -euo pipefail
|
|
|
|
head_sha=$(git rev-parse HEAD 2>/dev/null) || exit 0
|
|
|
|
divergent=""
|
|
while read -r local_ref local_sha remote_ref remote_sha; do
|
|
# Empty stdin (up-to-date push with no refs) or malformed line: skip.
|
|
[ -n "${local_sha:-}" ] || continue
|
|
# Deletion push (local sha is all-zeros): nothing built locally to validate.
|
|
case "$local_sha" in *[!0]*) : ;; *) continue ;; esac
|
|
# Pushed commit equals the checked-out HEAD: scoped lanes validated it.
|
|
[ "$local_sha" = "$head_sha" ] && continue
|
|
divergent="${divergent} ${local_ref:-?} -> ${remote_ref:-?} ($(git rev-parse --short "$local_sha" 2>/dev/null || echo "$local_sha"))
|
|
"
|
|
done
|
|
|
|
if [ -n "$divergent" ]; then
|
|
{
|
|
echo "Pushing commits that are not the checked-out HEAD:"
|
|
printf '%s' "$divergent"
|
|
echo "Local scoped lanes validated HEAD only; these refs rely on CI for"
|
|
echo "path-scoped checks. This is a warning, not a failure."
|
|
} >&2
|
|
fi
|
|
|
|
exit 0
|