From f812ce82a2aa8c6226565dca5b4d1dc5b830e87c Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 2 Nov 2025 20:36:33 +0100 Subject: [PATCH 1/2] fix: remove --no-verify suggestion from error message Removed option 3 from pre-push error message as it violates Critical Rule #4: No Bypass policy. The only legitimate override is .preflight-allow-large-pr for exceptional cases with maintainer approval. Follow-up to #81 based on Copilot review feedback. --- scripts/preflight.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/preflight.sh b/scripts/preflight.sh index 824bfeb..41e72e8 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -292,8 +292,7 @@ else echo "" >&2 echo "💡 Available options:" >&2 echo " 1. Split PR: Recommended approach" >&2 - echo " 2. Override check: touch .preflight-allow-large-pr" >&2 - echo " 3. Bypass hook: git push --no-verify (not recommended)" >&2 + echo " 2. Override check: touch .preflight-allow-large-pr (for exceptional cases)" >&2 echo "" >&2 echo "Note: Lock files and license files are already excluded" >&2 echo " See .preflight-exclude for custom exclusion patterns" >&2 From 7f4349958be3ec1204a1d054ee10b82b86f73e36 Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Sun, 2 Nov 2025 20:54:27 +0100 Subject: [PATCH 2/2] refactor: optimize CHANGELOG validation for consistency - Replace bash-specific [[ =~ ]] with POSIX-compliant case statement - Combine multiple grep -v calls into single grep -Ev with pattern group - Use grep -m 1 for early termination (performance improvement) - Fix sed range calculation (UNRELEASED_END is relative, not absolute) - Add clarifying comments about CHANGELOG_EXEMPT_PREFIXES and case sync - Improve warning message with specific line count and non-blocking note - Extract filter logic to reusable filter_changelog_content() function Aligns api/scripts/preflight.sh with optimizations applied to frontend, contracts, and .github repositories per Copilot review feedback. --- scripts/preflight.sh | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/scripts/preflight.sh b/scripts/preflight.sh index 41e72e8..8465098 100755 --- a/scripts/preflight.sh +++ b/scripts/preflight.sh @@ -170,14 +170,29 @@ if [ -f docs/openapi.yaml ] && command -v npx >/dev/null 2>&1; then fi # 4) CHANGELOG validation (for non-docs branches) -# Branch prefixes that are exempt from CHANGELOG updates (configuration) -CHANGELOG_EXEMPT_PREFIXES="^(docs|chore|ci|test)/" +# Branch prefixes that are exempt from CHANGELOG updates +# Note: These must be kept in sync with the case statement below +CHANGELOG_EXEMPT_PREFIXES="docs chore ci test" # Minimum lines in [Unreleased] to consider it non-empty # Typically: 3 lines = one line each for Added, Changed, Fixed sections MIN_CHANGELOG_LINES=3 +# Helper function to filter CHANGELOG content (POSIX-compliant with whitespace tolerance) +filter_changelog_content() { + grep -Ev '(^##|^$|^[[:space:]]*)' +} + CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "") -if [ -f CHANGELOG.md ] && [ "$CURRENT_BRANCH" != "main" ] && [[ ! "$CURRENT_BRANCH" =~ $CHANGELOG_EXEMPT_PREFIXES ]]; then +# Use POSIX-compliant case statement instead of bash-specific [[ =~ ]] for portability +# The case patterns below must match the prefixes in CHANGELOG_EXEMPT_PREFIXES +BRANCH_IS_EXEMPT=false +case "$CURRENT_BRANCH" in + docs/*|chore/*|ci/*|test/*) + BRANCH_IS_EXEMPT=true + ;; +esac + +if [ -f CHANGELOG.md ] && [ "$CURRENT_BRANCH" != "main" ] && [ "$BRANCH_IS_EXEMPT" = false ]; then # Check if CHANGELOG has [Unreleased] section if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then echo "❌ CHANGELOG.md missing [Unreleased] section" >&2 @@ -191,18 +206,20 @@ if [ -f CHANGELOG.md ] && [ "$CURRENT_BRANCH" != "main" ] && [[ ! "$CURRENT_BRAN UNRELEASED_START=$(grep -n '^## \[Unreleased\]' CHANGELOG.md | cut -d: -f1) if [ -n "$UNRELEASED_START" ]; then # Find next heading after [Unreleased], or use EOF if none found - UNRELEASED_END=$(tail -n +"$((UNRELEASED_START + 1))" CHANGELOG.md | grep -n '^## ' | head -1 | cut -d: -f1) + # Use grep -m 1 to stop after first match for better performance + UNRELEASED_END=$(tail -n +"$((UNRELEASED_START + 1))" CHANGELOG.md | grep -n -m 1 '^## ' | cut -d: -f1) if [ -n "$UNRELEASED_END" ]; then - # Extract content between [Unreleased] and next heading - UNRELEASED_CONTENT=$(sed -n "$((UNRELEASED_START + 1)),$((UNRELEASED_START + UNRELEASED_END - 1))p" CHANGELOG.md | grep -v '^##' | grep -v '^$' | grep -v '^' | wc -l) + # Extract content between [Unreleased] and next heading (using helper function) + # UNRELEASED_END is relative line number from tail, so add to UNRELEASED_START without -1 + UNRELEASED_CONTENT=$(sed -n "$((UNRELEASED_START + 1)),$((UNRELEASED_START + UNRELEASED_END))p" CHANGELOG.md | filter_changelog_content | wc -l) else - # [Unreleased] is the last section, extract all remaining content - UNRELEASED_CONTENT=$(tail -n +"$((UNRELEASED_START + 1))" CHANGELOG.md | grep -v '^##' | grep -v '^$' | grep -v '^' | wc -l) + # [Unreleased] is the last section, extract all remaining content (using helper function) + UNRELEASED_CONTENT=$(tail -n +"$((UNRELEASED_START + 1))" CHANGELOG.md | filter_changelog_content | wc -l) fi if [ "$UNRELEASED_CONTENT" -lt "$MIN_CHANGELOG_LINES" ]; then - echo "⚠️ Warning: [Unreleased] section appears empty in CHANGELOG.md" >&2 - echo "Did you forget to document your changes?" >&2 + echo "⚠️ Warning: [Unreleased] section appears empty in CHANGELOG.md (found fewer than $MIN_CHANGELOG_LINES content lines)" >&2 + echo "This is non-blocking, but please add a summary of your changes to the [Unreleased] section before merging." >&2 fi fi fi