Skip to content

Commit db55a19

Browse files
Fix Release Stage 1 race condition using max-parallel constraint (#228)
* Initial plan * Fix Release Stage 1 race condition by implementing sequential job execution - Replace parallel matrix strategy with sequential jobs - Add proper job dependencies (alpha -> beta -> stable) - Use always() conditions for error resilience - Maintain conditional execution based on release types - Add comprehensive test suite for validation Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> * Implement simpler race condition fix using max-parallel: 1 Replace sequential jobs approach with cleaner matrix strategy constraint: - Revert to original matrix structure - Add max-parallel: 1 to prevent race conditions - Update tests and documentation for new approach - Much simpler solution maintaining DRY principles Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com>
1 parent 8956534 commit db55a19

File tree

3 files changed

+213
-0
lines changed

3 files changed

+213
-0
lines changed

.github/workflows/release-stage-1_update_dependencies.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ jobs:
4545
name: Update ${{ matrix.release_type }} Dependencies
4646
needs: determine-release-types
4747
strategy:
48+
max-parallel: 1
4849
matrix: ${{ fromJson(needs.determine-release-types.outputs.matrix) }}
4950
runs-on: ubuntu-latest
5051
permissions:

RACE_CONDITION_FIX.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Race Condition Fix for Release Stage 1 Workflow
2+
3+
This document explains the fix implemented for the race condition in the Release Stage 1 workflow that was causing "GraphQL: Base branch was modified. Review and try the merge again" errors.
4+
5+
## Problem
6+
7+
The Release Stage 1 workflow was failing with race condition errors when multiple dependency update bots (alpha, beta, stable) tried to merge PRs simultaneously:
8+
9+
1. All three release types would run in parallel using the matrix strategy
10+
2. Each would create PRs and attempt to auto-merge them at the same time
11+
3. When one job merged its PR (modifying the base branch), other jobs would fail with "Base branch was modified"
12+
4. This resulted in workflow failures like run #17525831245
13+
14+
## Solution
15+
16+
The fix was implemented by adding a simple `max-parallel: 1` constraint to the existing matrix strategy:
17+
18+
```yaml
19+
strategy:
20+
max-parallel: 1
21+
matrix: ${{ fromJson(needs.determine-release-types.outputs.matrix) }}
22+
```
23+
24+
## Benefits
25+
26+
This approach provides several advantages:
27+
28+
- **✅ Simple and minimal** - Only one line added to the existing workflow
29+
- **✅ Preserves matrix structure** - Maintains the clean matrix approach instead of duplicating jobs
30+
- **✅ Prevents race conditions** - Ensures only one bot operates at a time
31+
- **✅ No code duplication** - Keeps the DRY principle intact
32+
- **✅ Easy to understand** - Clear intent and implementation
33+
- **✅ Maintains existing logic** - All conditional execution and matrix variables preserved
34+
35+
## How It Works
36+
37+
The `max-parallel: 1` constraint ensures that:
38+
39+
1. The matrix still defines all three jobs (alpha, beta, stable)
40+
2. GitHub Actions will only run one matrix job at a time
41+
3. Jobs execute sequentially, preventing simultaneous PR merge attempts
42+
4. Race conditions are eliminated while preserving the clean matrix structure
43+
44+
## Testing
45+
46+
The fix includes a comprehensive test suite that validates:
47+
48+
- YAML syntax correctness
49+
- Matrix strategy with max-parallel constraint
50+
- Single job structure (not separate sequential jobs)
51+
- Matrix variable usage preservation
52+
- Proper race condition prevention
53+
54+
Run tests with:
55+
56+
```bash
57+
./test/test-race-condition-fix.sh
58+
```
59+
60+
This fix resolves the race condition without the complexity of separate sequential jobs, making it both effective and maintainable.

test/test-race-condition-fix.sh

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#!/bin/bash
2+
3+
# Test script to validate the race condition fix for Release Stage 1
4+
# This script validates that the workflow uses max-parallel: 1 to prevent race conditions
5+
6+
set -e
7+
8+
echo "🧪 Testing Race Condition Fix for Release Stage 1..."
9+
echo
10+
11+
# Test 1: Validate YAML syntax
12+
echo "Test 1: Validating YAML syntax..."
13+
if python3 -c "import yaml; yaml.safe_load(open('.github/workflows/release-stage-1_update_dependencies.yml'))" 2>/dev/null; then
14+
echo "✅ YAML syntax is valid"
15+
else
16+
echo "❌ YAML syntax error"
17+
exit 1
18+
fi
19+
20+
# Test 2: Check that matrix strategy still exists but with max-parallel constraint
21+
echo
22+
echo "Test 2: Checking matrix strategy with max-parallel constraint..."
23+
WORKFLOW_FILE=".github/workflows/release-stage-1_update_dependencies.yml"
24+
25+
# Check that matrix strategy is present
26+
if grep -q "strategy:" "$WORKFLOW_FILE"; then
27+
echo "✅ Matrix strategy found"
28+
else
29+
echo "❌ Matrix strategy missing"
30+
exit 1
31+
fi
32+
33+
# Check that matrix configuration is present
34+
if grep -q "matrix:" "$WORKFLOW_FILE"; then
35+
echo "✅ Matrix configuration found"
36+
else
37+
echo "❌ Matrix configuration missing"
38+
exit 1
39+
fi
40+
41+
# Test 3: Check that max-parallel: 1 constraint is present
42+
echo
43+
echo "Test 3: Checking max-parallel constraint..."
44+
45+
if grep -q "max-parallel: 1" "$WORKFLOW_FILE"; then
46+
echo "✅ max-parallel: 1 constraint found"
47+
else
48+
echo "❌ max-parallel: 1 constraint missing"
49+
exit 1
50+
fi
51+
52+
# Test 4: Check that single update_dependencies job exists (not separate jobs)
53+
echo
54+
echo "Test 4: Checking single matrix job structure..."
55+
56+
if grep -q "update_dependencies:" "$WORKFLOW_FILE"; then
57+
echo "✅ Single update_dependencies job found"
58+
else
59+
echo "❌ update_dependencies job missing"
60+
exit 1
61+
fi
62+
63+
# Ensure separate alpha/beta/stable jobs don't exist (those were from the old approach)
64+
if grep -q "update_alpha_dependencies:" "$WORKFLOW_FILE"; then
65+
echo "❌ Separate alpha job found (should use matrix instead)"
66+
exit 1
67+
else
68+
echo "✅ No separate alpha job (using matrix approach correctly)"
69+
fi
70+
71+
# Ensure separate beta/stable jobs don't exist (those were from the old approach)
72+
if grep -q "update_beta_dependencies:" "$WORKFLOW_FILE"; then
73+
echo "❌ Separate beta job found (should use matrix instead)"
74+
exit 1
75+
else
76+
echo "✅ No separate beta job (using matrix approach correctly)"
77+
fi
78+
79+
if grep -q "update_stable_dependencies:" "$WORKFLOW_FILE"; then
80+
echo "❌ Separate stable job found (should use matrix instead)"
81+
exit 1
82+
else
83+
echo "✅ No separate stable job (using matrix approach correctly)"
84+
fi
85+
86+
# Test 5: Check that the job still uses matrix variables properly
87+
echo
88+
echo "Test 5: Checking matrix variable usage..."
89+
90+
if grep -q "\${{ matrix.release_type }}" "$WORKFLOW_FILE"; then
91+
echo "✅ Matrix release_type variable found"
92+
else
93+
echo "❌ Matrix release_type variable missing"
94+
exit 1
95+
fi
96+
97+
if grep -q "\${{ matrix.config_file }}" "$WORKFLOW_FILE"; then
98+
echo "✅ Matrix config_file variable found"
99+
else
100+
echo "❌ Matrix config_file variable missing"
101+
exit 1
102+
fi
103+
104+
# Test 6: Check that the job name uses matrix variable
105+
echo
106+
echo "Test 6: Checking matrix job name..."
107+
108+
if grep -q "Update \${{ matrix.release_type }} Dependencies" "$WORKFLOW_FILE"; then
109+
echo "✅ Job name uses matrix variable correctly"
110+
else
111+
echo "❌ Job name does not use matrix variable"
112+
exit 1
113+
fi
114+
115+
# Test 7: Verify that race condition is prevented
116+
echo
117+
echo "Test 7: Verifying race condition prevention..."
118+
119+
# Check that max-parallel and matrix are in the same strategy block
120+
STRATEGY_SECTION=$(python3 -c "
121+
import yaml
122+
with open('$WORKFLOW_FILE') as f:
123+
w = yaml.safe_load(f)
124+
strategy = w['jobs']['update_dependencies']['strategy']
125+
print('max-parallel' in strategy and 'matrix' in strategy)
126+
" 2>/dev/null)
127+
128+
if [[ "$STRATEGY_SECTION" == "True" ]]; then
129+
echo "✅ max-parallel and matrix are properly configured in strategy"
130+
else
131+
echo "❌ max-parallel and matrix not properly configured"
132+
exit 1
133+
fi
134+
135+
echo
136+
echo "🎉 All tests passed! The race condition fix is properly implemented."
137+
echo
138+
echo "Summary of changes:"
139+
echo " - ✅ Kept the clean matrix strategy structure"
140+
echo " - ✅ Added max-parallel: 1 constraint to prevent race conditions"
141+
echo " - ✅ Maintained all existing matrix variable usage"
142+
echo " - ✅ Preserved conditional execution logic"
143+
echo " - ✅ Much simpler solution than sequential jobs"
144+
echo
145+
echo "This should resolve the race condition that caused:"
146+
echo " - 'Base branch was modified. Review and try the merge again' errors"
147+
echo " - Failed PR merges when multiple bots run simultaneously"
148+
echo " - Release Stage 1 workflow failures"
149+
echo
150+
echo "The max-parallel: 1 constraint ensures that even though the matrix"
151+
echo "defines multiple jobs (alpha, beta, stable), only one will run at a time,"
152+
echo "preventing the race condition while maintaining the clean matrix structure."

0 commit comments

Comments
 (0)