From fda835f6ba243e85d5d7a474e34e1f97b9fa691a Mon Sep 17 00:00:00 2001 From: Mike Odnis Date: Tue, 14 Apr 2026 17:10:22 -0400 Subject: [PATCH] feat(installer): use `resq hooks install` / `scaffold-local` with fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swap scripts/install-hooks.{sh,ps1} to the new consolidated command paths introduced in resq-software/crates#60: resq hooks install (was: resq dev install-hooks) resq hooks scaffold-local (was: resq dev scaffold-local-hook) Both scripts probe `--help` for the new paths first and fall back to the legacy `dev` paths if the user's resq binary pre-dates the consolidation (e.g. a cached v0.2.6). The legacy paths still work too — they just print a deprecation warning. No user-facing break. README + AGENTS snippets updated to show the new canonical paths. Smoke-tested end-to-end with a master-built resq: the `hooks install` path is taken silently; hooks land in .git-hooks/; local-pre-push scaffold prompts as expected. Co-Authored-By: Claude Opus 4.6 (1M context) --- AGENTS.md | 4 ++-- README.md | 6 +++--- scripts/install-hooks.ps1 | 24 +++++++++++++++++------- scripts/install-hooks.sh | 26 +++++++++++++++++++++----- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f4f25d8..5b09e06 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,7 @@ scripts/ install-resq.sh — Installs the `resq` CLI binary from GitHub Releases (SHA-verified) # Canonical hook templates are owned by resq-software/crates # (crates/resq-cli/templates/git-hooks/). install-hooks.sh fetches them - # from there (or lets `resq dev install-hooks` scaffold offline). No copy + # from there (or lets `resq hooks install` scaffold offline). No copy # lives in this repo. lib/ log.{sh,ps1} — Colored log helpers @@ -65,7 +65,7 @@ Canonical hook templates live in [`resq-software/crates`](https://github.com/resq-software/crates/tree/master/crates/resq-cli/templates/git-hooks) and are installed into any ResQ repo by `scripts/install-hooks.sh` (or `.ps1`). When the `resq` binary is on PATH, the installer calls -`resq dev install-hooks` which scaffolds from the embedded templates — +`resq hooks install` which scaffolds from the embedded templates — offline, no network round-trip. Without `resq`, it falls back to fetching the templates from the crates repo via raw.githubusercontent.com. diff --git a/README.md b/README.md index a8a7345..4d52a8c 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ Everything is pinned via Nix flakes. No "works on my machine" issues. Six hook shims live in [`resq-software/crates`](https://github.com/resq-software/crates/tree/master/crates/resq-cli/templates/git-hooks) — embedded in the `resq` binary *and* served at a stable raw URL. `install-hooks.sh` picks the best path automatically: -1. **`resq` on PATH** → calls `resq dev install-hooks`, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed `resq`. +1. **`resq` on PATH** → calls `resq hooks install`, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed `resq`. 2. **No `resq`** → falls back to `curl` from `resq-software/crates/master/.../templates/git-hooks/`. The hooks delegate logic back to the `resq` binary (`resq pre-commit`, etc.), so updates roll out via `cargo install --git` (or `install-resq.sh`) without editing every repo. @@ -249,12 +249,12 @@ The hooks delegate logic back to the `resq` binary (`resq pre-commit`, etc.), so Each hook then dispatches to `.git-hooks/local-` (if executable) — the **only** place a repo commits hook customization. Generate one with the right language template: ```bash -resq dev scaffold-local-hook --kind auto # detects rust/python/node/dotnet/cpp/nix +resq hooks scaffold-local --kind auto # detects rust/python/node/dotnet/cpp/nix ``` `resq hooks doctor` reports drift, `resq hooks update` re-syncs from the embedded canonical, `resq hooks status` prints a one-line shell-friendly summary. -The canonical content lives in exactly one place: [`crates/resq-cli/templates/git-hooks/`](https://github.com/resq-software/crates/tree/master/crates/resq-cli/templates/git-hooks). The crates repo's own `.git-hooks/` (for dog-fooding) is kept identical via `hooks-sync.yml`. The `dev/` repo used to ship a third copy and was retired in Phase 4 — `install-hooks.sh` now fetches from the crates source (or lets `resq dev install-hooks` do it offline). Bats + Rust integration tests cover the hook behavior end-to-end. +The canonical content lives in exactly one place: [`crates/resq-cli/templates/git-hooks/`](https://github.com/resq-software/crates/tree/master/crates/resq-cli/templates/git-hooks). The crates repo's own `.git-hooks/` (for dog-fooding) is kept identical via `hooks-sync.yml`. The `dev/` repo used to ship a third copy and was retired in Phase 4 — `install-hooks.sh` now fetches from the crates source (or lets `resq hooks install` do it offline). Bats + Rust integration tests cover the hook behavior end-to-end. ## 📄 License diff --git a/scripts/install-hooks.ps1 b/scripts/install-hooks.ps1 index 3b76ec4..3073873 100644 --- a/scripts/install-hooks.ps1 +++ b/scripts/install-hooks.ps1 @@ -44,10 +44,14 @@ if ($onPath) { } # ── Path 1: use resq when present (preferred — offline, no raw fetch) ─────── +# Prefer the new `hooks install` path; fall back to `dev install-hooks` +# for binaries built before resq-software/crates#60. if ($resqBin) { - Write-Host "info Installing hooks via $resqBin dev install-hooks" -ForegroundColor Cyan + & $resqBin hooks install --help *> $null + $installArgs = if ($LASTEXITCODE -eq 0) { @('hooks', 'install') } else { @('dev', 'install-hooks') } + Write-Host "info Installing hooks via $resqBin $($installArgs -join ' ')" -ForegroundColor Cyan Push-Location $targetRoot - try { & $resqBin dev install-hooks } finally { Pop-Location } + try { & $resqBin @installArgs } finally { Pop-Location } } else { # ── Path 2: fall back to raw fetch from crates templates ──────────────── $hooks = @('pre-commit','commit-msg','prepare-commit-msg','pre-push','post-checkout','post-merge') @@ -77,9 +81,15 @@ if (-not $resqBin) { # ── Local-hook scaffold prompt ────────────────────────────────────────────── if ((Test-Path (Join-Path $hooksDir 'local-pre-push')) -or $env:RESQ_SKIP_LOCAL_SCAFFOLD) { exit 0 } -# Probe for subcommand support. -$probe = & $resqBin dev scaffold-local-hook --help 2>&1 -if ($LASTEXITCODE -ne 0) { exit 0 } +# Probe for subcommand support; prefer the new path. +& $resqBin hooks scaffold-local --help *> $null +if ($LASTEXITCODE -eq 0) { + $scaffoldArgs = @('hooks', 'scaffold-local') +} else { + & $resqBin dev scaffold-local-hook --help *> $null + if ($LASTEXITCODE -ne 0) { exit 0 } + $scaffoldArgs = @('dev', 'scaffold-local-hook') +} $answer = '' if ($env:YES -eq '1') { @@ -91,9 +101,9 @@ if ($env:YES -eq '1') { if ($answer -match '^[yY]') { Push-Location $targetRoot try { - & $resqBin dev scaffold-local-hook --kind auto + & $resqBin @scaffoldArgs --kind auto if ($LASTEXITCODE -ne 0) { - Write-Host 'warn scaffold-local-hook failed; run it manually with --kind .' -ForegroundColor Yellow + Write-Host 'warn scaffold-local failed; run it manually with --kind .' -ForegroundColor Yellow } } finally { Pop-Location } } diff --git a/scripts/install-hooks.sh b/scripts/install-hooks.sh index 3f27d17..3d01722 100755 --- a/scripts/install-hooks.sh +++ b/scripts/install-hooks.sh @@ -49,9 +49,17 @@ elif [ -x "$HOME/.cargo/bin/resq" ]; then fi # ── Path 1: use resq when present (preferred — offline, no raw fetch) ─────── +# Prefer the new `hooks install` path; fall back to `dev install-hooks` for +# binaries built before resq-software/crates#60. if [ -n "$RESQ_BIN" ]; then - printf 'info Installing hooks via %s dev install-hooks\n' "$RESQ_BIN" >&2 - (cd "$TARGET_ROOT" && "$RESQ_BIN" dev install-hooks) + if "$RESQ_BIN" hooks install --help >/dev/null 2>&1; then + install_cmd="hooks install" + else + install_cmd="dev install-hooks" + fi + printf 'info Installing hooks via %s %s\n' "$RESQ_BIN" "$install_cmd" >&2 + # shellcheck disable=SC2086 + (cd "$TARGET_ROOT" && "$RESQ_BIN" $install_cmd) else # ── Path 2: fall back to raw fetch from crates templates ──────────────── HOOKS="pre-commit commit-msg prepare-commit-msg pre-push post-checkout post-merge" @@ -82,7 +90,14 @@ fi if [ -f "$HOOKS_DIR/local-pre-push" ] || [ -n "${RESQ_SKIP_LOCAL_SCAFFOLD:-}" ]; then exit 0 fi -if ! "$RESQ_BIN" dev scaffold-local-hook --help >/dev/null 2>&1; then +# Probe for the new `hooks scaffold-local` path first; fall back to the +# legacy `dev scaffold-local-hook` for older binaries. Skip entirely if +# neither is available (very old resq). +if "$RESQ_BIN" hooks scaffold-local --help >/dev/null 2>&1; then + scaffold_cmd="hooks scaffold-local" +elif "$RESQ_BIN" dev scaffold-local-hook --help >/dev/null 2>&1; then + scaffold_cmd="dev scaffold-local-hook" +else exit 0 fi @@ -96,7 +111,8 @@ fi case "$answer" in [yY]|[yY][eE][sS]) - (cd "$TARGET_ROOT" && "$RESQ_BIN" dev scaffold-local-hook --kind auto) \ - || printf 'warn scaffold-local-hook failed; run it manually with --kind .\n' >&2 + # shellcheck disable=SC2086 + (cd "$TARGET_ROOT" && "$RESQ_BIN" $scaffold_cmd --kind auto) \ + || printf 'warn scaffold-local failed; run it manually with --kind .\n' >&2 ;; esac