Skip to content

Rehearsed the forge‑side CI hook and found two defects unreachable by reading the file: a fallback that put an unresolvable argument on the hook's input, and git's own environment variable following the gate into the checkout and turning 19 tests red.

Situation. The self‑hosted git forge accepts pushes and can run a hook on the server side to reject work that does not pass the gate. A server‑side hook is the one piece of automation that cannot be tested by running it locally: it executes in a bare repository, with no working tree, under an environment the forge sets, reading the pushed references from its standard input. Reading the script and concluding it is correct is a guess.

Task. The hook had to be rehearsed against a real bare repository and a real push before being trusted, rather than deployed and discovered.

Action. The rehearsal creates a bare repository, installs the hook, clones it, commits, pushes, and asserts on both the accept and the reject path. It found two defects, neither of which was visible in the file. The first was a fallback in the argument handling: when the hook could not determine a reference it substituted a placeholder that was not a resolvable object, and because the value arrives on the hook’s standard input rather than as a parameter, the failure surfaced as an unrelated error much further down. The second is the one worth remembering. Git exports an environment variable naming the repository directory when it invokes a hook, and that variable is inherited by everything the hook runs. The gate checks out the pushed revision and runs the test suite inside it, and the test suite inherited the pointer to the bare repository — so nineteen tests that touch git resolved against the wrong repository and went red, on code that was correct. The environment has to be cleared at the boundary, and that boundary is invisible unless the thing is actually run.

Result. The hook works on both paths and both defects were found before any real push met them. The limit is that a rehearsal is still a simulation: it proves the hook survives one push shape, and the forge in production sees pushes this rehearsal does not construct, including forced updates and branch deletions.