|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# metaobjects pre-push gate. |
| 4 | +# |
| 5 | +# `bun test` transpiles per-file and does NOT typecheck, so type-broken code ships |
| 6 | +# green on the test suite while the CI `typecheck` job goes red. CI catches it, but a |
| 7 | +# direct push to `main` by an admin BYPASSES branch protection — so red lands anyway. |
| 8 | +# This hook closes that hole locally: it runs the SAME `tsc` gate CI runs and BLOCKS |
| 9 | +# the push when it is red. Loud by design — nothing red should leave your machine |
| 10 | +# quietly. |
| 11 | +# |
| 12 | +# Scope: only the TypeScript build+typecheck gate (the one local `bun test` skips). |
| 13 | +# The Java / C# / Python compile+conformance gates stay CI-only (too heavy + toolchain |
| 14 | +# -dependent for a push hook); branch protection still requires them on PRs. |
| 15 | +# |
| 16 | +# Activate (one-time, per clone): git config core.hooksPath .githooks |
| 17 | +# Bypass (emergencies only): git push --no-verify |
| 18 | +# SKIP_TS_TYPECHECK=1 git push |
| 19 | +# |
| 20 | +set -uo pipefail |
| 21 | + |
| 22 | +if [ "${SKIP_TS_TYPECHECK:-}" = "1" ]; then |
| 23 | + echo "pre-push: SKIP_TS_TYPECHECK=1 set — skipping the TS typecheck gate (you asked for it)." >&2 |
| 24 | + exit 0 |
| 25 | +fi |
| 26 | + |
| 27 | +ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo .)" |
| 28 | +ZERO="0000000000000000000000000000000000000000" |
| 29 | + |
| 30 | +# Collect the files changed across every ref being pushed (stdin: <localref> <localsha> |
| 31 | +# <remoteref> <remotesha>). A brand-new branch (remote sha = zeros) has no remote base, |
| 32 | +# so we fail SAFE and run the gate rather than guess. |
| 33 | +changed="" |
| 34 | +run_gate=0 |
| 35 | +while read -r _localref localsha _remoteref remotesha; do |
| 36 | + [ -z "${localsha:-}" ] && continue |
| 37 | + if [ "$localsha" = "$ZERO" ]; then |
| 38 | + continue # branch deletion — nothing to check |
| 39 | + fi |
| 40 | + if [ "$remotesha" = "$ZERO" ]; then |
| 41 | + run_gate=1 # new branch: no base to diff — run the gate |
| 42 | + continue |
| 43 | + fi |
| 44 | + files="$(git diff --name-only "$remotesha..$localsha" 2>/dev/null || true)" |
| 45 | + changed="$changed |
| 46 | +$files" |
| 47 | +done |
| 48 | + |
| 49 | +# Skip the (slow) gate when the push touches no TypeScript sources. |
| 50 | +if [ "$run_gate" -ne 1 ]; then |
| 51 | + if printf '%s\n' "$changed" | grep -qE '^(server/typescript|client/web)/'; then |
| 52 | + run_gate=1 |
| 53 | + fi |
| 54 | +fi |
| 55 | + |
| 56 | +if [ "$run_gate" -ne 1 ]; then |
| 57 | + exit 0 |
| 58 | +fi |
| 59 | + |
| 60 | +echo "pre-push: TypeScript change detected — running the build + typecheck gate (CI parity)…" >&2 |
| 61 | +echo " (skip in an emergency with: git push --no-verify)" >&2 |
| 62 | + |
| 63 | +# typecheck resolves cross-package types through each dep's dist/, so build first — |
| 64 | +# exactly what .github/workflows/conformance.yml does before `typecheck`. |
| 65 | +if ! ( cd "$ROOT" && bun run --filter '*' build ) >/tmp/metaobjects-prepush-build.log 2>&1; then |
| 66 | + echo "" >&2 |
| 67 | + echo " ✖ pre-push BLOCKED: workspace build failed. Last lines:" >&2 |
| 68 | + tail -20 /tmp/metaobjects-prepush-build.log | sed 's/^/ /' >&2 |
| 69 | + echo " full log: /tmp/metaobjects-prepush-build.log" >&2 |
| 70 | + exit 1 |
| 71 | +fi |
| 72 | + |
| 73 | +if ! ( cd "$ROOT" && bun run --filter '*' typecheck ) >/tmp/metaobjects-prepush-tsc.log 2>&1; then |
| 74 | + echo "" >&2 |
| 75 | + echo " ✖ pre-push BLOCKED: tsc typecheck is RED. This is the gate CI enforces; do not push red." >&2 |
| 76 | + echo "" >&2 |
| 77 | + grep -E "error TS" /tmp/metaobjects-prepush-tsc.log | grep -v node_modules | sed 's/^/ /' | head -40 >&2 |
| 78 | + echo "" >&2 |
| 79 | + echo " full log: /tmp/metaobjects-prepush-tsc.log" >&2 |
| 80 | + echo " fix the errors, or (emergencies only) re-push with --no-verify." >&2 |
| 81 | + exit 1 |
| 82 | +fi |
| 83 | + |
| 84 | +echo "pre-push: ✓ TypeScript build + typecheck clean." >&2 |
| 85 | +exit 0 |
0 commit comments