85 lines
2.2 KiB
Python
Executable File
85 lines
2.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Simple commit message validator for git hooks.
|
|
|
|
Usage:
|
|
As git hook: cp validate.py .git/hooks/commit-msg && chmod +x .git/hooks/commit-msg
|
|
Standalone: python validate.py --message "feat: add feature"
|
|
From file: python validate.py commit-msg-file
|
|
"""
|
|
|
|
import sys
|
|
import re
|
|
|
|
|
|
def validate_commit(message):
|
|
"""Validate commit message, return (is_valid, error_message)."""
|
|
|
|
header = message.split('\n')[0]
|
|
|
|
# Special formats are always valid
|
|
if (header.startswith('Merge branch') or
|
|
header.startswith('Revert') or
|
|
header == 'chore: init'):
|
|
return True, None
|
|
|
|
# Standard format
|
|
pattern = (
|
|
r'^(feat|fix|refactor|perf|style|test|docs|build|ops|chore)'
|
|
r'(\([a-z0-9-]+\))?'
|
|
r'!?'
|
|
r': '
|
|
r'.{1,100}$'
|
|
)
|
|
|
|
if not re.match(pattern, header):
|
|
return False, (
|
|
f"Invalid format: {header}\n\n"
|
|
f"Expected: <type>(<scope>): <description>\n"
|
|
f"Example: feat(auth): add login\n\n"
|
|
f"Valid types: feat, fix, refactor, perf, style, test, docs, build, ops, chore"
|
|
)
|
|
|
|
# Additional checks
|
|
desc = header.split(': ', 1)[1]
|
|
|
|
if desc[0].isupper():
|
|
return False, "Description should start with lowercase"
|
|
|
|
if desc.endswith('.'):
|
|
return False, "Description should not end with period"
|
|
|
|
return True, None
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='Validate conventional commits')
|
|
parser.add_argument('file', nargs='?', help='Commit message file')
|
|
parser.add_argument('--message', help='Validate message directly')
|
|
args = parser.parse_args()
|
|
|
|
# Get message
|
|
if args.message:
|
|
message = args.message
|
|
elif args.file:
|
|
with open(args.file) as f:
|
|
message = f.read()
|
|
else:
|
|
message = sys.stdin.read()
|
|
|
|
# Validate
|
|
valid, error = validate_commit(message.strip())
|
|
|
|
if valid:
|
|
print("✓ Valid commit message")
|
|
sys.exit(0)
|
|
else:
|
|
print(f"✗ {error}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|