mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 16:35:33 +00:00
173 lines
No EOL
5.2 KiB
Bash
Executable file
173 lines
No EOL
5.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
#
|
|
# This prepare-commit-msg hook prepares commit messages to ensure
|
|
# that they meet our conventional commit standard.
|
|
#
|
|
# Whenever `git` detects an incoming commit, it will trigger this hook. If the message does not
|
|
# our conventional commit format, it will set it to the following format. (commit and merge only)
|
|
#
|
|
# "type(scope): message"
|
|
# If an issue number is detected at the front of the message and it is not a merge, also adds "___. Fixes #1234"
|
|
#
|
|
# Reference: https://github.com/keymanapp/keyman/wiki/Pull-Request-and-Commit-workflow-notes
|
|
#
|
|
|
|
COMMIT_MSG_FILE="$1"
|
|
COMMIT_SOURCE="$2"
|
|
SHA1="$3"
|
|
|
|
# Get the directory where this script lives, i.e. resources/git-hooks
|
|
HOOK_DIRECTORY="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
|
|
|
|
if [ -f $(dirname "${BASH_SOURCE[0]}")/commit-msg-defs ]; then
|
|
# This script was run from a symlink and the repo in question has its own commit-msg-defs.
|
|
. $(dirname "${BASH_SOURCE[0]}")/commit-msg-defs
|
|
else
|
|
# We'll grab the settings from the keymanapp/keyman repository.
|
|
. "$HOOK_DIRECTORY/../build/jq.inc.sh"
|
|
|
|
# Configurable options for message length.
|
|
min_length=4
|
|
max_length=128
|
|
|
|
# Read from our globally shared JSON-formatted scopes and types
|
|
scopes=( $("$JQ" -r '[.scopes | paths | reduce .[] as $item (""; if . == "" then $item else . + "/" + $item end)] | join(" ")' < "$HOOK_DIRECTORY/../scopes/scopes.json") )
|
|
types=( $("$JQ" -r '.commitTypes | join(" ")' < "$HOOK_DIRECTORY/../scopes/commit-types.json") )
|
|
fi
|
|
|
|
# build the regex pattern based on the config file
|
|
function build_regex() {
|
|
retypes=
|
|
for type in "${types[@]}"
|
|
do
|
|
retypes="${retypes}$type|"
|
|
done
|
|
retypes="(${retypes%?})"
|
|
|
|
rescope=
|
|
for scope in "${scopes[@]}"
|
|
do
|
|
rescope="${rescope}$scope|"
|
|
done
|
|
# %? removes last |
|
|
rescope="(${rescope%?})?"
|
|
|
|
recherry="(cherry-pick\/)?"
|
|
|
|
# Optionally captures an issue number at the front of the branch's name.
|
|
rebranchname="(([[:digit:]]+)\-)?(.+)"
|
|
|
|
regexp="^${retypes}\/${rescope}\/?${recherry}${rebranchname}\s*$"
|
|
}
|
|
|
|
function prepend_scope() {
|
|
# COMMIT_MSG_FILE and $COMMIT_SOURCE are already available.
|
|
|
|
# Step 1: if the branch follows our preferred branch conventions, we can use that!
|
|
branch="$(git rev-parse --abbrev-ref HEAD)"
|
|
build_regex
|
|
|
|
# Perform the actual regex check.
|
|
if [[ $branch =~ $regexp ]]; then
|
|
TYPE="${BASH_REMATCH[1]}"
|
|
SCOPE="${BASH_REMATCH[2]}"
|
|
CHERRYPICK="${BASH_REMATCH[3]}"
|
|
# 4 - just ISSUE, but with an appended '-'. Ashame BASH doesn't support non-capture groups.
|
|
ISSUE="${BASH_REMATCH[5]}"
|
|
NAME="${BASH_REMATCH[6]}"
|
|
|
|
EXTRA_WHITESPACE="\n"
|
|
|
|
# For known merge commits, consider the merge operation to be a 'chore'.
|
|
if [ "${COMMIT_SOURCE}" = "merge" ]; then
|
|
TYPE="chore"
|
|
ISSUE= # Invalidates adding the 'postfix' for these commit types.
|
|
EXTRA_WHITESPACE=
|
|
fi
|
|
|
|
# Build commit message title
|
|
|
|
if [ -z "$SCOPE" ]; then
|
|
prefix="$TYPE: $EXTRA_WHITESPACE"
|
|
else
|
|
prefix="$TYPE($SCOPE): $EXTRA_WHITESPACE"
|
|
fi
|
|
|
|
if [ ! -z "$CHERRYPICK" ]; then
|
|
prefix ="${prefix}🍒"
|
|
fi
|
|
|
|
# Add commit message trailers
|
|
|
|
if [ "${COMMIT_SOURCE}" != "merge" ]; then
|
|
if [ -n "${ISSUE// }" ] || [ ! -z "$CHERRYPICK" ]; then
|
|
prefix="${prefix}\n"
|
|
fi
|
|
|
|
if [ -n "${ISSUE// }" ]; then # Refer to https://unix.stackexchange.com/a/146945 for explanation.
|
|
prefix="${prefix}Fixes: #$ISSUE\n"
|
|
fi
|
|
|
|
if [ ! -z "$CHERRYPICK" ]; then
|
|
prefix="${prefix}Cherry-pick-of: #_ID_"
|
|
fi
|
|
fi
|
|
|
|
postfix=$(
|
|
cat <<EOF
|
|
\n# Keyman Conventional Commit suggestions:
|
|
#
|
|
# - Link to a Sentry issue with git trailer:
|
|
# Fixes: _MODULE_-_ID_
|
|
# - Give credit to co-authors:
|
|
# Co-authored-by: _Name_ <_email_>
|
|
# - Use imperative, present tense ('attach' not 'attaches', 'attached' etc)
|
|
# - Don't include a period at the end of the title
|
|
# - Always include a blank line before trailers
|
|
# - More: https://github.com/keymanapp/keyman/wiki/Pull-Request-and-Commit-workflow-notes
|
|
EOF
|
|
)
|
|
|
|
# Reuse any existing message text, wrapping it in our conventional commit formatting before
|
|
# presenting it to the user for any final edits.
|
|
echo -e "$prefix$(cat $COMMIT_MSG_FILE)$postfix" > $COMMIT_MSG_FILE
|
|
fi
|
|
|
|
# If it doesn't follow our branch-name format, we can't provide any help.
|
|
}
|
|
|
|
#
|
|
# Configurable options for types, scopes and message length.
|
|
#
|
|
|
|
# Step 1 - is the prepared message already in our conventional commits format?
|
|
$HOOK_DIRECTORY/commit-msg -q $COMMIT_MSG_FILE
|
|
isConventional=$? # Grabs the exit code.
|
|
|
|
if [ $isConventional -eq 0 ]; then
|
|
# If so, great! Don't do a thing!
|
|
exit 0
|
|
fi
|
|
|
|
# Debugging aides
|
|
#echo "COMMIT_MSG_FILE=$COMMIT_MSG_FILE"
|
|
#echo "COMMIT_SOURCE=$COMMIT_SOURCE"
|
|
|
|
# The default message is NOT in conventional format. Time to help out with that.
|
|
case $COMMIT_SOURCE in
|
|
squash)
|
|
# We don't help with squashes - this tends to combine two separate messages,
|
|
# and that can be messy.
|
|
exit 0
|
|
;;
|
|
merge|commit|'') # '' = plain `git commit`
|
|
# We can help here!
|
|
prepend_scope
|
|
;;
|
|
*)
|
|
# Default handler - we don't handle these types yet. Might be worth considering.
|
|
# Known possibilities: message (-m), template (-t)
|
|
exit 0
|
|
;;
|
|
esac |