diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5fe81e87ee..bff1205e2d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -79,6 +79,14 @@ The `make dev` and `make release` commands do three things: 4. Compile the OCaml bytecode to JavaScript (`_build/default/src/hazelweb/www/hazel.js`) using `js_of_ocaml`. +## Git Hooks + +`make deps` installs a `pre-commit` hook (under `scripts/git-hooks/`) by setting `core.hooksPath`. On every commit the hook runs `dune fmt --auto-promote` and re-stages any of the *staged* files the formatter touched, so commits never contain unformatted code. + +- Files you didn't stage are left alone, even if the formatter modifies them — your unrelated unstaged WIP won't be swept into the commit. +- If you ever need to bypass the hook (e.g. to share a WIP refactor that you don't want autoformatted yet), pass `--no-verify` to `git commit`. +- If your clone predates this and the hook isn't running, run `make deps` (or `make install-hooks`) once to install it. + ## Live Building For a smoother dev experience, use `make watch` rather than `make dev` to automatically watch for file changes and rebuild immediately. You can also run `make watch-release` to continuously build the release diff --git a/Makefile b/Makefile index 802a66945d..3c0234a7df 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,16 @@ HTML_DIR="$(shell pwd)/_build/default/src/web/www" SERVER="http://0.0.0.0:8000/" -.PHONY: all deps change-deps setup-instructor setup-student dev dev-helper dev-student fmt watch watch-release release release-student echo-html-dir serve serve2 repl test clean setup-zarith +.PHONY: all deps change-deps setup-instructor setup-student dev dev-helper dev-student fmt watch watch-release release release-student echo-html-dir serve serve2 repl test clean setup-zarith install-hooks all: dev +install-hooks: + @if [ "$$(git config --get core.hooksPath)" != "scripts/git-hooks" ]; then \ + git config core.hooksPath scripts/git-hooks; \ + echo "Installed git hooks (core.hooksPath -> scripts/git-hooks)."; \ + fi + # Install native BigInt runtime for zarith_stubs_js to fix WebWorker postMessage serialization. # The vendored runtime.js uses native JS BigInt (from zarith_stubs_js v0.17.0) which survives # structured clone, unlike the BigInteger.js library used in older versions. @@ -12,7 +18,7 @@ setup-zarith: @echo "Installing native BigInt zarith runtime..." @cp vendor/zarith_native_bigint_runtime.js "$$(opam var lib)/zarith_stubs_js/runtime.js" -deps: +deps: install-hooks opam repo add archive git+https://github.com/ocaml/opam-repository-archive opam update opam install ./hazel.opam.locked --deps-only --with-test --with-doc diff --git a/README.md b/README.md index 23f8fce8e8..4360f257fe 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ If you already have `ocaml` version 5.2.0, at least version 2.0 of `opam`, and a - `make deps` - `make dev` +`make deps` also installs a pre-commit git hook that auto-formats your staged code on every commit. See [CONTRIBUTING.md](CONTRIBUTING.md#git-hooks) for details. + ### Long Version If the above pre-requisites don't apply, please follow the step-by-step installation instructions contained in [INSTALL.md](INSTALL.md). diff --git a/scripts/git-hooks/pre-commit b/scripts/git-hooks/pre-commit new file mode 100755 index 0000000000..0438473c7e --- /dev/null +++ b/scripts/git-hooks/pre-commit @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -u + +# Capture which files are staged for this commit. After running the formatter, +# we'll re-add these (and only these) so the commit picks up any formatting +# fixes — without sweeping in unrelated unstaged changes the developer may have. +staged=$(git diff --cached --name-only --diff-filter=ACMR) + +echo "[pre-commit] dune fmt --auto-promote" +dune fmt --auto-promote || true + +if [ -n "$staged" ]; then + while IFS= read -r f; do + [ -e "$f" ] && git add -- "$f" + done <<<"$staged" +fi