Skip to content

Commit de5cd35

Browse files
committed
Optimize CI/CD for faster PR merge feedback
- Add new main-push-fast.yml workflow for quick feedback on main branch pushes - Uses test_discovery.py to run only affected module tests (10-20 min vs 1.5 hrs) - Convert continuous-integration.yml from push-triggered to scheduled builds - Schedule covers EST and CET working hours with 10 builds per weekday (9 AM - 9 PM) - Enhanced test_discovery.py to properly handle main branch commits - Maintains full test coverage while enabling rapid PR merge cycles - Builds spaced 2.5+ hours apart to prevent overlap with 90-minute build duration Signed-off-by: [email protected]
1 parent 8caffe8 commit de5cd35

File tree

3 files changed

+150
-12
lines changed

3 files changed

+150
-12
lines changed

.github/scripts/test_discovery.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ def _get_changed_files(self, base_ref: Optional[str] = None) -> List[str]:
6262
pr_head = os.environ.get('GITHUB_HEAD_HEAD') # PRs
6363
branch = os.environ.get('GITHUB_REF_NAME') # pushes
6464

65-
# For maintenance branches (cherry-picks), use single commit diff
66-
if branch and branch.endswith('.x'):
67-
# Maintenance branch - use diff with previous commit
65+
# For maintenance branches (cherry-picks) or main branch pushes, use single commit diff
66+
if (branch and branch.endswith('.x')) or (branch == 'main'):
67+
# Maintenance branch or main branch - use diff with previous commit
6868
cmd = ["git", "diff", "--name-only", "HEAD~1", "HEAD"]
6969
elif base_ref:
7070
# Explicit base reference provided - use two-dot diff for direct comparison
@@ -179,8 +179,9 @@ def _detect_default_base(self) -> str:
179179
branch = os.environ.get('GITHUB_REF_NAME')
180180

181181
# Show the actual strategy being used
182-
if branch and branch.endswith('.x'):
183-
return f"git diff HEAD~1 HEAD (maintenance branch {branch})"
182+
if (branch and branch.endswith('.x')) or (branch == 'main'):
183+
branch_type = "maintenance" if branch.endswith('.x') else "main"
184+
return f"git diff HEAD~1 HEAD ({branch_type} branch {branch})"
184185
elif pr_base:
185186
return f"origin/{pr_base} (PR base)"
186187
elif branch:

.github/workflows/continuous-integration.yml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
name: CI/CD build
22

33
on:
4-
push:
5-
branches:
6-
- main
7-
paths-ignore:
8-
- 'spring-ai-docs/**'
9-
- '*.md'
10-
- 'docs/**'
4+
schedule:
5+
# EST Working Hours (9 AM - 9 PM) - 5 builds per day (spaced 2.5+ hours apart for 90min builds)
6+
- cron: '30 12 * * 1-5' # 7:30 AM EST / 1:30 PM CET
7+
- cron: '0 15 * * 1-5' # 10:00 AM EST / 4:00 PM CET
8+
- cron: '30 18 * * 1-5' # 1:30 PM EST / 7:30 PM CET
9+
- cron: '0 21 * * 1-5' # 4:00 PM EST / 10:00 PM CET
10+
- cron: '0 2 * * 2-6' # 9:00 PM EST / 3:00 AM CET (next day)
11+
# CET Working Hours (9 AM - 9 PM) - 5 builds per day (spaced 2.5+ hours apart for 90min builds)
12+
- cron: '30 6 * * 1-5' # 7:30 AM CET / 1:30 AM EST
13+
- cron: '0 9 * * 1-5' # 10:00 AM CET / 4:00 AM EST
14+
- cron: '30 12 * * 1-5' # 1:30 PM CET / 7:30 AM EST
15+
- cron: '0 15 * * 1-5' # 4:00 PM CET / 10:00 AM EST
16+
- cron: '0 19 * * 1-5' # 9:00 PM CET / 3:00 PM EST
1117
workflow_dispatch:
1218

1319
jobs:
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
name: Main Push - Fast
2+
3+
on:
4+
push:
5+
branches: ['main']
6+
paths-ignore:
7+
- 'spring-ai-docs/**'
8+
- '*.md'
9+
- 'docs/**'
10+
11+
jobs:
12+
fast-impacted:
13+
name: Fast Build - Affected Modules
14+
runs-on: ubuntu-latest
15+
if: ${{ github.repository_owner == 'spring-projects' }}
16+
permissions:
17+
contents: read
18+
concurrency:
19+
group: ${{ github.workflow }}-${{ github.ref }}
20+
cancel-in-progress: true
21+
services:
22+
ollama:
23+
image: ollama/ollama:latest
24+
ports:
25+
- 11434:11434
26+
env:
27+
OLLAMA_WITH_REUSE: true
28+
steps:
29+
- uses: actions/checkout@v4
30+
with: { fetch-depth: 2 } # Need HEAD and HEAD~1 for single commit diff
31+
32+
- name: Free Disk Space
33+
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
34+
with:
35+
large-packages: false
36+
docker-images: false
37+
38+
- uses: actions/setup-java@v4
39+
with:
40+
java-version: '17'
41+
distribution: 'temurin'
42+
cache: 'maven'
43+
44+
- name: Set up Python
45+
uses: actions/setup-python@v5
46+
with:
47+
python-version: '3.11'
48+
49+
- name: Configure Testcontainers
50+
run: |
51+
echo "testcontainers.reuse.enable=true" > $HOME/.testcontainers.properties
52+
53+
- name: Show commit range
54+
run: |
55+
echo "Base ref: HEAD~1"
56+
git log --oneline "HEAD~1...HEAD"
57+
58+
- name: Compute impacted modules
59+
id: mods
60+
run: |
61+
echo "=== Module Detection Debug Info ==="
62+
echo "Environment variables:"
63+
echo " GITHUB_REF_NAME: $GITHUB_REF_NAME"
64+
echo " GITHUB_REF: $GITHUB_REF"
65+
echo " PWD: $(pwd)"
66+
echo ""
67+
68+
echo "Git state verification:"
69+
echo " HEAD: $(git rev-parse HEAD 2>/dev/null || echo 'FAILED')"
70+
echo " HEAD~1: $(git rev-parse HEAD~1 2>/dev/null || echo 'NOT AVAILABLE')"
71+
echo " Branch: $(git branch --show-current 2>/dev/null || echo 'DETACHED')"
72+
echo ""
73+
74+
echo "Testing different git diff approaches:"
75+
echo "1. HEAD~1..HEAD:"
76+
git diff --name-only HEAD~1..HEAD 2>&1 | head -10 || echo " FAILED: $?"
77+
78+
echo "2. git show HEAD:"
79+
git show --name-only --format= HEAD 2>&1 | head -10 || echo " FAILED: $?"
80+
81+
echo "3. Recent commits:"
82+
git log --oneline -3 2>/dev/null || echo " Git log failed"
83+
echo ""
84+
85+
echo "=== Running test_discovery.py for main branch ==="
86+
set -x # Enable bash debug mode
87+
MODS=$(python3 .github/scripts/test_discovery.py modules-from-diff --verbose 2>&1)
88+
EXIT_CODE=$?
89+
set +x # Disable bash debug mode
90+
91+
echo ""
92+
echo "=== Test Discovery Results ==="
93+
echo "Exit code: $EXIT_CODE"
94+
echo "Output:"
95+
echo "$MODS"
96+
echo ""
97+
98+
# Extract just the module list (last line that isn't stderr logging)
99+
MODULE_LIST=$(echo "$MODS" | grep -v "^Detected base ref:" | grep -v "^Changed files" | grep -v "^Final module list:" | tail -1)
100+
echo "Extracted modules: '$MODULE_LIST'"
101+
echo "modules=$MODULE_LIST" >> "$GITHUB_OUTPUT"
102+
103+
- name: Test affected modules with integration tests
104+
env:
105+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
106+
SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
107+
ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
108+
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
109+
OLLAMA_AUTOCONF_TESTS_ENABLED: "true"
110+
OLLAMA_WITH_REUSE: true
111+
run: |
112+
MODS="${{ steps.mods.outputs.modules }}"
113+
if [ -z "$MODS" ]; then
114+
echo "INFO: No affected modules detected - running quick verification build"
115+
echo "This could mean no Java/build files were changed, or only docs were modified"
116+
echo "Running a minimal compile check to ensure the build isn't broken"
117+
./mvnw -B -T 1C compile -DfailIfNoTests=false
118+
else
119+
echo "INFO: Running tests for affected modules: $MODS"
120+
./mvnw -B -T 1C -Pci-fast-integration-tests -DfailIfNoTests=false -pl "$MODS" -amd verify
121+
fi
122+
123+
- name: Deploy to Artifactory (affected modules only)
124+
if: steps.mods.outputs.modules != ''
125+
env:
126+
ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
127+
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
128+
run: |
129+
MODS="${{ steps.mods.outputs.modules }}"
130+
echo "INFO: Deploying affected modules to Artifactory: $MODS"
131+
./mvnw -B -s settings.xml -DfailIfNoTests=false -pl "$MODS" -amd deploy

0 commit comments

Comments
 (0)