From c111de0f3d48a86e42412f4092ca54eb9f8a1118 Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Wed, 24 Jun 2026 14:09:59 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(hooks):=20install-hooks.sh=20=E2=80=94?= =?UTF-8?q?=20one-command=20hook=20activation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 6 +++--- docs/wiki/quality-gates.md | 2 +- scripts/install-hooks.sh | 12 ++++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100755 scripts/install-hooks.sh diff --git a/AGENTS.md b/AGENTS.md index ee94d42..2be39e4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -108,9 +108,9 @@ cargo build --workspace --release ./scripts/ci-checks.sh # fmt --check, clippy -D warnings, test, markdown gate, wiki-lint --strict ``` -A `git push` to `main` also runs this gate through the pre-push hook (install -once: `ln -sf ../../scripts/pre-push .git/hooks/pre-push`). The hook rejects -the push if any gate fails; bypass only in emergencies with `--no-verify`. +A `git push` to `main` also runs this gate through the pre-push hook +(activate once: `./scripts/install-hooks.sh`). The hook rejects the push if +any gate fails; bypass only in emergencies with `--no-verify`. `.forgejo/workflows/ci.yml` encodes the same checks, but **no Actions runner is currently registered**, so nothing enforces them server-side. Until a runner is diff --git a/docs/wiki/quality-gates.md b/docs/wiki/quality-gates.md index 26a91a5..2757faa 100644 --- a/docs/wiki/quality-gates.md +++ b/docs/wiki/quality-gates.md @@ -11,7 +11,7 @@ A change is not "done" until the gate passes locally: ``` The pre-push hook (`scripts/pre-push`) runs this same gate on every `git push` -to `main` — install once with `ln -sf ../../scripts/pre-push .git/hooks/pre-push`. +to `main` — activate once per clone with `./scripts/install-hooks.sh`. The hook rejects the push if any gate fails; bypass only in emergencies with `--no-verify`. diff --git a/scripts/install-hooks.sh b/scripts/install-hooks.sh new file mode 100755 index 0000000..fa48c2d --- /dev/null +++ b/scripts/install-hooks.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# Install git hooks. Run once per clone. +# +# ./scripts/install-hooks.sh +# +# After this, every `git push` runs ci-checks.sh + wiki-lint --strict +# and rejects the push if the gate fails. Bypass only with --no-verify. +set -eu + +REPO_ROOT="$(git rev-parse --show-toplevel)" +ln -sf ../../scripts/pre-push "$REPO_ROOT/.git/hooks/pre-push" +echo "hooks installed: pre-push" -- 2.45.3 From 9f091454e504947f5cb02651ffdf4bfeda70ff07 Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Wed, 24 Jun 2026 14:13:06 +0200 Subject: [PATCH 2/2] fix(hooks): make install-hooks robust for worktrees/custom git dirs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The installer symlinked a RELATIVE target (../../scripts/pre-push), which only resolves for a standard /.git/hooks layout — it breaks in git worktrees or when .git is a file/elsewhere (both used by the agent harness), and assumed .git/hooks already exists. Resolve the real hooks dir via 'git rev-parse --git-path hooks' (worktree-safe), mkdir -p it, and symlink to the ABSOLUTE source path so it works regardless of where the hooks dir lives. Also verify pre-push exists + is executable. Tested: installs, link resolves to scripts/pre-push, idempotent. --- scripts/install-hooks.sh | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/scripts/install-hooks.sh b/scripts/install-hooks.sh index fa48c2d..efc0328 100755 --- a/scripts/install-hooks.sh +++ b/scripts/install-hooks.sh @@ -1,12 +1,24 @@ #!/bin/sh -# Install git hooks. Run once per clone. +# Install git hooks for this clone. Run once per clone (idempotent). # # ./scripts/install-hooks.sh # -# After this, every `git push` runs ci-checks.sh + wiki-lint --strict -# and rejects the push if the gate fails. Bypass only with --no-verify. +# After this, every `git push` runs ci-checks.sh + wiki-lint --strict and +# rejects the push if the gate fails. Bypass only with --no-verify. set -eu -REPO_ROOT="$(git rev-parse --show-toplevel)" -ln -sf ../../scripts/pre-push "$REPO_ROOT/.git/hooks/pre-push" -echo "hooks installed: pre-push" +repo_root="$(git rev-parse --show-toplevel)" +src="${repo_root}/scripts/pre-push" +[ -f "$src" ] || { echo "error: ${src} not found" >&2; exit 1; } +chmod +x "$src" + +# --git-path resolves the real hooks dir even in worktrees / custom git dirs, +# where .git is a file or the hooks live outside /.git/hooks. +cd "$repo_root" +hooks_dir="$(git rev-parse --git-path hooks)" +mkdir -p "$hooks_dir" + +# Symlink to the absolute source path → robust regardless of where the hooks +# dir actually lives (a relative target breaks for worktrees). +ln -sf "$src" "${hooks_dir}/pre-push" +echo "hooks installed: pre-push -> ${src}" -- 2.45.3