feat(projman): implement 8 improvement issues (#231-#238) #239
102
plugins/git-flow/hooks/branch-check.sh
Executable file
102
plugins/git-flow/hooks/branch-check.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/bin/bash
|
||||
# git-flow branch name validation hook
|
||||
# Validates branch names follow the convention: <type>/<description>
|
||||
# Command hook - guaranteed predictable behavior
|
||||
|
||||
# Read tool input from stdin (JSON format)
|
||||
INPUT=$(cat)
|
||||
|
||||
# Extract command from JSON input
|
||||
# The Bash tool sends {"command": "..."} format
|
||||
COMMAND=$(echo "$INPUT" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
||||
|
||||
# If no command found, exit silently (allow)
|
||||
if [ -z "$COMMAND" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if this is a branch creation command
|
||||
# Patterns: git checkout -b, git branch (without -d/-D), git switch -c/-C
|
||||
IS_BRANCH_CREATE=false
|
||||
BRANCH_NAME=""
|
||||
|
||||
# git checkout -b <branch>
|
||||
if echo "$COMMAND" | grep -qE 'git\s+checkout\s+(-b|--branch)\s+'; then
|
||||
IS_BRANCH_CREATE=true
|
||||
BRANCH_NAME=$(echo "$COMMAND" | sed -n 's/.*git\s\+checkout\s\+\(-b\|--branch\)\s\+\([^ ]*\).*/\2/p')
|
||||
fi
|
||||
|
||||
# git switch -c/-C <branch>
|
||||
if echo "$COMMAND" | grep -qE 'git\s+switch\s+(-c|-C|--create|--force-create)\s+'; then
|
||||
IS_BRANCH_CREATE=true
|
||||
BRANCH_NAME=$(echo "$COMMAND" | sed -n 's/.*git\s\+switch\s\+\(-c\|-C\|--create\|--force-create\)\s\+\([^ ]*\).*/\2/p')
|
||||
fi
|
||||
|
||||
# git branch <name> (without -d/-D/-m/-M which are delete/rename)
|
||||
if echo "$COMMAND" | grep -qE 'git\s+branch\s+[^-]' && ! echo "$COMMAND" | grep -qE 'git\s+branch\s+(-d|-D|-m|-M|--delete|--move|--list|--show-current)'; then
|
||||
IS_BRANCH_CREATE=true
|
||||
BRANCH_NAME=$(echo "$COMMAND" | sed -n 's/.*git\s\+branch\s\+\([^ -][^ ]*\).*/\1/p')
|
||||
fi
|
||||
|
||||
# If not a branch creation command, exit silently (allow)
|
||||
if [ "$IS_BRANCH_CREATE" = false ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If we couldn't extract the branch name, exit silently (allow)
|
||||
if [ -z "$BRANCH_NAME" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Remove any quotes from branch name
|
||||
BRANCH_NAME=$(echo "$BRANCH_NAME" | tr -d '"' | tr -d "'")
|
||||
|
||||
# Skip validation for special branches
|
||||
case "$BRANCH_NAME" in
|
||||
main|master|develop|development|staging|release|hotfix)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Allowed branch types
|
||||
VALID_TYPES="feat|fix|chore|docs|refactor|test|perf|debug"
|
||||
|
||||
# Validate branch name format: <type>/<description>
|
||||
# Description: lowercase letters, numbers, hyphens only, max 50 chars total
|
||||
if ! echo "$BRANCH_NAME" | grep -qE "^($VALID_TYPES)/[a-z0-9][a-z0-9-]*$"; then
|
||||
echo ""
|
||||
echo "[git-flow] Branch name validation failed"
|
||||
echo ""
|
||||
echo "Branch: $BRANCH_NAME"
|
||||
echo ""
|
||||
echo "Expected format: <type>/<description>"
|
||||
echo ""
|
||||
echo "Valid types: feat, fix, chore, docs, refactor, test, perf, debug"
|
||||
echo ""
|
||||
echo "Description rules:"
|
||||
echo " - Lowercase letters, numbers, and hyphens only"
|
||||
echo " - Must start with letter or number"
|
||||
echo " - No spaces or special characters"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " feat/add-user-auth"
|
||||
echo " fix/login-timeout"
|
||||
echo " chore/update-deps"
|
||||
echo " docs/api-reference"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check total length (max 50 chars)
|
||||
if [ ${#BRANCH_NAME} -gt 50 ]; then
|
||||
echo ""
|
||||
echo "[git-flow] Branch name too long"
|
||||
echo ""
|
||||
echo "Branch: $BRANCH_NAME (${#BRANCH_NAME} chars)"
|
||||
echo "Maximum: 50 characters"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Valid branch name
|
||||
exit 0
|
||||
74
plugins/git-flow/hooks/commit-msg-check.sh
Executable file
74
plugins/git-flow/hooks/commit-msg-check.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# git-flow commit message validation hook
|
||||
# Validates git commit messages follow conventional commit format
|
||||
# PreToolUse hook for Bash commands - type: command
|
||||
|
||||
# Read tool input from stdin
|
||||
INPUT=$(cat)
|
||||
|
||||
# Use Python to properly parse JSON and extract the command
|
||||
COMMAND=$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('command',''))" 2>/dev/null)
|
||||
|
||||
# If no command or python failed, allow through
|
||||
if [ -z "$COMMAND" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if it is a git commit command with -m flag
|
||||
if ! echo "$COMMAND" | grep -qE 'git\s+commit.*-m'; then
|
||||
# Not a git commit with -m, allow through
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract commit message - handle various quoting styles
|
||||
# Try double quotes first
|
||||
COMMIT_MSG=$(echo "$COMMAND" | sed -n 's/.*-m[[:space:]]*"\([^"]*\)".*/\1/p')
|
||||
# If empty, try single quotes
|
||||
if [ -z "$COMMIT_MSG" ]; then
|
||||
COMMIT_MSG=$(echo "$COMMAND" | sed -n "s/.*-m[[:space:]]*'\\([^']*\\)'.*/\\1/p")
|
||||
fi
|
||||
# If still empty, try HEREDOC pattern
|
||||
if [ -z "$COMMIT_MSG" ]; then
|
||||
if echo "$COMMAND" | grep -qE -- '-m[[:space:]]+"\$\(cat <<'; then
|
||||
# HEREDOC pattern - too complex to parse, allow through
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# If no message extracted, allow through
|
||||
if [ -z "$COMMIT_MSG" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Validate conventional commit format
|
||||
# Format: <type>(<scope>): <description>
|
||||
# or: <type>: <description>
|
||||
# Valid types: feat, fix, docs, style, refactor, perf, test, chore, build, ci
|
||||
|
||||
VALID_TYPES="feat|fix|docs|style|refactor|perf|test|chore|build|ci"
|
||||
|
||||
# Check if message matches conventional commit format
|
||||
if echo "$COMMIT_MSG" | grep -qE "^($VALID_TYPES)(\([a-zA-Z0-9_-]+\))?:[[:space:]]+.+"; then
|
||||
# Valid format
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Invalid format - output warning
|
||||
echo "[git-flow] WARNING: Commit message does not follow conventional commit format"
|
||||
echo ""
|
||||
echo "Expected format: <type>(<scope>): <description>"
|
||||
echo " or: <type>: <description>"
|
||||
echo ""
|
||||
echo "Valid types: feat, fix, docs, style, refactor, perf, test, chore, build, ci"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " feat(auth): add password reset functionality"
|
||||
echo " fix: resolve login timeout issue"
|
||||
echo " docs(readme): update installation instructions"
|
||||
echo ""
|
||||
echo "Your message: $COMMIT_MSG"
|
||||
echo ""
|
||||
echo "To proceed anyway, use /commit command which auto-generates valid messages."
|
||||
|
||||
# Exit with non-zero to block
|
||||
exit 1
|
||||
19
plugins/git-flow/hooks/hooks.json
Normal file
19
plugins/git-flow/hooks/hooks.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/branch-check.sh"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/commit-msg-check.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user