diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d884d055..42d2d582 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,6 +2,8 @@ # templates/flow-v2-shared/sync-tools.sh. exclude: ^templates/flow-v2-shared/tools/ +default_install_hook_types: [pre-commit, commit-msg] + repos: # Ruff for formatting and linting - repo: https://github.com/astral-sh/ruff-pre-commit @@ -41,3 +43,12 @@ repos: types: [python] pass_filenames: false stages: [manual] # Run only with: pre-commit run --hook-stage manual pyright + + # Mirrors .github/workflows/conventional-commits.yml's commit-message check. + # (The PR-title half of that workflow can't be checked locally — GitHub + # only knows the PR title once the PR exists.) + - id: conventional-commit-msg + name: conventional commit message + entry: scripts/check_commit_msg.sh + language: script + stages: [commit-msg] diff --git a/scripts/check_commit_msg.sh b/scripts/check_commit_msg.sh new file mode 100755 index 00000000..be0a8fc6 --- /dev/null +++ b/scripts/check_commit_msg.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Enforces Conventional Commits locally, mirroring .github/workflows/conventional-commits.yml. +set -euo pipefail + +MSG_FILE="$1" +MSG="$(head -n1 "$MSG_FILE")" +PATTERN='^(feat|fix|refactor|docs|style|test|chore|perf|ci|build|revert)(\([a-z0-9._/-]+\))?(!)?: .+' + +if echo "$MSG" | grep -Eq '^Merge (pull request|branch|remote-tracking)'; then + exit 0 +fi + +if ! echo "$MSG" | grep -Eq "$PATTERN"; then + echo "ERROR: commit message does not follow Conventional Commits." + echo " Got: $MSG" + echo " Expected: [optional scope]: " + echo " Types: feat | fix | refactor | docs | style | test | chore | perf | ci | build | revert" + echo " Examples: feat(sandbox): add preservation mode" + echo " fix: handle empty dataset gracefully" + exit 1 +fi