1 The hook
Read the tutorial. Build it yourself is the other way to read this part.
just setupA check that each person must remember to run is a check that someone forgets. We write the check as a recipe.
Then a hook runs the recipe before each git commit, and a last recipe tells git to use the hook.
The code before this part. The code after it.
1.1 A recipe that checks the code
A file with a syntax error breaks tally, and nothing finds it before it goes into git. A recipe check compiles
every Python file of tally. Python then reports a syntax error at once, without running the code.
A patch to justfile.
At the end of the justfile, the new recipe check runs py_compile, the compiler of Python, on each file of
src/tally.
just check && echo "the code compiles"It prints the code compiles, because every file compiles.
What changed
justfile: the recipe compiles every Python file. 4 lines added. The file.- look at
uv run python -m py_compile src/tally/*.py
- look at
The diff of justfile
@@ -13,3 +13,7 @@ run file:
# Run the tests
test:
uv run python -m unittest discover -s tests -q
+
+# Check that every Python file compiles
+check:
+ uv run python -m py_compile src/tally/*.py1.2 A hook script
just check works only when someone runs it. A hook runs it every time. git runs the file pre-commit of its
folder of hooks before each git commit, and stops the git commit when the file fails. The hook,
hooks/pre-commit, has three lines. They name the shell that runs it, say what it does, and run the check. The
file comes whole, as a patch.
A patch to hooks/pre-commit.
The new file hooks/pre-commit runs just check, and nothing else. When just check fails, the hook fails too.
sh hooks/pre-commit; echo "the hook said $?"The code compiles, so the hook says 0.
What changed
hooks/pre-commit: three lines that git runs before agit commit. the hook only runs the check. New, 3 lines. The file.- look at
just check
- look at
The diff of hooks/pre-commit
@@ -0,0 +1,3 @@
+#!/bin/sh
+# Run before each commit: refuse it if the code does not compile.
+just check1.3 Make it runnable
git runs a hook only if the hook is executable, so we make it executable.
This change came from running chmod +x hooks/pre-commit.
Run the hook by hand on a broken file, and see it fail. Then remove the file:
printf 'def (\n' > src/tally/broken.py; ./hooks/pre-commit; echo "the hook said $?"
rm src/tally/broken.pyThe hook says 1, so git would stop the git commit.
What changed
hooks/pre-commit: Its lines stay as they were. It comes fromchmod +x hooks/pre-commit. The file.
1.4 Install it
git does not copy hooks with a repository, and does not look in the folder hooks unless it is told to. So each
copy of the project installs the hook. A recipe install-hook sets core.hooksPath, the setting of git that names
the folder of hooks. The recipe changes the settings of git, so it is a recipe of its own, and not a part of
just setup. just setup only runs uv sync, which makes the environment.
A patch to justfile.
At the end of the justfile, the new recipe install-hook runs one line, git config core.hooksPath hooks.
just install-hook && git config core.hooksPathIt prints hooks, so git now runs the hook before each git commit.
What changed
justfile: one line of git config, in a recipe of its own. 4 lines added. The file.- look at
git config core.hooksPath hooks
- look at
The diff of justfile
@@ -17,3 +17,7 @@ test:
# Check that every Python file compiles
check:
uv run python -m py_compile src/tally/*.py
+
+# Make git run the hooks in hooks/, before each commit. This changes git's config, so it is not part of setup
+install-hook:
+ git config core.hooksPath hooks