Newer
Older
TelosDB / .githooks / pre-commit
@楽曲作りまくりおじさん 楽曲作りまくりおじさん 17 hours ago 2 KB docs: remove Japanese duplicate journal (keep ASCII copy)
#!/usr/bin/env bash
# Simple pre-commit guard for common mistakes
# - Prevent committing or deleting .env files
# - Block deletions in protected directories (docs/, src/backend/)

set -e

# Get staged name-status
STAGED=$(git diff --cached --name-status)

# Prevent adding/modifying .env files
if echo "$STAGED" | grep -E '^[AM]\s+.*\.env(\.|$)\|^A\s+\.env$' >/dev/null; then
  echo "ERROR: .env files must not be added/modified in repository. Keep them local and add to .gitignore." >&2
  exit 1
fi

# Prevent deletion of critical/protected files
DELETED=$(echo "$STAGED" | awk '$1 == "D" {print $2}')
if [ -n "$DELETED" ]; then
  # Protected list file: list of paths that are considered critical for ongoing development
  PROTECTED_FILE=".protected-files"

  # If protected-file list exists, only require explicit approval for deletions that match it
  if [ -f "$PROTECTED_FILE" ]; then
    mapfile -t PROTECTED < <(sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//' "$PROTECTED_FILE" | sed '/^$/d')
  else
    PROTECTED=()
  fi

  # Always block deletion/add/modify of .env files
  while read -r f; do
    case "$f" in
      *.env|.env|.env.*)
        echo "ERROR: .env files must not be deleted/added/modified in repository. Keep them local and add to .gitignore." >&2
        exit 1
        ;;
    esac
  done <<< "$DELETED"

  # Build a list of deletions that hit the protected list
  NEED_APPROVAL=()
  while read -r f; do
    for p in "${PROTECTED[@]}"; do
      # If protected entry ends with '/', treat as prefix (directory)
      if [[ "$p" == */ ]]; then
        if [[ "$f" == "$p"* ]]; then
          NEED_APPROVAL+=("$f")
        fi
      else
        # match exact file or files under directory indicated without trailing slash
        if [[ "$f" == "$p" || "$f" == "$p"/* ]]; then
          NEED_APPROVAL+=("$f")
        fi
      fi
    done
  done <<< "$DELETED"

  if [ ${#NEED_APPROVAL[@]} -gt 0 ]; then
    APPROVAL_FILE=".deletions-approved"
    if [ ! -f "$APPROVAL_FILE" ]; then
      echo "ERROR: Deletions of protected files detected but no $APPROVAL_FILE found." >&2
      echo "To approve deletions, create $APPROVAL_FILE listing the exact protected paths to delete (one per line), commit it first, then perform deletions." >&2
      exit 1
    fi

    mapfile -t APPROVED < <(sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//' "$APPROVAL_FILE" | sed '/^$/d')

    for f in "${NEED_APPROVAL[@]}"; do
      ok=0
      for a in "${APPROVED[@]}"; do
        if [[ "$a" == */ ]]; then
          if [[ "$f" == "$a"* ]]; then ok=1; break; fi
        else
          if [[ "$f" == "$a" ]]; then ok=1; break; fi
        fi
      done
      if [ $ok -ne 1 ]; then
        echo "ERROR: Deletion of protected file $f is not listed in $APPROVAL_FILE." >&2
        echo "Add the exact path or a directory (with trailing slash) to $APPROVAL_FILE and commit that file first to approve the deletion." >&2
        exit 1
      fi
    done
  fi
fi

# All checks passed
exit 0