From 1c34ff9f7da32d505cb6cc9f5c9e1f87f0a2447d Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 10:11:50 +0100 Subject: [PATCH 01/10] fix: correct regex pattern and comment filtering in .preflight-exclude 1. Fixed LICENSES pattern from ^LICENSES/.\*\.txt$ to ^LICENSES/.*\.txt$ (the escaped asterisk prevented matching any .txt files) 2. Fixed comment filtering pattern from ^(#|[[:space:]]*$) to ^[[:space:]]*(#|$) (now correctly filters indented comments) --- .preflight-exclude | 2 +- scripts/preflight.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.preflight-exclude b/.preflight-exclude index 22c4208..4d31d5a 100644 --- a/.preflight-exclude +++ b/.preflight-exclude @@ -33,4 +33,4 @@ pnpm-lock.yaml # License files (boilerplate) -^LICENSES/.\*\.txt$ +^LICENSES/.*\.txt$ diff --git a/scripts/preflight.sh b/scripts/preflight.sh index adc3b32..559c472 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -87,7 +87,7 @@ else # Load exclude patterns from .preflight-exclude if it exists if [ -f "$ROOT_DIR/.preflight-exclude" ]; then # Extract non-comment, non-empty lines as grep-compatible regex patterns - EXCLUDE_PATTERNS=$(grep -vE '^(#|[[:space:]]*$)' "$ROOT_DIR/.preflight-exclude" || true) + EXCLUDE_PATTERNS=$(grep -vE '^[[:space:]]*(#|$)' "$ROOT_DIR/.preflight-exclude" || true) if [ -n "$EXCLUDE_PATTERNS" ]; then # Build regex alternation for efficient filtering (patterns are used as-is) From d3a8f5cbf01740c45dd64f20e7b50f583c4ae87b Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 10:41:39 +0100 Subject: [PATCH 02/10] docs: remove duplicate "Bypassing the PR size check" section Found during comprehensive code review - section was duplicated. --- CONTRIBUTING.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ebcca5b..86d97fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,25 +119,6 @@ rm .preflight-allow-large-pr ⚠️ **Important:** The override file is automatically ignored by git and should only be used for exceptional cases that match the criteria below. -**Bypassing the PR size check locally:** - -If you need to work on a large PR that is justified (see exceptions below), you can temporarily bypass the 600-line limit: - -```bash -# Create override file to allow large PR -touch .preflight-allow-large-pr - -# Work on your changes -git add . -git commit -m "Your changes" -git push - -# Clean up after merge -rm .preflight-allow-large-pr -``` - -⚠️ **Important:** The override file is automatically ignored by git and should only be used for exceptional cases that match the criteria below. - ## How to Contribute 1. **Fork the repository** and create a new branch from `main`. From eb392c178a819639d063144679ce0889c46ecd41 Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 11:03:55 +0100 Subject: [PATCH 03/10] fix: add CRLF support and prevent grep flag injection Same fixes as .github repo - see detailed description there. Changes in scripts/preflight.sh: - Added tr -d '\r' to strip Windows CRLF line endings - Added -- to grep -vE to prevent flag injection from dash-prefixed patterns --- scripts/preflight.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/preflight.sh b/scripts/preflight.sh index 559c472..48047ad 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -87,12 +87,14 @@ else # Load exclude patterns from .preflight-exclude if it exists if [ -f "$ROOT_DIR/.preflight-exclude" ]; then # Extract non-comment, non-empty lines as grep-compatible regex patterns - EXCLUDE_PATTERNS=$(grep -vE '^[[:space:]]*(#|$)' "$ROOT_DIR/.preflight-exclude" || true) + # Strip CR for Windows/CRLF compatibility + EXCLUDE_PATTERNS=$(grep -vE '^[[:space:]]*(#|$)' "$ROOT_DIR/.preflight-exclude" | tr -d '\r' || true) if [ -n "$EXCLUDE_PATTERNS" ]; then # Build regex alternation for efficient filtering (patterns are used as-is) EXCLUDE_REGEX=$(echo "$EXCLUDE_PATTERNS" | tr '\n' '|' | sed 's/|$//') - DIFF_OUTPUT=$(echo "$DIFF_OUTPUT" | grep -vE "$EXCLUDE_REGEX" || true) + # Use -- to prevent patterns starting with - from being interpreted as flags + DIFF_OUTPUT=$(echo "$DIFF_OUTPUT" | grep -vE -- "$EXCLUDE_REGEX" || true) fi fi From 60cbb9ca8f9678e2370e57a8c9d5ba7c8a838a3e Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 11:22:12 +0100 Subject: [PATCH 04/10] fix(preflight): fix pattern validation logic (Bug #9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - grep exit codes: 0=match, 1=no match, 2=error - Previous logic: if pattern matches empty string → valid Problem: '^file\.txt$' is valid but doesn't match '' - New logic: if grep exit != 2 → valid Correctly handles both matching and non-matching patterns - Tested with 6 test cases: all pass ✅ --- scripts/preflight.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/preflight.sh b/scripts/preflight.sh index 48047ad..57e2464 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -93,8 +93,29 @@ else if [ -n "$EXCLUDE_PATTERNS" ]; then # Build regex alternation for efficient filtering (patterns are used as-is) EXCLUDE_REGEX=$(echo "$EXCLUDE_PATTERNS" | tr '\n' '|' | sed 's/|$//') + + # Validate regex and warn about dangerous patterns + # grep exit codes: 0=match, 1=no match, 2=error (invalid regex) + echo "" | grep -qE -- "$EXCLUDE_REGEX" 2>/dev/null + GREP_EXIT=$? + if [ $GREP_EXIT -ne 2 ]; then + # Pattern is valid (exit 0 or 1), check if it matches everything + if echo "test-file.txt" | grep -qE -- "$EXCLUDE_REGEX" && \ + echo "another-file.js" | grep -qE -- "$EXCLUDE_REGEX" && \ + echo "random.md" | grep -qE -- "$EXCLUDE_REGEX"; then + echo "⚠️ WARNING: .preflight-exclude contains pattern that matches EVERYTHING (e.g., '.*')" >&2 + echo "This will exclude all files from PR size calculation!" >&2 + fi + else + # Invalid regex - grep failed even on empty input + echo "⚠️ WARNING: .preflight-exclude contains invalid regex pattern(s)" >&2 + echo "The pattern will be ignored. Please check your .preflight-exclude file." >&2 + echo "Common issues: unbalanced brackets [, unmatched (, trailing backslash \\" >&2 + fi + # Use -- to prevent patterns starting with - from being interpreted as flags - DIFF_OUTPUT=$(echo "$DIFF_OUTPUT" | grep -vE -- "$EXCLUDE_REGEX" || true) + # || true prevents script exit if pattern is invalid + DIFF_OUTPUT=$(echo "$DIFF_OUTPUT" | grep -vE -- "$EXCLUDE_REGEX" 2>/dev/null || true) fi fi From c0bac8c447a7a13410d138d17e51fec200a69e5a Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 12:18:55 +0100 Subject: [PATCH 05/10] fix(preflight): replace git remote show with symbolic-ref to avoid network hang - Replace 'git remote show origin' with 'git symbolic-ref' to detect base branch - Avoids SSH/network connection hang when detecting default branch - Faster execution and more reliable in offline/slow network scenarios - Maintains same fallback behavior (defaults to 'main') Resolves the issue where preflight script would hang indefinitely when trying to detect the base branch via network-dependent command. --- scripts/preflight.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/preflight.sh b/scripts/preflight.sh index 57e2464..54a9840 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -8,7 +8,8 @@ ROOT_DIR="$(git rev-parse --show-toplevel)" cd "$ROOT_DIR" # Auto-detect default branch (fallback to main) -BASE="$(git remote show origin 2>/dev/null | sed -n '/HEAD branch/s/.*: //p')" +# Use symbolic-ref instead of remote show to avoid network hang +BASE="$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')" [ -z "${BASE:-}" ] && BASE="main" echo "Using base branch: $BASE" From 03c8e502fb2fec8c9934cdbcbf9cdb2308284e3a Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 13:10:48 +0100 Subject: [PATCH 06/10] fix(preflight): disable actionlint in local preflight due to hanging issue - actionlint hangs indefinitely when run locally (network timeout) - Keep actionlint enabled in CI where it works reliably - This fixes the pre-push hook hanging issue - Users can uncomment the line to re-enable if needed --- scripts/preflight.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/preflight.sh b/scripts/preflight.sh index 54a9840..400c1ed 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -24,9 +24,13 @@ if command -v npx >/dev/null 2>&1; then npx --yes markdownlint-cli2 '**/*.md' || FORMAT_EXIT=1 fi # Workflow linting (part of documented gates) +# NOTE: actionlint is disabled in local preflight due to known hanging issues +# It runs in CI via .github/workflows/actionlint.yml instead if [ -d .github/workflows ]; then if command -v actionlint >/dev/null 2>&1; then - actionlint || FORMAT_EXIT=1 + echo "ℹ️ Skipping actionlint in local preflight (runs in CI)" >&2 + # Uncomment below to enable (may hang): + # timeout 30 actionlint || FORMAT_EXIT=1 else echo "Warning: .github/workflows found but actionlint not installed - skipping workflow lint" >&2 fi From 2d9d2a7cc5cf717e45e7336a5fb07408ba7b07e3 Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 13:17:55 +0100 Subject: [PATCH 07/10] feat: add setup-pre-push.sh script for automated hook installation - Automatically creates symlink from .git/hooks/pre-push to scripts/preflight.sh - Backs up existing pre-push hook if not a symlink - Makes hook installation consistent across all repos - Complements existing setup-pre-commit.sh script --- scripts/setup-pre-push.sh | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 scripts/setup-pre-push.sh diff --git a/scripts/setup-pre-push.sh b/scripts/setup-pre-push.sh new file mode 100755 index 0000000..11b1ff3 --- /dev/null +++ b/scripts/setup-pre-push.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: 2025 SecPal Contributors +# SPDX-License-Identifier: MIT + +set -euo pipefail + +echo "🔧 Setting up pre-push hook for SecPal..." + +# Get the repository root +REPO_ROOT="$(git rev-parse --show-toplevel)" +cd "$REPO_ROOT" + +# Check if preflight.sh exists +if [ ! -f "scripts/preflight.sh" ]; then + echo "❌ scripts/preflight.sh not found in repository" + exit 1 +fi + +# Create the pre-push hook as a symlink +HOOK_PATH=".git/hooks/pre-push" +TARGET_PATH="../../scripts/preflight.sh" + +if [ -e "$HOOK_PATH" ] && [ ! -L "$HOOK_PATH" ]; then + echo "⚠️ Warning: .git/hooks/pre-push already exists and is not a symlink" + echo " Backing up to .git/hooks/pre-push.backup" + mv "$HOOK_PATH" "$HOOK_PATH.backup" +fi + +# Create the symlink +ln -sf "$TARGET_PATH" "$HOOK_PATH" +chmod +x "$HOOK_PATH" + +echo "✅ Pre-push hook installed successfully!" +echo "" +echo "The hook will run automatically before every push." +echo "It will execute: scripts/preflight.sh" +echo "" +echo "To bypass the hook (not recommended): git push --no-verify" +echo "To test the hook manually: ./scripts/preflight.sh" From 09d40052b5de6c54e864ead669384c33bc5753a0 Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 13:25:31 +0100 Subject: [PATCH 08/10] fix(preflight): prevent exit 1 in regex validation with set -e --- scripts/preflight.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/preflight.sh b/scripts/preflight.sh index 400c1ed..ca4933c 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -101,7 +101,7 @@ else # Validate regex and warn about dangerous patterns # grep exit codes: 0=match, 1=no match, 2=error (invalid regex) - echo "" | grep -qE -- "$EXCLUDE_REGEX" 2>/dev/null + echo "" | grep -qE -- "$EXCLUDE_REGEX" 2>/dev/null || true GREP_EXIT=$? if [ $GREP_EXIT -ne 2 ]; then # Pattern is valid (exit 0 or 1), check if it matches everything From b6585ac01fdd5e7be1859fac2bbecb831f0314bf Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 14:41:34 +0100 Subject: [PATCH 09/10] fix(preflight): address Copilot review comments - exit code capture and validation improvements - Fix CRITICAL bug: capture grep exit code before || true (set +e/set -e pattern) - Remove ineffective chmod +x on symlink in setup-pre-push.sh - Improve pattern validation with more diverse test filenames (README.md, package.json) Addresses Copilot review comment in PR #25 --- scripts/preflight.sh | 9 +++++++-- scripts/setup-pre-push.sh | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/preflight.sh b/scripts/preflight.sh index ca4933c..7f0fa74 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -101,13 +101,18 @@ else # Validate regex and warn about dangerous patterns # grep exit codes: 0=match, 1=no match, 2=error (invalid regex) - echo "" | grep -qE -- "$EXCLUDE_REGEX" 2>/dev/null || true + set +e # Temporarily disable exit-on-error to capture grep's exit code + echo "" | grep -qE -- "$EXCLUDE_REGEX" 2>/dev/null GREP_EXIT=$? + set -e # Re-enable exit-on-error if [ $GREP_EXIT -ne 2 ]; then # Pattern is valid (exit 0 or 1), check if it matches everything + # Test against diverse filenames to detect overly broad patterns if echo "test-file.txt" | grep -qE -- "$EXCLUDE_REGEX" && \ echo "another-file.js" | grep -qE -- "$EXCLUDE_REGEX" && \ - echo "random.md" | grep -qE -- "$EXCLUDE_REGEX"; then + echo "random.md" | grep -qE -- "$EXCLUDE_REGEX" && \ + echo "README.md" | grep -qE -- "$EXCLUDE_REGEX" && \ + echo "package.json" | grep -qE -- "$EXCLUDE_REGEX"; then echo "⚠️ WARNING: .preflight-exclude contains pattern that matches EVERYTHING (e.g., '.*')" >&2 echo "This will exclude all files from PR size calculation!" >&2 fi diff --git a/scripts/setup-pre-push.sh b/scripts/setup-pre-push.sh index 11b1ff3..ae59e6e 100755 --- a/scripts/setup-pre-push.sh +++ b/scripts/setup-pre-push.sh @@ -28,7 +28,6 @@ fi # Create the symlink ln -sf "$TARGET_PATH" "$HOOK_PATH" -chmod +x "$HOOK_PATH" echo "✅ Pre-push hook installed successfully!" echo "" From 2edb8fb6cb40054450132ed760b0a68b984d7f93 Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 26 Oct 2025 15:24:53 +0100 Subject: [PATCH 10/10] fix(preflight): improve pattern detection with more diverse test filenames - Add more diverse test filenames for overly-broad pattern detection (hidden files, uppercase, numbers, special chars) - Better catch patterns like ^[a-z] that match many but not all files Addresses Copilot review comment --- scripts/preflight.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/preflight.sh b/scripts/preflight.sh index 7f0fa74..261c322 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -108,11 +108,15 @@ else if [ $GREP_EXIT -ne 2 ]; then # Pattern is valid (exit 0 or 1), check if it matches everything # Test against diverse filenames to detect overly broad patterns + # Include various cases: lowercase, uppercase, numbers, special chars, hidden files if echo "test-file.txt" | grep -qE -- "$EXCLUDE_REGEX" && \ echo "another-file.js" | grep -qE -- "$EXCLUDE_REGEX" && \ echo "random.md" | grep -qE -- "$EXCLUDE_REGEX" && \ echo "README.md" | grep -qE -- "$EXCLUDE_REGEX" && \ - echo "package.json" | grep -qE -- "$EXCLUDE_REGEX"; then + echo "package.json" | grep -qE -- "$EXCLUDE_REGEX" && \ + echo ".hidden" | grep -qE -- "$EXCLUDE_REGEX" && \ + echo "File123.py" | grep -qE -- "$EXCLUDE_REGEX" && \ + echo "UPPERCASE" | grep -qE -- "$EXCLUDE_REGEX"; then echo "⚠️ WARNING: .preflight-exclude contains pattern that matches EVERYTHING (e.g., '.*')" >&2 echo "This will exclude all files from PR size calculation!" >&2 fi