From ed995cbeeca0ef59962bf5c75ec6c2a0917b39c0 Mon Sep 17 00:00:00 2001 From: Henry Chen Date: Sun, 7 Jun 2026 19:09:29 -0400 Subject: [PATCH] .git: Centralize .githooks and add autoformatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-commit hooks enforce formatting and linting locally before code reaches CI, giving developers instant feedback and keeping the commit history clean. Auto-fixing reduces friction — most violations are corrected automatically without the developer needing to do anything extra. Co-Authored-By: Claude Sonnet 4.6 --- .githooks/pre-commit | 76 ++++++++++++++++++++++++++++++++ pre-commit => .githooks/pre-push | 14 +++--- Justfile | 13 ++---- 3 files changed, 86 insertions(+), 17 deletions(-) create mode 100755 .githooks/pre-commit rename pre-commit => .githooks/pre-push (58%) mode change 100644 => 100755 diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..a9dae49 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,76 @@ +#!/bin/bash +# Pre-commit hook: Runs automatically before every commit to check for unencrypted secrets, +# auto-format, and lint staged files. +# Installed automatically via `just dev` or manually via `just install-pre-scripts`. +# To bypass (use sparingly): git commit --no-verify +echo "--Running pre-commit" +EXIT_CODE=0 + +# --- Secrets check --- +# Get a list of all secret.yaml and secrets.yaml files staged for commit +SECRET_FILES=$(git diff --cached --name-only | grep -E 'secret(s)?\.yaml$') +for FILE in $SECRET_FILES; do + if [ -f "$FILE" ]; then + if ! grep -q "sops:" "$FILE"; then + echo "--------------------------------------------------------" + echo "ERROR: Unencrypted secret file detected: $FILE" + echo "Please encrypt this file by running \`just encrypt $FILE\`," + echo "then run \`git add $FILE\`." + echo "--------------------------------------------------------" + EXIT_CODE=1 + fi + fi +done + +# --- Backend: format + lint (if any Java files staged) --- +JAVA_FILES=$(git diff --cached --name-only | grep '\.java$') +if [ -n "$JAVA_FILES" ]; then + echo "--Running backend formatter (Spotless)..." + ./mvnw spotless:apply -q + if [ $? -ne 0 ]; then + echo "ERROR: Spotless formatting failed." + EXIT_CODE=1 + else + echo "$JAVA_FILES" | xargs git add + fi + + echo "--Running backend linter (Checkstyle)..." + ./mvnw checkstyle:check -q + if [ $? -ne 0 ]; then + echo "ERROR: Checkstyle found violations." + EXIT_CODE=1 + fi +fi + +# --- Frontend: format + lint (if any frontend files staged) --- +FRONTEND_FILES=$(git diff --cached --name-only | grep -E '^js/.*\.(ts|tsx|js|jsx|css|json|md)$') +if [ -n "$FRONTEND_FILES" ]; then + echo "--Running frontend formatter (Prettier)..." + (cd js && pnpm prettier --write --log-level warn .) + if [ $? -ne 0 ]; then + echo "ERROR: Prettier formatting failed." + EXIT_CODE=1 + else + echo "$FRONTEND_FILES" | xargs git add + fi + + echo "--Running frontend linter (ESLint)..." + (cd js && pnpm eslint . --fix) + if [ $? -ne 0 ]; then + echo "ERROR: ESLint found violations that could not be auto-fixed." + EXIT_CODE=1 + else + echo "$FRONTEND_FILES" | xargs git add + fi + + echo "--Running frontend linter (Stylelint)..." + (cd js && pnpm stylelint '**/*.css' --fix --allow-empty-input) + if [ $? -ne 0 ]; then + echo "ERROR: Stylelint found violations that could not be auto-fixed." + EXIT_CODE=1 + else + echo "$FRONTEND_FILES" | xargs git add + fi +fi + +exit $EXIT_CODE diff --git a/pre-commit b/.githooks/pre-push old mode 100644 new mode 100755 similarity index 58% rename from pre-commit rename to .githooks/pre-push index 88b53b4..9afcc4b --- a/pre-commit +++ b/.githooks/pre-push @@ -1,20 +1,20 @@ #!/bin/bash -# Get a list of all secret.yaml and secrets.yaml files staged for commit -echo "--Running pre-commit" -FILES=$(git diff --cached --name-only | grep -E 'secret(s)?\.yaml$') +echo "--Running pre-push" EXIT_CODE=0 -for FILE in $FILES; do + +# --- Secrets check --- +SECRET_FILES=$(git diff --cached --name-only | grep -E 'secret(s)?\.yaml$') +for FILE in $SECRET_FILES; do if [ -f "$FILE" ]; then if ! grep -q "sops:" "$FILE"; then echo "--------------------------------------------------------" echo "ERROR: Unencrypted secret file detected: $FILE" - echo "Please encrypt this file by running \`just e $FILE\`," + echo "Please encrypt this file by running \`just encrypt $FILE\`," echo "then run \`git add $FILE\`." echo "--------------------------------------------------------" EXIT_CODE=1 fi fi done + exit $EXIT_CODE -EOF -chmod +x .git/hooks/pre-commit diff --git a/Justfile b/Justfile index 1558692..b29dda3 100644 --- a/Justfile +++ b/Justfile @@ -65,7 +65,7 @@ frontend-test *args: # Run the dev servers (backend & frontend) dev *args: - #cp internal/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit && + just install-pre-scripts npx concurrently "just backend-dev" "just frontend-dev" {{args}} # Run the dev servers (backend & frontend) but the backend will launch a debugger server. @@ -95,13 +95,6 @@ encrypt file *args: edit file *args: just install-pre-scripts && sops edit {{ file }} {{ args }} -# Git hooks are installed on almost every command -# so that we don't accidentally add unencrypted secrets to the git history. +# Configure Git to use the .githooks directory (idempotent) install-pre-scripts: - just install-pre-commit && just install-pre-push - -install-pre-commit: - cp pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit - -install-pre-push: - cp pre-commit .git/hooks/pre-push && chmod +x .git/hooks/pre-push + [ "$(git config core.hooksPath)" = ".githooks" ] || git config core.hooksPath .githooks