Skip to content

Commit 0ee3ef3

Browse files
committed
ci: add rule for pr commit message
1 parent 58e502e commit 0ee3ef3

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

.github/workflows/commit.yml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: Custom Commit Message Validator
2+
3+
on:
4+
pull_request:
5+
types: [opened, reopened, synchronize] # Triggers on PR open, reopen, or new commits pushed to the PR branch
6+
7+
jobs:
8+
validate_commit_messages:
9+
runs-on: ubuntu-latest # Specifies the runner environment
10+
11+
permissions:
12+
contents: read # Needed for checkout
13+
pull-requests: read # Needed to fetch PR details and commit SHAs
14+
15+
steps:
16+
- name: Checkout Repository
17+
uses: actions/checkout@v4 # Action to checkout your repository
18+
with:
19+
fetch-depth: 0 # Fetches the entire history for git log operations. Crucial for retrieving full commit messages.
20+
21+
- name: Get PR Commits SHAs
22+
id: get_pr_commits
23+
run: |
24+
# Use GitHub CLI to get all commit SHAs associated with the current Pull Request.
25+
# This robustly gets the commits that are part of the PR's changes.
26+
# The jq filter extracts the 'oid' (Object ID, which is the SHA) of each commit.
27+
PR_COMMIT_SHAS=$(gh pr view ${{ github.event.pull_request.number }} --json commits --jq '.commits[].oid')
28+
29+
# Output the SHAs as a space-separated string.
30+
# This variable will be accessible via steps.get_pr_commits.outputs.PR_COMMITS_LIST
31+
echo "PR_COMMITS_LIST=${PR_COMMIT_SHAS}" >> "$GITHUB_OUTPUT"
32+
env:
33+
# GITHUB_TOKEN is automatically provided by GitHub Actions with sufficient permissions.
34+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35+
36+
- name: Validate Each Commit Message
37+
run: |
38+
# Read the space-separated list of commit SHAs into a Bash array.
39+
# IFS (Internal Field Separator) is set to space to split the string correctly.
40+
IFS=' ' read -r -a commit_shas <<< "${{ steps.get_pr_commits.outputs.PR_COMMITS_LIST }}"
41+
42+
# Initialize a flag to track if any commit fails validation.
43+
has_validation_failed=false
44+
45+
# Loop through each commit SHA.
46+
for commit_sha in "${commit_shas[@]}"; do
47+
echo "--- Checking commit: ${commit_sha} ---"
48+
49+
# Get the full commit message (subject + body).
50+
# --format=%B gets the raw body (subject and body).
51+
# -n 1 limits to the latest commit for the given SHA.
52+
commit_message=$(git log --format=%B -n 1 "${commit_sha}")
53+
echo "Message content:"
54+
echo "${commit_message}"
55+
echo "" # Add a newline for readability
56+
57+
# --- Validation Logic ---
58+
59+
# 1. Check for Conventional Commit format (type(scope)!: subject)
60+
# This regex allows for an optional scope and an optional breaking change marker (!).
61+
# Common types: feat, fix, docs, chore, style, refactor, perf, test, build, ci, revert
62+
if [[ ! "${commit_message}" =~ ^(feat|fix|docs|chore|style|refactor|perf|test|build|ci|revert)(\([a-zA-Z0-9_-]+\))?(!?): ]]; then
63+
echo "::error file=COMMIT_MESSAGE::Commit ${commit_sha} does not start with a conventional commit type (e.g., 'feat:', 'fix:'). Message: '${commit_message}'"
64+
has_validation_failed=true
65+
continue # Move to the next commit
66+
fi
67+
68+
# Extract the first line (subject) for further checks.
69+
commit_subject=$(echo "${commit_message}" | head -n 1)
70+
71+
# 2. Check subject line length (e.g., max 72 characters)
72+
if [[ ${#commit_subject} -gt 72 ]]; then
73+
echo "::warning file=COMMIT_MESSAGE::Commit ${commit_sha} subject line exceeds 72 characters. Length: ${#commit_subject}. Subject: '${commit_subject}'"
74+
# This is a warning, not a failure, adjust as needed.
75+
fi
76+
77+
# 3. Check for empty line between subject and body (if body exists)
78+
# Check if there's more than just the subject line.
79+
if [[ $(echo "${commit_message}" | wc -l) -gt 1 ]]; then
80+
# Check if the second line is empty.
81+
# Use awk to get the second line and trim whitespace.
82+
second_line=$(echo "${commit_message}" | awk 'NR==2 {print}' | xargs)
83+
if [[ -n "${second_line}" ]]; then # If the second line is NOT empty
84+
echo "::error file=COMMIT_MESSAGE::Commit ${commit_sha} is missing an empty line between the subject and body. Message: '${commit_message}'"
85+
has_validation_failed=true
86+
continue
87+
fi
88+
fi
89+
90+
# Add more custom validation rules here as needed:
91+
# - Subject capitalization (e.g., must be lowercase)
92+
# - Subject must not end with a period
93+
# - Body line length limits
94+
# - Required body content for certain types (e.g., 'fix:' requires a 'Fixes #ISSUE' line)
95+
96+
done # End of commit loop
97+
98+
# If any commit failed validation, exit with a non-zero status to fail the job.
99+
if [ "$has_validation_failed" = true ]; then
100+
echo "::error::One or more commit messages failed validation. Please review the errors above."
101+
exit 1
102+
fi
103+
104+
echo "All commit messages in the PR passed validation."

0 commit comments

Comments
 (0)