140 lines
4.8 KiB
Bash
140 lines
4.8 KiB
Bash
#!/usr/bin/env bash
|
|
# Git extension: auto-commit.sh
|
|
# Automatically commit changes after a Spec Kit command completes.
|
|
# Checks per-command config keys in git-config.yml before committing.
|
|
#
|
|
# Usage: auto-commit.sh <event_name>
|
|
# e.g.: auto-commit.sh after_specify
|
|
|
|
set -e
|
|
|
|
EVENT_NAME="${1:-}"
|
|
if [ -z "$EVENT_NAME" ]; then
|
|
echo "Usage: $0 <event_name>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
_find_project_root() {
|
|
local dir="$1"
|
|
while [ "$dir" != "/" ]; do
|
|
if [ -d "$dir/.specify" ] || [ -d "$dir/.git" ]; then
|
|
echo "$dir"
|
|
return 0
|
|
fi
|
|
dir="$(dirname "$dir")"
|
|
done
|
|
return 1
|
|
}
|
|
|
|
REPO_ROOT=$(_find_project_root "$SCRIPT_DIR") || REPO_ROOT="$(pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
# Check if git is available
|
|
if ! command -v git >/dev/null 2>&1; then
|
|
echo "[specify] Warning: Git not found; skipped auto-commit" >&2
|
|
exit 0
|
|
fi
|
|
|
|
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
echo "[specify] Warning: Not a Git repository; skipped auto-commit" >&2
|
|
exit 0
|
|
fi
|
|
|
|
# Read per-command config from git-config.yml
|
|
_config_file="$REPO_ROOT/.specify/extensions/git/git-config.yml"
|
|
_enabled=false
|
|
_commit_msg=""
|
|
|
|
if [ -f "$_config_file" ]; then
|
|
# Parse the auto_commit section for this event.
|
|
# Look for auto_commit.<event_name>.enabled and .message
|
|
# Also check auto_commit.default as fallback.
|
|
_in_auto_commit=false
|
|
_in_event=false
|
|
_default_enabled=false
|
|
|
|
while IFS= read -r _line; do
|
|
# Detect auto_commit: section
|
|
if echo "$_line" | grep -q '^auto_commit:'; then
|
|
_in_auto_commit=true
|
|
_in_event=false
|
|
continue
|
|
fi
|
|
|
|
# Exit auto_commit section on next top-level key
|
|
if $_in_auto_commit && echo "$_line" | grep -Eq '^[a-z]'; then
|
|
break
|
|
fi
|
|
|
|
if $_in_auto_commit; then
|
|
# Check default key
|
|
if echo "$_line" | grep -Eq "^[[:space:]]+default:[[:space:]]"; then
|
|
_val=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
|
|
[ "$_val" = "true" ] && _default_enabled=true
|
|
fi
|
|
|
|
# Detect our event subsection
|
|
if echo "$_line" | grep -Eq "^[[:space:]]+${EVENT_NAME}:"; then
|
|
_in_event=true
|
|
continue
|
|
fi
|
|
|
|
# Inside our event subsection
|
|
if $_in_event; then
|
|
# Exit on next sibling key (same indent level as event name)
|
|
if echo "$_line" | grep -Eq '^[[:space:]]{2}[a-z]' && ! echo "$_line" | grep -Eq '^[[:space:]]{4}'; then
|
|
_in_event=false
|
|
continue
|
|
fi
|
|
if echo "$_line" | grep -Eq '[[:space:]]+enabled:'; then
|
|
_val=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
|
|
[ "$_val" = "true" ] && _enabled=true
|
|
[ "$_val" = "false" ] && _enabled=false
|
|
fi
|
|
if echo "$_line" | grep -Eq '[[:space:]]+message:'; then
|
|
_commit_msg=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//')
|
|
fi
|
|
fi
|
|
fi
|
|
done < "$_config_file"
|
|
|
|
# If event-specific key not found, use default
|
|
if [ "$_enabled" = "false" ] && [ "$_default_enabled" = "true" ]; then
|
|
# Only use default if the event wasn't explicitly set to false
|
|
# Check if event section existed at all
|
|
if ! grep -q "^[[:space:]]*${EVENT_NAME}:" "$_config_file" 2>/dev/null; then
|
|
_enabled=true
|
|
fi
|
|
fi
|
|
else
|
|
# No config file — auto-commit disabled by default
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$_enabled" != "true" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
# Check if there are changes to commit
|
|
if git diff --quiet HEAD 2>/dev/null && git diff --cached --quiet 2>/dev/null && [ -z "$(git ls-files --others --exclude-standard 2>/dev/null)" ]; then
|
|
echo "[specify] No changes to commit after $EVENT_NAME" >&2
|
|
exit 0
|
|
fi
|
|
|
|
# Derive a human-readable command name from the event
|
|
# e.g., after_specify -> specify, before_plan -> plan
|
|
_command_name=$(echo "$EVENT_NAME" | sed 's/^after_//' | sed 's/^before_//')
|
|
_phase=$(echo "$EVENT_NAME" | grep -q '^before_' && echo 'before' || echo 'after')
|
|
|
|
# Use custom message if configured, otherwise default
|
|
if [ -z "$_commit_msg" ]; then
|
|
_commit_msg="[Spec Kit] Auto-commit ${_phase} ${_command_name}"
|
|
fi
|
|
|
|
# Stage and commit
|
|
_git_out=$(git add . 2>&1) || { echo "[specify] Error: git add failed: $_git_out" >&2; exit 1; }
|
|
_git_out=$(git commit -q -m "$_commit_msg" 2>&1) || { echo "[specify] Error: git commit failed: $_git_out" >&2; exit 1; }
|
|
|
|
echo "✓ Changes committed ${_phase} ${_command_name}" >&2
|