| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- #!/usr/bin/env bash
- set -euo pipefail
- # Pre-commit hook: keeps dist/ in sync with src/.
- #
- # Why: vendor/qmd/dist/ is tracked in git. Consumer machines fetch the
- # repo and use dist/ AS-IS without rebuilding (cli/build.sh and the fleet
- # pull_and_build_and_deploy.sh both consume the pre-built JS). A commit
- # that updates src/*.ts without committing the matching dist/ will ship a
- # CLI that crashes with MODULE_NOT_FOUND on any new import path.
- # Reference: i-9l2ueyyz (post-mortem of i-qkarfffa Stage-3 dist/embedding/
- # catch-up bug).
- #
- # Behaviour:
- # 1. If no staged file matches ^src/.*\.ts$ -> exit 0 (fast path for
- # docs/config-only commits).
- # 2. Otherwise run `npm run build` (which runs tsc + chmod the CLI shim).
- # 3. Then `git diff --quiet -- dist/` -- if dist/ changed as a result of
- # the rebuild AND those changes are not staged, FAIL LOUDLY with a
- # message naming the drifted files and the recovery command.
- #
- # Bypass: `git commit --no-verify` (standard git escape hatch).
- # --- collect staged src/*.ts paths -----------------------------------------
- mapfile -t STAGED_SRC < <(git diff --cached --name-only --diff-filter=ACMR | grep -E '^src/.*\.ts$' || true)
- if [[ "${#STAGED_SRC[@]}" -eq 0 ]]; then
- # Nothing under src/ in this commit -- no rebuild required.
- exit 0
- fi
- echo "pre-commit: ${#STAGED_SRC[@]} staged src/*.ts file(s) detected -- rebuilding dist/" >&2
- # --- snapshot dist/ before rebuild so we can detect rebuild-induced drift --
- # Use SHA-256 of git ls-tree-style content (working tree) for every dist/ file
- # we care about. Cheap enough for ~75 files.
- DIST_SHA_BEFORE=$(find dist -type f \( -name '*.js' -o -name '*.d.ts' -o -name '*.js.map' -o -name '*.d.ts.map' \) -print0 \
- | sort -z \
- | xargs -0 sha256sum \
- | sha256sum \
- | awk '{print $1}')
- # --- run build -- this also gates on tsc errors ----------------------------
- if ! npm run --silent build >/tmp/qmd-pre-commit-build.log 2>&1; then
- echo "" >&2
- echo "pre-commit: npm run build FAILED" >&2
- echo "" >&2
- echo "Build output (last 40 lines):" >&2
- tail -n 40 /tmp/qmd-pre-commit-build.log >&2
- echo "" >&2
- echo "Full log: /tmp/qmd-pre-commit-build.log" >&2
- exit 1
- fi
- DIST_SHA_AFTER=$(find dist -type f \( -name '*.js' -o -name '*.d.ts' -o -name '*.js.map' -o -name '*.d.ts.map' \) -print0 \
- | sort -z \
- | xargs -0 sha256sum \
- | sha256sum \
- | awk '{print $1}')
- # --- determine drift relative to git index ---------------------------------
- # `git diff --quiet -- dist/` exits 0 if working tree dist/ matches index.
- # Non-zero means dist/ has changes that are NOT staged.
- if git diff --quiet -- dist/; then
- # dist/ matches index -- everything is in sync, commit may proceed.
- exit 0
- fi
- # Drift detected. Print the offending files + recovery command.
- echo "" >&2
- echo "==========================================================================" >&2
- echo "ABORT: dist/ out of sync with src/" >&2
- echo "==========================================================================" >&2
- echo "" >&2
- echo "Your commit modifies src/*.ts but dist/ is not in sync after rebuild." >&2
- echo "Shipping this commit would push a broken @oivo/qmd to consumer machines" >&2
- echo "(see i-9l2ueyyz / i-qkarfffa Stage-3 retrospective)." >&2
- echo "" >&2
- echo "Drifted files:" >&2
- git diff --name-only -- dist/ | sed 's/^/ /' >&2
- echo "" >&2
- if [[ "$DIST_SHA_BEFORE" == "$DIST_SHA_AFTER" ]]; then
- # The rebuild produced the SAME dist/ as before, but git still reports
- # drift -- this means dist/ was already drifted before this commit
- # (someone hand-edited or forgot to stage a previous build). Tell them.
- echo "Note: rebuild produced no changes -- dist/ was already drifted before this" >&2
- echo "commit. Stage the drift OR revert dist/ to HEAD before retrying:" >&2
- echo "" >&2
- echo " # Option A: include the drift in this commit" >&2
- echo " git add dist/ && git commit # retry" >&2
- echo "" >&2
- echo " # Option B: discard the drift (use if dist/ was hand-edited)" >&2
- echo " git checkout HEAD -- dist/ && git commit # retry" >&2
- else
- # The rebuild itself produced new dist/ content. User just needs to stage
- # the freshly-built dist/.
- echo "Recovery (run, then retry the commit):" >&2
- echo "" >&2
- echo " git add dist/" >&2
- echo " git commit # retry" >&2
- fi
- echo "" >&2
- echo "Bypass (NOT recommended -- ships broken @oivo/qmd to fleet):" >&2
- echo " git commit --no-verify" >&2
- echo "" >&2
- exit 1
|