hledger/tools/commitlint

139 lines
4.5 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# commitlint [GITRANGE|FILE]
# Check unpushed commits, or commits in the specified range, or a
# commit message in FILE, for compliance with hledger's conventions
# (https://hledger.org/CONTRIBUTING.html#commit-messages). If the
# argument contains - or .. or ^! it's a range, otherwise a file
# containing a proposed commit message.
# Run interactively, or symlink as .git/hooks/commit-msg to check
# your messages before commit.
#
# Examples:
# commitlint foo # commit message in ./foo
# commitlint HEAD^! # last commit
# commitlint d5d19f841^! # this commit
# commitlint -20 # last 20 commits
# commitlint master.. # commits in this branch
# commitlint 1.21..1.22 | grep -F [FAIL] # bad commits in 1.22 release cycle
# commitlint # unpushed commits
set -e
if [[ -n ${NO_COLOR+set} ]]
then
RED=""
GRN=""
NRM=""
else
RED="\033[31m"
GRN="\033[32m"
NRM="\033[0m"
fi
# checkcommit GITHASH - check this commit's message and print result
function checkcommit()
{
HASH=${1}
MSG=$(git log -1 "$HASH" --pretty=format:"%s%n%b")
checkmsg "$MSG"
}
# checkmsg MSG [GITHASH] - check this commit message and print result
function checkmsg()
{
MSG=${1}
HASH=${2}
if [[ -n $HASH ]]
then
HASH="$HASH "
fi
SUMMARY=$(echo "$MSG" | head -1)
FMT="%s%-60s %b${NRM}\n"
# Is this some boring commit, eg a hard-to avoid github merge commit ?
# Ignore those.
if echo "$SUMMARY" | grep -qE '^Merge'
then
# shellcheck disable=SC2059
printf "$FMT" "$HASH" "$SUMMARY" "[ignored]"
# Does the summary follow convention ?
# [;]type[!]: [topic: [subtopic: ...]] subject
# spaces after ; and ! and : are optional (also before, but that should be discouraged)
# the type prefix is required and must be all word characters
# there can be zero or more topic prefixes of increasing depth
# a topic prefix must begin with a word character, can contain spaces/slashes/commas
# (so potentially multiple topic labels, eg "imp: bs, cf, is: cli/doc: blah blah")
elif ! echo "$SUMMARY" | grep -qE '^( *; *)?\w+( *!)? *: *(\w[\w,/ ]* *: *)*'
then
# shellcheck disable=SC2059
printf "$FMT" "$HASH" "$SUMMARY" "${RED}[FAIL]"
STATUS=1
# Looks like a good commit.
else
# shellcheck disable=SC2059
printf "$FMT" "$HASH" "$SUMMARY" "${GRN}[ok]"
fi
}
STATUS=
RANGE=${*:-"@{u}.."} # @{u} = upstream
if [[ $RANGE =~ (-|\.\.|\^!) ]]
then
HASHES=$(git log --abbrev-commit --pretty=format:%h "$RANGE")
for HASH in $HASHES; do checkcommit "$HASH"; done
else
MSG=$(cat "$1")
checkmsg "$MSG"
fi
if [[ -z $STATUS ]]
then
printf "" # "${GRN}Ok${NRM}\n"
else
# shellcheck disable=SC2059
printf "\n${RED}Commit(s) not in preferred style.${NRM}\n"
cat <<EOF
---------------------------------------------------------------------------
Commit messages should follow this format:
[;]type[!]: [topic:] summary
[Optional description, ready for release notes/changelog.]
Explanation:
The subject line should have a type: prefix. Common types:
feat imp fix - end-user changes (->release notes & changelogs)
cha pkg lib - packager/builder/lib-user changes (->changelogs)
dev doc test ci ref cln - developer changes (->just commit log, mostly)
It can additionally have a topic prefix (and optionally subtopics), such as:
bin examples install cli ui web print reg bal bs balcmds journal csv ...
(see https://hledger.org/CONTRIBUTING.html#open-issues -> COMPONENT)
Space, comma and slash are also tolerated inside topics for now. Eg:
imp: bs, cf, is: cli/doc: blah blah blah
Mention any related issues, usually parenthesised at end of summary: (#1234)
! indicates a breaking change.
; skips expensive CI tests.
These conventions are evolving. In practice, any type or topic will do.
Use your best judgement and we'll polish during code review.
More context: https://hledger.org/CONTRIBUTING.html#commit-messages
You can set up this script to check your commit messages locally:
1. before committing:
a. safer but must redo: cp tools/commitlint .git/hooks/commit-msg
b. more convenient: ln -s ../../tools/commitlint .git/hooks/commit-msg
2. before pushing: tools/commitlint && git push
---------------------------------------------------------------------------
EOF
fi
exit 0"$STATUS"