Skip to content

Merge dev into main

Merge dev into main #9

Workflow file for this run

name: Custom Commit Message Validator
on:
pull_request:
types: [opened, reopened, synchronize] # Triggers on PR open, reopen, or new commits pushed to the PR branch
jobs:
validate_commit_messages:
runs-on: ubuntu-latest # Specifies the runner environment
permissions:
contents: read # Needed for checkout
pull-requests: read # Needed to fetch PR details and commit SHAs
steps:
- name: Checkout Repository
uses: actions/checkout@v4 # Action to checkout your repository
with:
fetch-depth: 0 # Fetches the entire history for git log operations. Crucial for retrieving full commit messages.
- name: Get PR Commits SHAs
id: get_pr_commits
run: |
# Use GitHub CLI to get all commit SHAs associated with the current Pull Request.
# This robustly gets the commits that are part of the PR's changes.
# The jq filter extracts the 'oid' (Object ID, which is the SHA) of each commit.
PR_COMMIT_SHAS=$(gh pr view ${{ github.event.pull_request.number }} --json commits --jq '[.commits[].oid] | join(" ")')
# Output the SHAs as a space-separated string.
# This variable will be accessible via steps.get_pr_commits.outputs.PR_COMMITS_LIST
echo "PR_COMMITS_LIST=${PR_COMMIT_SHAS}" >> "$GITHUB_OUTPUT"
env:
# GITHUB_TOKEN is automatically provided by GitHub Actions with sufficient permissions.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Validate Each Commit Message
run: |
# Read the space-separated list of commit SHAs into a Bash array.
# IFS (Internal Field Separator) is set to space to split the string correctly.
IFS=' ' read -r -a commit_shas <<< "${{ steps.get_pr_commits.outputs.PR_COMMITS_LIST }}"
# Initialize a flag to track if any commit fails validation.
has_validation_failed=false
# Loop through each commit SHA.
for commit_sha in "${commit_shas[@]}"; do
echo "--- Checking commit: ${commit_sha} ---"
# Get the full commit message (subject + body).
# --format=%B gets the raw body (subject and body).
# -n 1 limits to the latest commit for the given SHA.
commit_message=$(git log --format=%B -n 1 "${commit_sha}")
echo "Message content:"
echo "${commit_message}"
echo "" # Add a newline for readability
# --- Validation Logic ---
# 1. Check for Conventional Commit format (type(scope)!: subject)
# This regex allows for an optional scope and an optional breaking change marker (!).
# Common types: feat, fix, docs, chore, style, refactor, perf, test, build, ci, revert
if [[ ! "${commit_message}" =~ ^(feat|fix|docs|chore|style|refactor|perf|test|build|ci|revert)(\([a-zA-Z0-9_-]+\))?(!?): ]]; then
echo "::error file=COMMIT_MESSAGE::Commit ${commit_sha} does not start with a conventional commit type (e.g., 'feat:', 'fix:'). Message: '${commit_message}'"
has_validation_failed=true
continue # Move to the next commit
fi
# Extract the first line (subject) for further checks.
commit_subject=$(echo "${commit_message}" | head -n 1)
# 2. Check subject line length (e.g., max 72 characters)
if [[ ${#commit_subject} -gt 72 ]]; then
echo "::warning file=COMMIT_MESSAGE::Commit ${commit_sha} subject line exceeds 72 characters. Length: ${#commit_subject}. Subject: '${commit_subject}'"
# This is a warning, not a failure, adjust as needed.
fi
# 3. Check for empty line between subject and body (if body exists)
# Check if there's more than just the subject line.
if [[ $(echo "${commit_message}" | wc -l) -gt 1 ]]; then
# Check if the second line is empty.
# Use awk to get the second line and trim whitespace.
second_line=$(echo "${commit_message}" | awk 'NR==2 {print}' | xargs)
if [[ -n "${second_line}" ]]; then # If the second line is NOT empty
echo "::error file=COMMIT_MESSAGE::Commit ${commit_sha} is missing an empty line between the subject and body. Message: '${commit_message}'"
has_validation_failed=true
continue
fi
fi
# Add more custom validation rules here as needed:
# - Subject capitalization (e.g., must be lowercase)
# - Subject must not end with a period
# - Body line length limits
# - Required body content for certain types (e.g., 'fix:' requires a 'Fixes #ISSUE' line)
done # End of commit loop
# If any commit failed validation, exit with a non-zero status to fail the job.
if [ "$has_validation_failed" = true ]; then
echo "::error::One or more commit messages failed validation. Please review the errors above."
exit 1
fi
echo "All commit messages in the PR passed validation."