Skip to content

Instantly share code, notes, and snippets.

@nyxkrage
Last active August 18, 2023 12:44
Show Gist options
  • Save nyxkrage/c3e97636cfd3f2fc6f823d68c9465722 to your computer and use it in GitHub Desktop.
Save nyxkrage/c3e97636cfd3f2fc6f823d68c9465722 to your computer and use it in GitHub Desktop.
Conventional Commit Helper

Install

Make sure charmbracelet/gum and fzf are installed

mkdir ~/.local/share/bin
curl "https://gist.githubusercontent.com/nyxkrage/c3e97636cfd3f2fc6f823d68c9465722/raw/7c8d1f3b90b69af12009b754cbec35dde63a95c0/convcommit.bash" -o ~/.local/share/bin/convcommit
chmod +x ~/.local/share/bin/convcommit
# cc for convetional commit
git config --global alias.cc "-c core.editor=\"$HOME/.local/share/bin/convcommit\" commit"

Showcase

Choosing the type of commit image
Choosing the scope of the commit, this is based on the previously used scopes for the project, but allows you to also just type a new one
image
Typing the commit header, this restricts you to 80 characters, to keep the title short, and encourages the use of the body of the commit message
image
Typing the commit message, this will automatically wrap lines for you at 110 characters once you submit
image

#!/usr/bin/env bash
set -eumo pipefail
IFS=$'\n\t'
OUTPUT=/dev/stdout
if [ $# -gt 0 ]; then
OUTPUT=$1
fi
declare -a DEPENDENCIES
declare -a MISSING_DEPENDECIES
declare -a TYPES
declare -a SCOPES
DEPENDENCIES=("gum" "git" "fzf")
MISSING_DEPENDECIES=()
TYPES=(
$'build\tChange that affect the build system or external dependencies'
$'ci\tChange to CI configuration files and scripts'
$'docs\tDocumentation only changes'
$'feat\tNew feature'
$'fix\tBug fix'
$'perf\tCode change that improves performance'
$'refactor\tCode change that neither fixes a bug nor adds a feature'
$'test\tAdding missing tests or correcting existing tests'
)
mapfile -t SCOPES < <(git log --pretty=format:%s | cut -d':' -f1 | sed -e '/^[^()]*$/d' -e 's/.*(\(.*\)).*/\1/')
SCOPES+=("NONE")
for DEPENDENCY in "${DEPENDENCIES[@]}"; do
if ! command -v "$DEPENDENCY" &>/dev/null; then
MISSING_DEPENDECIES+=("$DEPENDENCY")
fi
done
if [ ${#MISSING_DEPENDECIES[@]} -gt 0 ]; then
echo "The following dependencies are missing: ${MISSING_DEPENDECIES[*]}" >&2
exit 126
fi
# COMMIT TYPE
# Only allow the specified types
echo -e "\rSelect a type of change: " >&2
TYPE=$(echo "${TYPES[*]}" | tr -s ' ' | fzf --height=10 --delimiter='\t' --with-nth=1 --preview="cut -d$'\t' -f2- <<<{}" --preview-window='right,90%' | cut -d$'\t' -f1)
printf "\x1b[1ASelect a type of change: %s\n" "$TYPE" >&2
# COMMIT SCOPE
# Use previous previously defined scopes or create new if it isn't an already used scope
echo "Select the scope of the change:" >&2
set +o pipefail # fzf will exit with exit code 1 if no matches, we dont want to stop the program in that case
SCOPE=$(echo "${SCOPES[*]}" | tr -s ' ' | fzf --height=10 --print-query --reverse | tail -1)
set -o pipefail
printf "\x1b[1ASelect the scope of the change: %s\n" "$SCOPE" >&2
if [[ "$SCOPE" != "NONE" ]]; then
SCOPE="($SCOPE)"
else
SCOPE=""
fi
BREAK=""
if gum confirm "Is this a breaking change?"; then
BREAK="!"
fi
# COMMIT TITLE
TITLE=$(gum input --char-limit=80 --width=110 --prompt="${TYPE}${SCOPE}${BREAK}: " --placeholder="short summary of the change")
TITLE="${TYPE}${SCOPE}${BREAK}: $TITLE"
# COMMIT MESSAGE
BODY=$(gum write --char-limit=0 --width=0 --height=10 --placeholder="Detailed summary of the change (CTRL+D to finish)" | fold -w 110 -s)
if [[ -n "$BODY" ]]; then
SEP=$'\n\n'
BODY="${SEP}${BODY}"
fi
printf "%s%s\n" "$TITLE" "$BODY" >"$OUTPUT"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment