Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions .git-hooks/commit-msg
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
#!/usr/bin/env bash
# Copyright 2026 ResQ
# Copyright 2026 ResQ Software
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Canonical ResQ commit-msg shim — source: resq-software/dev.
# Enforces Conventional Commits; blocks fixup/squash/WIP on main/master.

set -euo pipefail

[ -n "${GIT_HOOKS_SKIP:-}" ] && exit 0

# INPUT_FILE stores the path to the commit message file.
INPUT_FILE=${1:-}
# PATTERN is the regular expression for a valid Conventional Commit message.
PATTERN="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .+$"
INPUT_FILE="${1:-}"
PATTERN="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?(!)?: .+$"

# Validate only the first line (subject).
FIRST_LINE=$(head -1 "$INPUT_FILE")
SUBJECT=$(echo "$FIRST_LINE" | sed 's/^\[[A-Z][A-Z]*-[0-9]*\] //')
# Ticket-prefix regex matches what prepare-commit-msg prepends ({2,} chars).
SUBJECT=$(sed -E 's/^\[[A-Z]{2,}-[0-9]+\][[:space:]]*//' <<<"$FIRST_LINE")

if ! echo "$SUBJECT" | grep -qE "$PATTERN"; then
# WIP / fixup! / squash! guard runs *before* the format check so users get
# a branch-specific error message on main/master instead of the generic
# "Invalid commit message format".
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
case "$BRANCH" in
main|master)
if grep -qiE "^(\[[A-Z]{2,}-[0-9]+\] )?(fixup!|squash!|wip[: ])" <<<"$FIRST_LINE"; then
echo "Error: fixup!/squash!/WIP commits are not allowed on $BRANCH."
echo "Create a feature branch instead."
exit 1
fi
;;
esac

if ! grep -qE "$PATTERN" <<<"$SUBJECT"; then
echo "Error: Invalid commit message format."
echo "Expected format: type(scope): subject"
echo "Expected: type(scope)(!): subject"
echo "Examples:"
echo " feat(core): add new feature"
echo " feat!: remove deprecated API (breaking change marker)"
echo " fix(ui): fix button color"
exit 1
fi

# Block fixup!/WIP commits on master
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
if [ "$BRANCH" = "master" ]; then
if echo "$FIRST_LINE" | grep -qiE "^(\[[A-Z]+-[0-9]+\] )?(fixup!|squash!|wip[: ])"; then
echo "Error: fixup!/squash!/WIP commits are not allowed on master."
echo "Create a feature branch instead."
exit 1
fi
LOCAL_HOOK="$(git rev-parse --show-toplevel)/.git-hooks/local-commit-msg"
if [ -x "$LOCAL_HOOK" ]; then
exec "$LOCAL_HOOK" "$@"
fi
30 changes: 30 additions & 0 deletions .git-hooks/local-pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Repo-specific pre-commit for the resq-cli home repo.
#
# The canonical pre-commit delegates to `resq pre-commit` but only looks on
# PATH and ~/.cargo/bin. This repo *ships* resq-cli, so a contributor may be
# working with a workspace-local build at target/{release,debug}/resq and not
# have the binary installed globally. Runs only when PATH + cargo lookup both
# failed — keeps the canonical contract while preserving the old behavior.
set -euo pipefail

# If PATH or ~/.cargo/bin already has resq, the canonical already ran it.
if command -v resq >/dev/null 2>&1 || [ -x "$HOME/.cargo/bin/resq" ]; then
exit 0
fi

PROJECT_ROOT="$(git rev-parse --show-toplevel)"
if [ -x "$PROJECT_ROOT/target/release/resq" ]; then
RESQ_BIN="$PROJECT_ROOT/target/release/resq"
elif [ -x "$PROJECT_ROOT/target/debug/resq" ]; then
RESQ_BIN="$PROJECT_ROOT/target/debug/resq"
elif command -v cargo >/dev/null 2>&1; then
echo "📦 Building resq-cli from workspace for pre-commit checks..."
cargo build -q -p resq-cli
RESQ_BIN="$PROJECT_ROOT/target/debug/resq"
else
echo "⚠️ resq not on PATH and cargo not available. Install it or run from nix develop." >&2
exit 0
fi

exec "$RESQ_BIN" pre-commit --root "$PROJECT_ROOT" "$@"
47 changes: 18 additions & 29 deletions .git-hooks/post-checkout
Original file line number Diff line number Diff line change
@@ -1,41 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
#
# Copyright 2026 ResQ
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# post-checkout
# Copyright 2026 ResQ Software
# SPDX-License-Identifier: Apache-2.0
#
# Automatically notifies when lock files change during branch checkout.
#
# Usage:
# .git-hooks/post-checkout PREV_HEAD NEW_HEAD IS_BRANCH_CHECKOUT
#
# Exit codes:
# 0 Always.
# Canonical ResQ post-checkout shim — source: resq-software/dev.
# Notifies when lock files change so devs know to resync dependencies.

set -euo pipefail

[ -n "${GIT_HOOKS_SKIP:-}" ] && exit 0

PREV_HEAD="${1:-}"
NEW_HEAD="${2:-}"
IS_BRANCH_CHECKOUT="${3:-}"

if [ "$IS_BRANCH_CHECKOUT" != "1" ]; then exit 0; fi
if [ "$PREV_HEAD" = "$NEW_HEAD" ]; then exit 0; fi
if [ "$IS_BRANCH_CHECKOUT" = "1" ] && [ "$PREV_HEAD" != "$NEW_HEAD" ]; then
CHANGED=$(git diff --name-only "$PREV_HEAD" "$NEW_HEAD" 2>/dev/null || true)

CHANGED=$(git diff --name-only "$PREV_HEAD" "$NEW_HEAD" 2>/dev/null)
grep -q "^Cargo\.lock$" <<<"$CHANGED" && echo "📦 Cargo.lock changed — run: cargo build"
grep -q "^bun\.lockb\?$" <<<"$CHANGED" && echo "📦 bun.lock changed — run: bun install"
grep -q "^uv\.lock$" <<<"$CHANGED" && echo "📦 uv.lock changed — run: uv sync"
grep -q "^flake\.lock$" <<<"$CHANGED" && echo "📦 flake.lock changed — exit and re-enter: nix develop"
fi

if echo "$CHANGED" | grep -q "^Cargo\.lock$"; then
echo "📦 Cargo.lock changed — dependencies will update on next build"
LOCAL_HOOK="$(git rev-parse --show-toplevel)/.git-hooks/local-post-checkout"
if [ -x "$LOCAL_HOOK" ]; then
exec "$LOCAL_HOOK" "$@"
fi

exit 0
44 changes: 17 additions & 27 deletions .git-hooks/post-merge
Original file line number Diff line number Diff line change
@@ -1,34 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
#
# Copyright 2026 ResQ
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# post-merge
# Copyright 2026 ResQ Software
# SPDX-License-Identifier: Apache-2.0
#
# Automatically notifies when lock files change after a merge.
#
# Usage:
# .git-hooks/post-merge
#
# Exit codes:
# 0 Always.
# Canonical ResQ post-merge shim — source: resq-software/dev.
# Notifies when lock files change after a merge.

set -euo pipefail

[ -n "${GIT_HOOKS_SKIP:-}" ] && exit 0

CHANGED_FILES=$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD 2>/dev/null)
CHANGED=$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD 2>/dev/null || true)

grep -q "^Cargo\.lock$" <<<"$CHANGED" && echo "📦 Cargo.lock changed after merge — run: cargo build"
grep -q "^bun\.lockb\?$" <<<"$CHANGED" && echo "📦 bun.lock changed after merge — run: bun install"
grep -q "^uv\.lock$" <<<"$CHANGED" && echo "📦 uv.lock changed after merge — run: uv sync"
grep -q "^flake\.lock$" <<<"$CHANGED" && echo "📦 flake.lock changed after merge — exit and re-enter: nix develop"

if echo "$CHANGED_FILES" | grep -q "^Cargo\.lock$"; then
echo "📦 Cargo.lock changed after merge — dependencies will update on next build"
LOCAL_HOOK="$(git rev-parse --show-toplevel)/.git-hooks/local-post-merge"
if [ -x "$LOCAL_HOOK" ]; then
exec "$LOCAL_HOOK" "$@"
fi

exit 0
57 changes: 22 additions & 35 deletions .git-hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,55 +1,42 @@
#!/usr/bin/env bash
# Copyright 2026 ResQ
# Copyright 2026 ResQ Software
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Canonical ResQ pre-commit shim — source: resq-software/dev.
# Delegates all logic to `resq pre-commit`; runs `.git-hooks/local-pre-commit`
# as a repo-specific escape hatch if present and executable.

set -euo pipefail

# HOOK_PATH stores the absolute path to this script.
if [ ${#BASH_SOURCE[@]} -gt 0 ] && [ -n "${BASH_SOURCE[0]:-}" ]; then
HOOK_PATH="${BASH_SOURCE[0]}"
else
HOOK_PATH="$0"
fi

# Resolve symlinks
HOOK_PATH=$(readlink -f "$HOOK_PATH")
# SCRIPT_DIR stores the absolute path to the hooks directory.
SCRIPT_DIR=$(dirname "$HOOK_PATH")
# PROJECT_ROOT stores the absolute path to the project root.
PROJECT_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)

[ -n "${GIT_HOOKS_SKIP:-}" ] && exit 0

# ── Resolve resq CLI binary ─────────────────────────────────────────────────
PROJECT_ROOT="$(git rev-parse --show-toplevel)"

# ── Resolve resq binary ─────────────────────────────────────────────────────
# PATH first (covers nix develop + ~/.cargo/bin on PATH). Soft-skip otherwise
# so a missing backend never blocks a commit silently — the user sees a hint.
RESQ_BIN=""
if command -v resq >/dev/null 2>&1; then
RESQ_BIN="resq"
elif [ -f "$PROJECT_ROOT/target/release/resq" ]; then
RESQ_BIN="$PROJECT_ROOT/target/release/resq"
elif [ -f "$PROJECT_ROOT/target/debug/resq" ]; then
RESQ_BIN="$PROJECT_ROOT/target/debug/resq"
elif command -v cargo >/dev/null 2>&1; then
echo "📦 Building resq CLI for pre-commit checks..."
cargo build -q -p resq-cli
RESQ_BIN="$PROJECT_ROOT/target/debug/resq"
elif [ -x "$HOME/.cargo/bin/resq" ]; then
RESQ_BIN="$HOME/.cargo/bin/resq"
fi

if [ -z "$RESQ_BIN" ]; then
echo "⚠️ ResQ CLI not found and cannot be built. Skipping pre-commit checks."
exit 0
if [ -n "$RESQ_BIN" ]; then
"$RESQ_BIN" pre-commit --root "$PROJECT_ROOT" "$@"
else
echo "⚠️ resq not found — skipping ResQ pre-commit checks."
echo " Install: enter 'nix develop', or run:"
echo " cargo install --git https://github.com/resq-software/crates resq-cli"
fi

# ── Delegate to Rust CLI ─────────────────────────────────────────────────────
# This runs: copyright, secrets, audit, formatting, and versioning prompt
exec "$RESQ_BIN" pre-commit --root "$PROJECT_ROOT" "$@"
# ── Local override ──────────────────────────────────────────────────────────
LOCAL_HOOK="$PROJECT_ROOT/.git-hooks/local-pre-commit"
if [ -x "$LOCAL_HOOK" ]; then
exec "$LOCAL_HOOK" "$@"
fi
Loading
Loading