scripts/verify-commits.sh

169 lines
4.6 KiB
Bash

#!/bin/bash
# Semantic commit pattern:
# feat|fix|docs|style|refactor|test|perf|build|ci|chore|revert(optional scope): message
# Allow dots (.), uppercase letters, and other common filename characters in scope
PATTERN='^(feat|fix|docs|style|refactor|test|perf|build|ci|chore|revert)(\([a-zA-Z0-9\-\.\_]+\))?: .{1,}$'
# Pattern for merge commits from pull requests - simplified to catch all formats
PR_MERGE_PATTERN='^Merge pull request'
# Pattern for standard Git merge commits
GIT_MERGE_PATTERN='^Merge (branch|remote-tracking branch) .+ into .+$'
# Counters for reporting
TOTAL_COMMITS=0
VALID_COMMITS=0
INVALID_COMMITS=0
MERGE_COMMITS=0
# Check if colors should be used
USE_COLORS=1
if [ "$1" = "--no-color" ] || [ "$1" = "-n" ]; then
USE_COLORS=0
fi
# Define color functions that check if colors should be used
color() {
if [ $USE_COLORS -eq 1 ] && [ -t 1 ]; then
case "$1" in
"red") echo -en "\033[0;31m" ;;
"green") echo -en "\033[0;32m" ;;
"yellow") echo -en "\033[0;33m" ;;
"blue") echo -en "\033[0;34m" ;;
"reset") echo -en "\033[0m" ;;
esac
fi
}
# Function to check if current directory is a Git repository
check_git_repo() {
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
color "red"
echo "Error: This is not a Git repository"
color "reset"
exit 1
fi
}
# Ask for commit range or use default
get_commit_range() {
color "yellow"
echo "Enter commit range to check (default: all commits):"
color "reset"
echo "Examples:"
echo " - HEAD~10..HEAD (last 10 commits)"
echo " - main..feature (commits in feature branch not in main)"
echo " - Press Enter for all commits"
read -r RANGE
if [ -z "$RANGE" ]; then
RANGE="--all"
fi
color "yellow"
echo "Checking commits in range: "
color "reset"
echo "$RANGE"
}
# Initialize
check_git_repo
get_commit_range
# Get all commit hashes
if [ "$RANGE" = "--all" ]; then
COMMITS=$(git log --format="%H")
else
COMMITS=$(git log "$RANGE" --format="%H")
fi
# Check each commit message
for COMMIT in $COMMITS; do
TOTAL_COMMITS=$((TOTAL_COMMITS + 1))
COMMIT_MSG=$(git log --format=%B -n 1 "$COMMIT")
COMMIT_SUBJECT=$(echo "$COMMIT_MSG" | head -n 1)
COMMIT_DATE=$(git show -s --format=%ci "$COMMIT")
COMMIT_AUTHOR=$(git show -s --format="%an <%ae>" "$COMMIT")
PARENT_COUNT=$(git rev-list --parents -n 1 "$COMMIT" | awk '{print NF - 1}')
echo
color "yellow"
echo -n "Commit: "
color "reset"
echo "${COMMIT:0:8} - $COMMIT_DATE - $COMMIT_AUTHOR"
echo " $COMMIT_SUBJECT"
# Check if it's a merge commit
if [ $PARENT_COUNT -gt 1 ] && (echo "$COMMIT_SUBJECT" | grep -E "$PR_MERGE_PATTERN" >/dev/null || echo "$COMMIT_SUBJECT" | grep -E "$GIT_MERGE_PATTERN" >/dev/null); then
color "blue"
echo -n " ➜ Merge commit (exempt from validation)"
color "reset"
echo
MERGE_COMMITS=$((MERGE_COMMITS + 1))
continue
fi
# Validate against semantic pattern
if echo "$COMMIT_SUBJECT" | grep -E "$PATTERN" >/dev/null; then
color "green"
echo -n " ✓ Valid semantic commit"
color "reset"
echo
VALID_COMMITS=$((VALID_COMMITS + 1))
else
color "red"
echo -n " ✗ Invalid commit message"
color "reset"
echo
color "yellow"
echo -n " Should follow pattern:"
color "reset"
echo " type(scope): message"
INVALID_COMMITS=$((INVALID_COMMITS + 1))
fi
done
# Print summary
echo
color "yellow"
echo "========== Validation Summary =========="
color "reset"
echo "Total commits checked: ${TOTAL_COMMITS}"
color "green"
echo -n "Valid semantic commits: ${VALID_COMMITS}"
color "reset"
echo
color "blue"
echo -n "Merge commits (exempted): ${MERGE_COMMITS}"
color "reset"
echo
color "red"
echo -n "Invalid semantic commits: ${INVALID_COMMITS}"
color "reset"
echo
# Print percentage
if [ $TOTAL_COMMITS -gt 0 ]; then
VALID_PERCENT=$((($VALID_COMMITS + $MERGE_COMMITS) * 100 / $TOTAL_COMMITS))
echo "Compliance rate: ${VALID_PERCENT}%"
if [ $INVALID_COMMITS -gt 0 ]; then
echo
color "yellow"
echo "Recommendations:"
color "reset"
echo "- Review invalid commits and consider amending them for consistency"
echo "- Run this script periodically to ensure commit message standards"
else
echo
color "green"
echo "All commits comply with semantic commit standards!"
color "reset"
fi
fi
exit 0