#!/usr/bin/env bash # Conventional Commits, with the scope naming an epic in plan/. # # The scope is how a commit says which line of work it belongs to, and it is # only useful if it means something: an id that matches no epic is a typo or an # epic somebody forgot to write. Archived epics count - a follow-up fix to # finished work is still that epic's commit. # # scripts/check-commit-scope.sh .git/COMMIT_EDITMSG set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" MSG_FILE="${1:?usage: check-commit-scope.sh }" SUBJECT="$(head -n 1 "$MSG_FILE")" # Fixups and merges are not ours to judge. case "$SUBJECT" in fixup!*|squash!*|Merge*|Revert*) exit 0 ;; esac CONVENTIONAL='^(feat|fix|docs|refactor|perf|test|build|ci|chore)(\([a-z0-9-]+\))?!?: .+' if ! printf '%s' "$SUBJECT" | grep -Eq "$CONVENTIONAL"; then echo "commit subject is not Conventional Commits:" >&2 echo " $SUBJECT" >&2 echo "want: type(scope): summary e.g. feat(features): normalise the basis" >&2 exit 1 fi SCOPE="$(printf '%s' "$SUBJECT" | sed -n 's/^[a-z]*(\([a-z0-9-]*\)).*/\1/p')" [ -n "$SCOPE" ] && [ "$SCOPE" != "plan" ] || exit 0 for dir in "$HERE/plan" "$HERE/plan/complete"; do [ -f "$dir/$SCOPE.md" ] && exit 0 done echo "commit scope '$SCOPE' names no epic in plan/." >&2 echo "known:" >&2 for f in "$HERE"/plan/*.md "$HERE"/plan/complete/*.md; do [ -e "$f" ] || continue b="$(basename "$f" .md)" [ "$b" = "README" ] || echo " $b" >&2 done echo " plan (for the register itself)" >&2 exit 1