Skip to content

ci: hooks-sync workflow (shellcheck + drift check vs crates templates)#2

Merged
WomB0ComB0 merged 1 commit intomainfrom
ci/hooks-sync
Apr 14, 2026
Merged

ci: hooks-sync workflow (shellcheck + drift check vs crates templates)#2
WomB0ComB0 merged 1 commit intomainfrom
ci/hooks-sync

Conversation

@WomB0ComB0
Copy link
Copy Markdown
Member

Summary

  • Add .github/workflows/hooks-sync.yml that runs on any change to scripts/git-hooks/** or scripts/install-hooks.sh
  • Two jobs:
    • lintshellcheck -S warning + bash -n on all 6 hooks and the installer
    • drift-check — fetches canonical templates from resq-software/crates@master and fails if scripts/git-hooks/<hook> differs from crates/resq-cli/templates/git-hooks/<hook>
  • Locks out the drift that inspired this hardening pass (three copies existed before; we now gate two of them at CI)

A symmetric workflow lands in resq-software/crates in a follow-up PR.

Test plan

  • shellcheck -S warning + bash -n green locally
  • Deliberately edit one byte of scripts/git-hooks/pre-commit on a side branch — drift-check must fail with a useful diff
  • Revert — drift-check green again
  • Typo in a hook (e.g. set -eo pipfail) — lint job must fail

Context

Phase 1 of the canonical-hooks hardening roadmap (~/.claude/plans/binary-hugging-kite.md).

🤖 Generated with Claude Code

@gemini-code-assist
Copy link
Copy Markdown

Note

Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 14, 2026

Warning

Rate limit exceeded

@WomB0ComB0 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 53 minutes and 39 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 53 minutes and 39 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d9ac4b0c-a348-49ef-916e-7f4503d3b187

📥 Commits

Reviewing files that changed from the base of the PR and between fdb2a77 and 4795930.

📒 Files selected for processing (1)
  • .github/workflows/hooks-sync.yml
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/hooks-sync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

WomB0ComB0 added a commit that referenced this pull request Apr 14, 2026
Phase 3.1 of the canonical-hooks hardening roadmap.

tests/hooks/ — 33 bats tests across 5 files:
  - commit-msg.bats: conventional commits accept/reject, ! marker, ticket
    prefix tolerance, WIP-on-main guard, GIT_HOOKS_SKIP, local dispatch,
    here-string safety against `-`-prefixed subjects
  - prepare-commit-msg.bats: ticket-prefix injection from branch name,
    no-op on merge/squash/commit sources
  - pre-push.bats: branch-naming convention, changeset-release allowlist,
    force-push guard with ref-replay through stdin, fast-forward path,
    GIT_HOOKS_SKIP, local-pre-push stdin propagation
  - post-checkout.bats: lock-file change notice, local dispatch
  - pre-commit.bats: soft-skip when resq is missing, local dispatch
  - helpers.bash: shared init_repo_with_hooks / checkout_branch /
    commit_no_hooks helpers (the last bypasses installed hooks during
    test setup so commit-msg doesn't reject "x" fixture commits)

.github/workflows/hooks-tests.yml — installs bats and runs the suite.
Decoupled from hooks-sync.yml so no merge conflict with #2.

Two real bugs surfaced and fixed by writing the tests:

1. pre-push: my earlier here-doc refactor (commit 21c7a77) fed an empty
   line through `<<EOF\n$PUSH_REFS\nEOF` when stdin was empty, causing
   the force-push guard to fire spuriously on `git push` invocations
   without any refs (e.g. setup pushes from a clean main). Now guarded
   with `[ -n "$PUSH_REFS" ]` and a per-iteration `[ -z "$local_sha" ]`
   skip.

2. commit-msg: WIP/fixup/squash guard ran *after* the conventional-commits
   format check. Since "WIP: foo" / "fixup! x" never pass the format
   regex, the WIP guard was dead code and users always saw the generic
   "Invalid format" error on main. Moved the WIP guard before the format
   check so users on main get the branch-specific "WIP commits not
   allowed on main" message.

Verified: bats tests/hooks/ → 33/33 pass locally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WomB0ComB0 added a commit that referenced this pull request Apr 14, 2026
Phase 3.1 of the canonical-hooks hardening roadmap.

tests/hooks/ — 33 bats tests across 5 files:
  - commit-msg.bats: conventional commits accept/reject, ! marker, ticket
    prefix tolerance, WIP-on-main guard, GIT_HOOKS_SKIP, local dispatch,
    here-string safety against `-`-prefixed subjects
  - prepare-commit-msg.bats: ticket-prefix injection from branch name,
    no-op on merge/squash/commit sources
  - pre-push.bats: branch-naming convention, changeset-release allowlist,
    force-push guard with ref-replay through stdin, fast-forward path,
    GIT_HOOKS_SKIP, local-pre-push stdin propagation
  - post-checkout.bats: lock-file change notice, local dispatch
  - pre-commit.bats: soft-skip when resq is missing, local dispatch
  - helpers.bash: shared init_repo_with_hooks / checkout_branch /
    commit_no_hooks helpers (the last bypasses installed hooks during
    test setup so commit-msg doesn't reject "x" fixture commits)

.github/workflows/hooks-tests.yml — installs bats and runs the suite.
Decoupled from hooks-sync.yml so no merge conflict with #2.

Two real bugs surfaced and fixed by writing the tests:

1. pre-push: my earlier here-doc refactor (commit 21c7a77) fed an empty
   line through `<<EOF\n$PUSH_REFS\nEOF` when stdin was empty, causing
   the force-push guard to fire spuriously on `git push` invocations
   without any refs (e.g. setup pushes from a clean main). Now guarded
   with `[ -n "$PUSH_REFS" ]` and a per-iteration `[ -z "$local_sha" ]`
   skip.

2. commit-msg: WIP/fixup/squash guard ran *after* the conventional-commits
   format check. Since "WIP: foo" / "fixup! x" never pass the format
   regex, the WIP guard was dead code and users always saw the generic
   "Invalid format" error on main. Moved the WIP guard before the format
   check so users on main get the branch-specific "WIP commits not
   allowed on main" message.

Verified: bats tests/hooks/ → 33/33 pass locally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New workflow runs on any change to scripts/git-hooks/** or
scripts/install-hooks.sh:

- shellcheck -S warning + bash -n every hook + the installer
- Fetch canonical templates from resq-software/crates@master and fail the
  job if scripts/git-hooks/<hook> differs from
  crates/resq-cli/templates/git-hooks/<hook>

This locks out silent drift between the two sources of truth. A symmetric
workflow will land in resq-software/crates in a follow-up PR.

Phase 1 of the hooks-hardening roadmap.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@WomB0ComB0 WomB0ComB0 merged commit fdeea6a into main Apr 14, 2026
4 of 5 checks passed
@WomB0ComB0 WomB0ComB0 deleted the ci/hooks-sync branch April 14, 2026 11:45
WomB0ComB0 added a commit that referenced this pull request Apr 14, 2026
Phase 3.1 of the canonical-hooks hardening roadmap.

tests/hooks/ — 33 bats tests across 5 files:
  - commit-msg.bats: conventional commits accept/reject, ! marker, ticket
    prefix tolerance, WIP-on-main guard, GIT_HOOKS_SKIP, local dispatch,
    here-string safety against `-`-prefixed subjects
  - prepare-commit-msg.bats: ticket-prefix injection from branch name,
    no-op on merge/squash/commit sources
  - pre-push.bats: branch-naming convention, changeset-release allowlist,
    force-push guard with ref-replay through stdin, fast-forward path,
    GIT_HOOKS_SKIP, local-pre-push stdin propagation
  - post-checkout.bats: lock-file change notice, local dispatch
  - pre-commit.bats: soft-skip when resq is missing, local dispatch
  - helpers.bash: shared init_repo_with_hooks / checkout_branch /
    commit_no_hooks helpers (the last bypasses installed hooks during
    test setup so commit-msg doesn't reject "x" fixture commits)

.github/workflows/hooks-tests.yml — installs bats and runs the suite.
Decoupled from hooks-sync.yml so no merge conflict with #2.

Two real bugs surfaced and fixed by writing the tests:

1. pre-push: my earlier here-doc refactor (commit 21c7a77) fed an empty
   line through `<<EOF\n$PUSH_REFS\nEOF` when stdin was empty, causing
   the force-push guard to fire spuriously on `git push` invocations
   without any refs (e.g. setup pushes from a clean main). Now guarded
   with `[ -n "$PUSH_REFS" ]` and a per-iteration `[ -z "$local_sha" ]`
   skip.

2. commit-msg: WIP/fixup/squash guard ran *after* the conventional-commits
   format check. Since "WIP: foo" / "fixup! x" never pass the format
   regex, the WIP guard was dead code and users always saw the generic
   "Invalid format" error on main. Moved the WIP guard before the format
   check so users on main get the branch-specific "WIP commits not
   allowed on main" message.

Verified: bats tests/hooks/ → 33/33 pass locally.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WomB0ComB0 added a commit that referenced this pull request Apr 14, 2026
Standardization pass across public ResQ repos:

  .github/workflows/security.yml  Thin caller for the reusable workflow
                                  in resq-software/.github. Passes the
                                  language list appropriate for this
                                  repo (["actions"] — bash-only repo,
                                  CodeQL analyzes workflow YAML).
  .github/dependabot.yml           Weekly grouped github-actions
                                  updates, Monday 06:17 UTC.
  .github/CODEOWNERS               Default owner + explicit rules for
                                  the installer + hooks surface.

Inherits CoC / Contributing / Security / Support / issue + PR templates
from resq-software/.github (landed in that repo's #2).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WomB0ComB0 added a commit that referenced this pull request Apr 15, 2026
Standardization pass across public ResQ repos:

  .github/workflows/security.yml  Thin caller for the reusable workflow
                                  in resq-software/.github. Passes the
                                  language list appropriate for this
                                  repo (["actions"] — bash-only repo,
                                  CodeQL analyzes workflow YAML).
  .github/dependabot.yml           Weekly grouped github-actions
                                  updates, Monday 06:17 UTC.
  .github/CODEOWNERS               Default owner + explicit rules for
                                  the installer + hooks surface.

Inherits CoC / Contributing / Security / Support / issue + PR templates
from resq-software/.github (landed in that repo's #2).

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WomB0ComB0 added a commit that referenced this pull request Apr 15, 2026
Retire the whole "AI-assisted development" block (120 lines across
"The convention" / "Why .agents/skills exists" / "AGENTS.md contract" /
"Writing a skill" / "Keeping things in sync" / "Supported tools").

That convention required each repo to maintain parallel directories for
Claude / Codex / Cursor / Gemini / Copilot, keep AGENTS.md ↔ CLAUDE.md
in sync via agent-sync.sh, and manage symlinks from a `.agents/skills/`
source of truth. In practice: too much per-repo maintenance and an
ever-expanding surface for supply-chain tampering (every agent tool is
another directory on disk that could get a malicious rule file).

Replacement is a 2-paragraph "Contributor guide" section pointing at:
- `AGENTS.md` at the root of each repo — the actual canonical dev guide
- Org-level defaults in resq-software/.github: CONTRIBUTING.md,
  SECURITY.md, CODE_OF_CONDUCT.md (which every public repo now
  inherits automatically since PR #2 in that repo)

README goes from 261 lines to 141. Grep for `.agents/`, `skills`,
`CLAUDE`, `Cursor`, `Gemini`, `Codex` in this file → zero hits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WomB0ComB0 added a commit that referenced this pull request Apr 15, 2026
Retire the whole "AI-assisted development" block (120 lines across
"The convention" / "Why .agents/skills exists" / "AGENTS.md contract" /
"Writing a skill" / "Keeping things in sync" / "Supported tools").

That convention required each repo to maintain parallel directories for
Claude / Codex / Cursor / Gemini / Copilot, keep AGENTS.md ↔ CLAUDE.md
in sync via agent-sync.sh, and manage symlinks from a `.agents/skills/`
source of truth. In practice: too much per-repo maintenance and an
ever-expanding surface for supply-chain tampering (every agent tool is
another directory on disk that could get a malicious rule file).

Replacement is a 2-paragraph "Contributor guide" section pointing at:
- `AGENTS.md` at the root of each repo — the actual canonical dev guide
- Org-level defaults in resq-software/.github: CONTRIBUTING.md,
  SECURITY.md, CODE_OF_CONDUCT.md (which every public repo now
  inherits automatically since PR #2 in that repo)

README goes from 261 lines to 141. Grep for `.agents/`, `skills`,
`CLAUDE`, `Cursor`, `Gemini`, `Codex` in this file → zero hits.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant