max / alloy
| 1 | #!/bin/bash |
| 2 | # Canonical pre-push gate. Two gates, and they answer different questions: |
| 3 | # |
| 4 | # internal deps does every in-house `version` requirement in the tree still |
| 5 | # resolve against the crate it names? Runs in EVERY repo. |
| 6 | # test targets do this repo's test targets build? Runs where there is a |
| 7 | # root Cargo.toml to run one command in. |
| 8 | # |
| 9 | # DO NOT EDIT IN PLACE. The master is _private/infra/bootstrap/githooks/pre-push. |
| 10 | # |
| 11 | # Bypass for a work-in-progress push: git push --no-verify |
| 12 | # |
| 13 | # WHY THE FIRST GATE EXISTS. Every cross-repo dependency carries a `version` |
| 14 | # alongside its `git` URL, so cargo refuses a sibling it was not written against |
| 15 | # instead of compiling something surprising. That requirement is the protection |
| 16 | # and it is also the maintenance: bumping a library's minor breaks every consumer |
| 17 | # whose requirement excludes it, and CLAUDE.md's rule is that the bump and the |
| 18 | # forward fix are one pass. Nothing enforced the rule, so quasi went 0.11 -> 0.14 |
| 19 | # over two evenings and MNW's server could not resolve at all for a day. The |
| 20 | # nightly sweep found it and a red cell in a grid is not the same as being told. |
| 21 | # |
| 22 | # This gate is that rule, mechanised, at the moment it is broken: the push that |
| 23 | # would leave a consumer unable to build is the push that is refused. It reads |
| 24 | # the WORKING COPIES in the tree, not the remotes, because `~/Code/.cargo/config.toml` |
| 25 | # redirects every one of these dependencies to the working copy -- so a local bump |
| 26 | # breaks a consumer's build here whether or not it has been pushed anywhere. |
| 27 | # |
| 28 | # It also grades this repo's manifests AS THEY EXIST AT THE PUSHED COMMIT, and |
| 29 | # fails on either view. Reading the working copy alone means an uncommitted |
| 30 | # forward-fix is graded instead of the text git is publishing: on 2026-08-24 that |
| 31 | # printed "internal deps coherent" over an mnw-cli requirement that had been |
| 32 | # advanced on disk and never committed, and Sando failed to resolve it minutes |
| 33 | # later. Both views are kept because they catch different things -- the commit |
| 34 | # view cannot see a local bump that breaks every build on this machine. |
| 35 | # |
| 36 | # `cargo check` and `cargo clippy` both compile only the lib and bin targets, so a |
| 37 | # break confined to `tests/` or a `#[cfg(test)]` module is clean under both and |
| 38 | # lands unnoticed (goingson's sqlx 0.9 upgrade shipped exactly that way). |
| 39 | # `--no-run` builds every test target without running them, which is the cheap |
| 40 | # half of the suite and enough to catch a compile break. Tests still run |
| 41 | # separately. |
| 42 | # |
| 43 | # `--workspace` is load-bearing wherever default-members is narrower than the |
| 44 | # workspace: goingson's is src-tauri alone, so a bare `cargo test --no-run` would |
| 45 | # skip core, db-sqlite, go-mcp and got. |
| 46 | |
| 47 | |
| 48 | ROOT="" |
| 49 | |
| 50 | |
| 51 | # See the canonical pre-commit: a hook run from an editor or a cron job does not |
| 52 | # get the profile's PATH, and a hook that cannot find cargo is worse than none. |
| 53 | |
| 54 | |
| 55 | # Refs arrive on stdin as "<local ref> <local sha> <remote ref> <remote sha>". |
| 56 | # A branch deletion has an all-zero local sha and no tree to push. Read once, |
| 57 | # ahead of both gates: stdin is not seekable and a second reader gets nothing. |
| 58 | # |
| 59 | # The sha is KEPT, not just tested. Gate 1 grades the manifests at that commit as |
| 60 | # well as the ones on disk, because they are not always the same text and the |
| 61 | # difference is invisible in the good case. Last sha wins on a multi-ref push: |
| 62 | # the gate wants a commit whose tree it can read, and grading one of them beats |
| 63 | # grading none. A deletion contributes an all-zero sha and is skipped. |
| 64 | pushing=0 |
| 65 | pushed_sha="" |
| 66 | while ; do |
| 67 | case "" in |
| 68 | *[!0]*) pushing=1; pushed_sha="" ;; |
| 69 | esac |
| 70 | done |
| 71 | [ || |
| 72 | |
| 73 | # ── gate 1: internal dependency coherence ────────────────────────────────── |
| 74 | # |
| 75 | # Deliberately no cargo: this reads manifests and answers in well under a second, |
| 76 | # where `cargo metadata` on the server is tens of seconds and fails outright on |
| 77 | # exactly the state being detected. |
| 78 | CODE_ROOT="" |
| 79 | if [ && ; then |
| 80 | if ! ; then |
| 81 | |
| 82 | |
| 83 | fi |
| 84 | fi |
| 85 | |
| 86 | # ── gate 2: test targets build ───────────────────────────────────────────── |
| 87 | # |
| 88 | # MNW and synckit have no root Cargo.toml by design (standalone crates, no root |
| 89 | # workspace), so there is no one command to run and they get gate 1 only. |
| 90 | [ || |
| 91 | |
| 92 | |
| 93 | if ! ; then |
| 94 | |
| 95 | |
| 96 | |
| 97 | fi |
| 98 | |
| 99 | |
| 100 |