Skip to content

Commit cfd9095

Browse files
committed
fix: implement fail-fast fallback and single-commit git diff strategy
- Fix fallback logic to fail fast instead of building entire project when no modules detected - Change git diff strategy to use HEAD~1..HEAD for maintenance branches (single cherry-pick commits) - Add local testing script for debugging without GitHub Actions cycles - Update verbose logging to show which git diff strategy is being used Root cause: Cherry-picked commits on maintenance branches were compared against themselves using origin/branch...HEAD, resulting in 0 changed files and fallback to full builds. Now maintenance branches always use single-commit diff which correctly detects changes.
1 parent 84f83e5 commit cfd9095

File tree

3 files changed

+105
-37
lines changed

3 files changed

+105
-37
lines changed

.github/scripts/test_discovery.py

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -57,45 +57,25 @@ def _get_changed_files(self, base_ref: Optional[str] = None) -> List[str]:
5757
try:
5858
# Determine the reference to diff against
5959
pr_base = os.environ.get('GITHUB_BASE_REF') # PRs
60-
pr_head = os.environ.get('GITHUB_HEAD_REF') # PRs
60+
pr_head = os.environ.get('GITHUB_HEAD_HEAD') # PRs
6161
branch = os.environ.get('GITHUB_REF_NAME') # pushes
6262

63-
# Prefer explicit flag if provided
64-
if base_ref:
65-
ref_for_diff = base_ref
63+
# For maintenance branches (cherry-picks), always use single commit diff
64+
if branch and branch.endswith('.x'):
65+
# Maintenance branch - cherry-picks are always single commits
66+
cmd = ["git", "diff", "--name-only", "HEAD~1..HEAD"]
67+
elif base_ref:
68+
# Explicit base reference provided - use two-dot diff for direct comparison
69+
cmd = ["git", "diff", "--name-only", f"{base_ref}..HEAD"]
6670
elif pr_base and pr_head:
6771
# PR context - compare against base branch
68-
ref_for_diff = f"origin/{pr_base}"
72+
cmd = ["git", "diff", "--name-only", f"origin/{pr_base}..HEAD"]
6973
elif pr_base:
7074
# PR context fallback
71-
ref_for_diff = f"origin/{pr_base}"
72-
elif branch:
73-
# Push context - compare against remote tracking branch
74-
ref_for_diff = f"origin/{branch}"
75+
cmd = ["git", "diff", "--name-only", f"origin/{pr_base}..HEAD"]
7576
else:
76-
# Final fallback - find merge base with main
77-
ref_for_diff = None
78-
79-
if ref_for_diff:
80-
# Compare remote tracking branch tip to HEAD (handles multi-commit pushes)
81-
cmd = ["git", "diff", "--name-only", f"{ref_for_diff}...HEAD"]
82-
else:
83-
# Push context or other - compare with previous commit
84-
# First try to find a reasonable base commit
85-
merge_base_cmd = ["git", "merge-base", "HEAD", "origin/main"]
86-
try:
87-
merge_base_result = subprocess.run(
88-
merge_base_cmd,
89-
cwd=self.repo_root,
90-
capture_output=True,
91-
text=True,
92-
check=True
93-
)
94-
base_commit = merge_base_result.stdout.strip()
95-
cmd = ["git", "diff", "--name-only", f"{base_commit}...HEAD"]
96-
except subprocess.CalledProcessError:
97-
# Fallback to last commit
98-
cmd = ["git", "diff", "--name-only", "HEAD~1..HEAD"]
77+
# Final fallback - single commit diff (most reliable)
78+
cmd = ["git", "diff", "--name-only", "HEAD~1..HEAD"]
9979

10080
# Execute the git diff command
10181
result = subprocess.run(
@@ -189,12 +169,15 @@ def _detect_default_base(self) -> str:
189169
pr_base = os.environ.get('GITHUB_BASE_REF')
190170
branch = os.environ.get('GITHUB_REF_NAME')
191171

192-
if pr_base:
193-
return f"origin/{pr_base}"
172+
# Show the actual strategy being used
173+
if branch and branch.endswith('.x'):
174+
return f"HEAD~1 (single commit - maintenance branch {branch})"
175+
elif pr_base:
176+
return f"origin/{pr_base} (PR base)"
194177
elif branch:
195-
return f"origin/{branch}"
178+
return f"HEAD~1 (single commit - branch {branch})"
196179
else:
197-
return "origin/main (merge-base)"
180+
return "HEAD~1 (single commit - fallback)"
198181

199182

200183
def modules_from_diff_cli():

.github/scripts/test_local.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/bin/bash
2+
3+
# Local testing script to simulate GitHub Actions maintenance-fast.yml workflow
4+
# Usage: ./test_local.sh [branch_name]
5+
6+
set -e
7+
8+
BRANCH_NAME=${1:-"1.0.x"}
9+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
11+
echo "=== Local GitHub Actions Simulation ==="
12+
echo "Branch: $BRANCH_NAME"
13+
echo "Script dir: $SCRIPT_DIR"
14+
echo ""
15+
16+
# Simulate GitHub Actions environment variables
17+
export GITHUB_REF_NAME="$BRANCH_NAME"
18+
export GITHUB_REF="refs/heads/$BRANCH_NAME"
19+
20+
echo "=== Step 1: Show commit range ==="
21+
echo "Base ref: origin/$GITHUB_REF_NAME"
22+
if git rev-parse "origin/$GITHUB_REF_NAME" >/dev/null 2>&1; then
23+
git log --oneline "origin/$GITHUB_REF_NAME...HEAD" | head -5
24+
else
25+
echo "WARNING: origin/$GITHUB_REF_NAME not found, using HEAD~1"
26+
git log --oneline HEAD~1..HEAD
27+
fi
28+
echo ""
29+
30+
echo "=== Step 2: Compute impacted modules ==="
31+
cd "$SCRIPT_DIR/../.."
32+
echo "Working directory: $(pwd)"
33+
34+
# Test different git diff strategies locally
35+
echo "--- Testing git diff strategies ---"
36+
37+
echo "Strategy 1: origin/$GITHUB_REF_NAME...HEAD (three-dot)"
38+
FILES_3DOT=$(git diff --name-only "origin/$GITHUB_REF_NAME...HEAD" 2>/dev/null || echo "")
39+
echo "Files found: $(echo "$FILES_3DOT" | wc -l)"
40+
echo "$FILES_3DOT" | head -3
41+
42+
echo ""
43+
echo "Strategy 2: origin/$GITHUB_REF_NAME..HEAD (two-dot)"
44+
FILES_2DOT=$(git diff --name-only "origin/$GITHUB_REF_NAME..HEAD" 2>/dev/null || echo "")
45+
echo "Files found: $(echo "$FILES_2DOT" | wc -l)"
46+
echo "$FILES_2DOT" | head -3
47+
48+
echo ""
49+
echo "Strategy 3: HEAD~1..HEAD (single commit)"
50+
FILES_1COMMIT=$(git diff --name-only "HEAD~1..HEAD" 2>/dev/null || echo "")
51+
echo "Files found: $(echo "$FILES_1COMMIT" | wc -l)"
52+
echo "$FILES_1COMMIT" | head -3
53+
54+
echo ""
55+
echo "--- Running test_discovery.py ---"
56+
MODS=$(python3 .github/scripts/test_discovery.py modules-from-diff --base "origin/$GITHUB_REF_NAME" --verbose 2>&1)
57+
MODULE_LIST=$(echo "$MODS" | tail -1)
58+
59+
echo "Script output:"
60+
echo "$MODS"
61+
echo ""
62+
echo "Final modules: '$MODULE_LIST'"
63+
64+
echo ""
65+
echo "=== Step 3: Test build logic ==="
66+
if [ -z "$MODULE_LIST" ]; then
67+
echo "ERROR: No modules detected - git diff failed to find changes"
68+
echo "This likely indicates a problem with the git diff strategy"
69+
echo "Failing fast to avoid wasted resources and investigate the issue"
70+
echo "Check the 'Compute impacted modules' step output for debugging info"
71+
exit 1
72+
else
73+
echo "SUCCESS: Would build modules: $MODULE_LIST"
74+
echo "Build command would be:"
75+
echo "./mvnw -B -T 1C -Pintegration-tests -DfailIfNoTests=false -pl \"$MODULE_LIST\" -amd verify"
76+
fi
77+
78+
echo ""
79+
echo "=== Local test complete ==="

.github/workflows/maintenance-fast.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,11 @@ jobs:
4747
SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
4848
run: |
4949
MODS="${{ steps.mods.outputs.modules }}"
50-
if [ -z "$MODS" ]; then MODS="."; fi
50+
if [ -z "$MODS" ]; then
51+
echo "ERROR: No modules detected - git diff failed to find changes"
52+
echo "This likely indicates a problem with the git diff strategy"
53+
echo "Failing fast to avoid wasted resources and investigate the issue"
54+
echo "Check the 'Compute impacted modules' step output for debugging info"
55+
exit 1
56+
fi
5157
./mvnw -B -T 1C -Pintegration-tests -DfailIfNoTests=false -pl "$MODS" -amd verify

0 commit comments

Comments
 (0)