|
| 1 | +# GitHub Actions Scripts |
| 2 | + |
| 3 | +This directory contains scripts used by GitHub Actions workflows for the Spring AI project. |
| 4 | + |
| 5 | +## test_discovery.py |
| 6 | + |
| 7 | +A Python script that determines which Maven modules are affected by changes in a PR or push, enabling efficient CI builds that only test modified code. |
| 8 | + |
| 9 | +### Usage |
| 10 | + |
| 11 | +```bash |
| 12 | +# Basic usage (auto-detects context) |
| 13 | +python3 .github/scripts/test_discovery.py modules-from-diff |
| 14 | + |
| 15 | +# With explicit base reference (for maintenance branches) |
| 16 | +python3 .github/scripts/test_discovery.py modules-from-diff --base origin/1.0.x |
| 17 | + |
| 18 | +# With verbose logging (debugging) |
| 19 | +python3 .github/scripts/test_discovery.py modules-from-diff --verbose |
| 20 | + |
| 21 | +# Combined options |
| 22 | +python3 .github/scripts/test_discovery.py modules-from-diff --base origin/1.0.x --verbose |
| 23 | +``` |
| 24 | + |
| 25 | +### CLI Options |
| 26 | + |
| 27 | +- `--base <ref>`: Explicit base reference for git diff (e.g., `origin/1.0.x`) |
| 28 | +- `--verbose`: Show detailed logging to stderr including detected base, changed files, and final module list |
| 29 | + |
| 30 | +### Output |
| 31 | + |
| 32 | +- **Empty string**: No modules affected (documentation/config changes only) |
| 33 | +- **Comma-separated list**: Module paths suitable for `mvn -pl` parameter |
| 34 | + |
| 35 | +### Examples |
| 36 | + |
| 37 | +```bash |
| 38 | +# Single module affected |
| 39 | +$ python3 .github/scripts/test_discovery.py modules-from-diff |
| 40 | +vector-stores/spring-ai-qdrant-store |
| 41 | + |
| 42 | +# Multiple modules affected |
| 43 | +$ python3 .github/scripts/test_discovery.py modules-from-diff |
| 44 | +vector-stores/spring-ai-qdrant-store,models/spring-ai-openai |
| 45 | + |
| 46 | +# No code changes (docs only) |
| 47 | +$ python3 .github/scripts/test_discovery.py modules-from-diff |
| 48 | + |
| 49 | +# Verbose output (to stderr) |
| 50 | +$ python3 .github/scripts/test_discovery.py modules-from-diff --verbose |
| 51 | +vector-stores/spring-ai-qdrant-store |
| 52 | +Detected base ref: origin/main (merge-base) |
| 53 | +Changed files (2): |
| 54 | + - vector-stores/spring-ai-qdrant-store/src/main/java/QdrantVectorStore.java |
| 55 | + - vector-stores/spring-ai-qdrant-store/src/test/java/QdrantTests.java |
| 56 | +Final module list: vector-stores/spring-ai-qdrant-store |
| 57 | + |
| 58 | +# Maintenance branch with explicit base |
| 59 | +$ python3 .github/scripts/test_discovery.py modules-from-diff --base origin/1.0.x |
| 60 | +vector-stores/spring-ai-qdrant-store |
| 61 | +``` |
| 62 | + |
| 63 | +### Integration with GitHub Actions |
| 64 | + |
| 65 | +#### PR-based builds (`_java-build` reusable workflow): |
| 66 | + |
| 67 | +```yaml |
| 68 | +- name: Compute impacted modules (optional) |
| 69 | + id: mods |
| 70 | + if: inputs.mode == 'impacted' |
| 71 | + run: | |
| 72 | + MODS=$(python3 .github/scripts/test_discovery.py modules-from-diff) |
| 73 | + echo "modules=$MODS" >> $GITHUB_OUTPUT |
| 74 | +
|
| 75 | +- name: Build |
| 76 | + run: | |
| 77 | + case "${{ inputs.mode }}" in |
| 78 | + impacted) |
| 79 | + MODS="${{ steps.mods.outputs.modules }}" |
| 80 | + ./mvnw -B -T 1C -DskipITs -DfailIfNoTests=false -pl "${MODS}" -amd verify |
| 81 | + ;; |
| 82 | + esac |
| 83 | +``` |
| 84 | +
|
| 85 | +#### Maintenance branch fast builds (`maintenance-fast.yml`): |
| 86 | + |
| 87 | +```yaml |
| 88 | +- name: Compute impacted modules |
| 89 | + id: mods |
| 90 | + run: | |
| 91 | + MODS=$(python3 .github/scripts/test_discovery.py modules-from-diff --base "origin/$GITHUB_REF_NAME" --verbose) |
| 92 | + echo "modules=$MODS" >> $GITHUB_OUTPUT |
| 93 | +
|
| 94 | +- name: Fast compile + unit tests |
| 95 | + run: | |
| 96 | + MODS="${{ steps.mods.outputs.modules }}" |
| 97 | + if [ -z "$MODS" ]; then MODS="."; fi |
| 98 | + ./mvnw -B -T 1C -DskipITs -DfailIfNoTests=false -pl "$MODS" -amd verify |
| 99 | +``` |
| 100 | + |
| 101 | +### Algorithm |
| 102 | + |
| 103 | +The script: |
| 104 | + |
| 105 | +1. **Detects changed files** using `git diff` against the appropriate base branch |
| 106 | +2. **Maps files to Maven modules** by walking up directory tree to find `pom.xml` |
| 107 | +3. **Filters relevant files** (Java source, tests, resources, build files) |
| 108 | +4. **Returns module paths** in Maven-compatible format |
| 109 | + |
| 110 | +### Environment Variables |
| 111 | + |
| 112 | +The script automatically detects the CI context using: |
| 113 | + |
| 114 | +- `GITHUB_BASE_REF`: Base branch for PR builds |
| 115 | +- `GITHUB_HEAD_REF`: Head branch for PR builds |
| 116 | +- `GITHUB_REF_NAME`: Current branch for push builds (maintenance branches) |
| 117 | +- Falls back to `origin/main` merge base when context unclear |
| 118 | + |
| 119 | +### Context Detection Logic |
| 120 | + |
| 121 | +1. **Explicit `--base`**: Use provided reference directly |
| 122 | +2. **PR Context**: Compare against `origin/$GITHUB_BASE_REF` |
| 123 | +3. **Push Context**: Compare against `origin/$GITHUB_REF_NAME` |
| 124 | +4. **Fallback**: Find merge base with `origin/main` |
| 125 | + |
| 126 | +### Error Handling |
| 127 | + |
| 128 | +- Returns empty string on errors to gracefully fall back to full builds |
| 129 | +- Logs errors to stderr for debugging |
| 130 | +- Never fails the CI pipeline due to discovery issues |
| 131 | + |
| 132 | +## Fast Maintenance Branch Workflow |
| 133 | + |
| 134 | +### Overview |
| 135 | + |
| 136 | +The `maintenance-fast.yml` workflow provides efficient CI builds for maintenance branch cherry-picks: |
| 137 | + |
| 138 | +- **Triggers**: Only on pushes to `*.*.x` branches (e.g., `1.0.x`, `1.1.x`) |
| 139 | +- **Cherry-pick Guard**: Job-level guard prevents runner startup unless commit message contains "(cherry picked from commit" |
| 140 | +- **Fast Execution**: Unit tests only (skips integration tests) |
| 141 | +- **Smart Targeting**: Only tests affected modules using test discovery |
| 142 | + |
| 143 | +### Features |
| 144 | + |
| 145 | +- **Job-level Guard**: `if: contains(github.event.head_commit.message, '(cherry picked from commit')` |
| 146 | +- **Explicit Base**: Uses `--base origin/$GITHUB_REF_NAME` for accurate multi-commit diff |
| 147 | +- **Verbose Logging**: Shows commit range and detailed test discovery output |
| 148 | +- **Safe Fallback**: Compiles root (`.`) if no modules detected |
| 149 | +- **Concurrency Control**: Cancels superseded runs automatically |
| 150 | + |
| 151 | +### Example Output |
| 152 | + |
| 153 | +``` |
| 154 | +Base ref: origin/1.0.x |
| 155 | +3b59e6840 test: Enhances test coverage for QdrantObjectFactory.toObjectMap |
| 156 | + |
| 157 | +Detected base ref: origin/1.0.x |
| 158 | +Changed files (1): |
| 159 | + - vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantObjectFactoryTests.java |
| 160 | +Final module list: vector-stores/spring-ai-qdrant-store |
| 161 | + |
| 162 | +[INFO] Building Spring AI Qdrant Store 1.0.1-SNAPSHOT |
| 163 | +[INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0 |
| 164 | +[INFO] BUILD SUCCESS |
| 165 | +``` |
| 166 | +
|
| 167 | +### Safety Measures |
| 168 | +
|
| 169 | +- **Cherry-pick Only**: Won't run on manual pushes to maintenance branches |
| 170 | +- **Nightly Safety Net**: Full integration test builds still run daily |
| 171 | +- **Error Handling**: Falls back to root compilation if module detection fails |
| 172 | +- **Minimal Permissions**: `contents: read` only |
0 commit comments