From c4acf95a92ab6bdbb8c1f0ecdc153b2f394db620 Mon Sep 17 00:00:00 2001 From: Eric Bottard Date: Tue, 9 Dec 2025 16:29:09 +0100 Subject: [PATCH] Rework various CI workflows into a single one. Trigger on main and dev branches (1.0.x, 1.1.x, etc) Do a maven build and install, optimized with maven buildcache. Save generated artifacts and make them available to subsequent jobs. Run ITs in separate jobs. Docs: trigger antora build workflow, generate aggregate javadocs archive and upload/unzip it. Deploy generated artifacts to artifactory. The workflow gets canceled if a push to the same branch happens while it's running. Signed-off-by: Eric Bottard --- .github/scripts/README.md | 172 ----------- .github/scripts/test_discovery.py | 257 ---------------- .github/scripts/test_local.sh | 79 ----- .github/workflows/continuous-integration.yml | 291 ++++++++++++++---- .github/workflows/deploy-docs.yml | 25 +- .../workflows/fast-continuous-integration.yml | 289 ----------------- .github/workflows/main-push-fast.yml | 158 ---------- .github/workflows/maintenance-fast.yml | 95 ------ ...-release.yml => maven-central-release.yml} | 0 .../ai/chat/client/package-info.java | 2 +- spring-ai-docs/src/assembly/javadocs.xml | 31 -- 11 files changed, 225 insertions(+), 1174 deletions(-) delete mode 100644 .github/scripts/README.md delete mode 100755 .github/scripts/test_discovery.py delete mode 100755 .github/scripts/test_local.sh delete mode 100644 .github/workflows/fast-continuous-integration.yml delete mode 100644 .github/workflows/main-push-fast.yml delete mode 100644 .github/workflows/maintenance-fast.yml rename .github/workflows/{new-maven-central-release.yml => maven-central-release.yml} (100%) delete mode 100644 spring-ai-docs/src/assembly/javadocs.xml diff --git a/.github/scripts/README.md b/.github/scripts/README.md deleted file mode 100644 index 189bdd07dc4..00000000000 --- a/.github/scripts/README.md +++ /dev/null @@ -1,172 +0,0 @@ -# GitHub Actions Scripts - -This directory contains scripts used by GitHub Actions workflows for the Spring AI project. - -## test_discovery.py - -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. - -### Usage - -```bash -# Basic usage (auto-detects context) -python3 .github/scripts/test_discovery.py modules-from-diff - -# With explicit base reference (for maintenance branches) -python3 .github/scripts/test_discovery.py modules-from-diff --base origin/1.0.x - -# With verbose logging (debugging) -python3 .github/scripts/test_discovery.py modules-from-diff --verbose - -# Combined options -python3 .github/scripts/test_discovery.py modules-from-diff --base origin/1.0.x --verbose -``` - -### CLI Options - -- `--base `: Explicit base reference for git diff (e.g., `origin/1.0.x`) -- `--verbose`: Show detailed logging to stderr including detected base, changed files, and final module list - -### Output - -- **Empty string**: No modules affected (documentation/config changes only) -- **Comma-separated list**: Module paths suitable for `mvn -pl` parameter - -### Examples - -```bash -# Single module affected -$ python3 .github/scripts/test_discovery.py modules-from-diff -vector-stores/spring-ai-qdrant-store - -# Multiple modules affected -$ python3 .github/scripts/test_discovery.py modules-from-diff -vector-stores/spring-ai-qdrant-store,models/spring-ai-openai - -# No code changes (docs only) -$ python3 .github/scripts/test_discovery.py modules-from-diff - -# Verbose output (to stderr) -$ python3 .github/scripts/test_discovery.py modules-from-diff --verbose -vector-stores/spring-ai-qdrant-store -Detected base ref: origin/main (merge-base) -Changed files (2): - - vector-stores/spring-ai-qdrant-store/src/main/java/QdrantVectorStore.java - - vector-stores/spring-ai-qdrant-store/src/test/java/QdrantTests.java -Final module list: vector-stores/spring-ai-qdrant-store - -# Maintenance branch with explicit base -$ python3 .github/scripts/test_discovery.py modules-from-diff --base origin/1.0.x -vector-stores/spring-ai-qdrant-store -``` - -### Integration with GitHub Actions - -#### PR-based builds (`_java-build` reusable workflow): - -```yaml -- name: Compute impacted modules (optional) - id: mods - if: inputs.mode == 'impacted' - run: | - MODS=$(python3 .github/scripts/test_discovery.py modules-from-diff) - echo "modules=$MODS" >> $GITHUB_OUTPUT - -- name: Build - run: | - case "${{ inputs.mode }}" in - impacted) - MODS="${{ steps.mods.outputs.modules }}" - ./mvnw -B -T 1C -DskipITs -DfailIfNoTests=false -pl "${MODS}" -amd verify - ;; - esac -``` - -#### Maintenance branch fast builds (`maintenance-fast.yml`): - -```yaml -- name: Compute impacted modules - id: mods - run: | - MODS=$(python3 .github/scripts/test_discovery.py modules-from-diff --base "origin/$GITHUB_REF_NAME" --verbose) - echo "modules=$MODS" >> $GITHUB_OUTPUT - -- name: Fast compile + unit tests - run: | - MODS="${{ steps.mods.outputs.modules }}" - if [ -z "$MODS" ]; then MODS="."; fi - ./mvnw -B -T 1C -DskipITs -DfailIfNoTests=false -pl "$MODS" -amd verify -``` - -### Algorithm - -The script: - -1. **Detects changed files** using `git diff` against the appropriate base branch -2. **Maps files to Maven modules** by walking up directory tree to find `pom.xml` -3. **Filters relevant files** (Java source, tests, resources, build files) -4. **Returns module paths** in Maven-compatible format - -### Environment Variables - -The script automatically detects the CI context using: - -- `GITHUB_BASE_REF`: Base branch for PR builds -- `GITHUB_HEAD_REF`: Head branch for PR builds -- `GITHUB_REF_NAME`: Current branch for push builds (maintenance branches) -- Falls back to `origin/main` merge base when context unclear - -### Context Detection Logic - -1. **Explicit `--base`**: Use provided reference directly -2. **PR Context**: Compare against `origin/$GITHUB_BASE_REF` -3. **Push Context**: Compare against `origin/$GITHUB_REF_NAME` -4. **Fallback**: Find merge base with `origin/main` - -### Error Handling - -- Returns empty string on errors to gracefully fall back to full builds -- Logs errors to stderr for debugging -- Never fails the CI pipeline due to discovery issues - -## Fast Maintenance Branch Workflow - -### Overview - -The `maintenance-fast.yml` workflow provides efficient CI builds for maintenance branch cherry-picks: - -- **Triggers**: Only on pushes to `*.*.x` branches (e.g., `1.0.x`, `1.1.x`) -- **Cherry-pick Guard**: Job-level guard prevents runner startup unless commit message contains "(cherry picked from commit" -- **Fast Execution**: Unit tests only (skips integration tests) -- **Smart Targeting**: Only tests affected modules using test discovery - -### Features - -- **Job-level Guard**: `if: contains(github.event.head_commit.message, '(cherry picked from commit')` -- **Explicit Base**: Uses `--base origin/$GITHUB_REF_NAME` for accurate multi-commit diff -- **Verbose Logging**: Shows commit range and detailed test discovery output -- **Safe Fallback**: Compiles root (`.`) if no modules detected -- **Concurrency Control**: Cancels superseded runs automatically - -### Example Output - -``` -Base ref: origin/1.0.x -3b59e6840 test: Enhances test coverage for QdrantObjectFactory.toObjectMap - -Detected base ref: origin/1.0.x -Changed files (1): - - vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantObjectFactoryTests.java -Final module list: vector-stores/spring-ai-qdrant-store - -[INFO] Building Spring AI Qdrant Store 1.0.1-SNAPSHOT -[INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0 -[INFO] BUILD SUCCESS -``` - -### Safety Measures - -- **Cherry-pick Only**: Won't run on manual pushes to maintenance branches -- **Nightly Safety Net**: Full integration test builds still run daily -- **Error Handling**: Falls back to root compilation if module detection fails -- **Minimal Permissions**: `contents: read` only \ No newline at end of file diff --git a/.github/scripts/test_discovery.py b/.github/scripts/test_discovery.py deleted file mode 100755 index 93c105769f3..00000000000 --- a/.github/scripts/test_discovery.py +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/env python3 -""" -GitHub Actions compatible test discovery script -Outputs comma-separated list of affected modules for CI builds - -This script is designed to work in GitHub Actions CI environment where: -- The repository is already checked out -- We need to determine which Maven modules are affected by the changes -- Output should be a simple comma-separated list for use with mvn -pl parameter -""" - -import sys -import os -import subprocess -from pathlib import Path -from typing import List, Optional, Set - -class CITestDiscovery: - """Test discovery for CI environments (GitHub Actions)""" - - def __init__(self, repo_root: Path = Path(".")): - self.repo_root = Path(repo_root) - self._last_git_command = None - - def modules_from_diff(self, base_ref: Optional[str] = None, verbose: bool = False) -> str: - """Get affected modules from git diff (for GitHub Actions)""" - try: - changed_files = self._get_changed_files(base_ref) - affected_modules = self._discover_affected_modules(changed_files) - - # Verbose logging to stderr - if verbose: - detected_base = base_ref if base_ref else self._detect_default_base() - print(f"Detected base ref: {detected_base}", file=sys.stderr) - print(f"Git diff strategy used: {self._get_last_git_command()}", file=sys.stderr) - print(f"Changed files ({len(changed_files)}):", file=sys.stderr) - for file in changed_files[:10]: # Limit to first 10 for readability - print(f" - {file}", file=sys.stderr) - if len(changed_files) > 10: - print(f" ... and {len(changed_files) - 10} more files", file=sys.stderr) - result_str = ",".join(affected_modules) if affected_modules else "" - print(f"Final module list: {result_str}", file=sys.stderr) - - if not affected_modules: - # Return empty string for no modules (GitHub Actions will skip module-specific build) - return "" - else: - # Return comma-separated list for mvn -pl parameter - return ",".join(affected_modules) - - except Exception as e: - # In CI, print error to stderr and return empty string to fail gracefully - print(f"Error in test discovery: {e}", file=sys.stderr) - print("Falling back to full build due to test discovery failure", file=sys.stderr) - return "" - - def _get_changed_files(self, base_ref: Optional[str] = None) -> List[str]: - """Get changed files from git diff in CI context""" - try: - # Determine the reference to diff against - pr_base = os.environ.get('GITHUB_BASE_REF') # PRs - pr_head = os.environ.get('GITHUB_HEAD_HEAD') # PRs - branch = os.environ.get('GITHUB_REF_NAME') # pushes - - # For maintenance branches (cherry-picks) or main branch pushes, use single commit diff - if (branch and branch.endswith('.x')) or (branch == 'main'): - # Maintenance branch or main branch - use diff with previous commit - cmd = ["git", "diff", "--name-only", "HEAD~1", "HEAD"] - elif base_ref: - # Explicit base reference provided - use two-dot diff for direct comparison - cmd = ["git", "diff", "--name-only", f"{base_ref}..HEAD"] - elif pr_base and pr_head: - # PR context - compare against base branch - cmd = ["git", "diff", "--name-only", f"origin/{pr_base}..HEAD"] - elif pr_base: - # PR context fallback - cmd = ["git", "diff", "--name-only", f"origin/{pr_base}..HEAD"] - else: - # Final fallback - single commit diff (most reliable) - cmd = ["git", "diff", "--name-only", "HEAD~1..HEAD"] - - # Execute the git diff command - self._last_git_command = ' '.join(cmd) # Store for debugging - result = subprocess.run( - cmd, - cwd=self.repo_root, - capture_output=True, - text=True, - check=True - ) - - files = result.stdout.strip().split('\n') - return [f for f in files if f.strip()] - - except subprocess.CalledProcessError as e: - print(f"Git command failed: {e}", file=sys.stderr) - print(f"Command: {' '.join(e.cmd) if hasattr(e, 'cmd') else 'unknown'}", file=sys.stderr) - print(f"Exit code: {e.returncode}", file=sys.stderr) - print(f"Stdout: {e.stdout if hasattr(e, 'stdout') else 'N/A'}", file=sys.stderr) - print(f"Stderr: {e.stderr if hasattr(e, 'stderr') else 'N/A'}", file=sys.stderr) - return [] - except Exception as e: - print(f"Error getting changed files: {e}", file=sys.stderr) - print(f"Error type: {type(e).__name__}", file=sys.stderr) - import traceback - print(f"Traceback: {traceback.format_exc()}", file=sys.stderr) - return [] - - def _discover_affected_modules(self, changed_files: List[str]) -> List[str]: - """Identify which Maven modules are affected by the changed files""" - modules = set() - - for file_path in changed_files: - module = self._find_module_for_file(file_path) - # DEBUG: Print what we're finding - print(f"DEBUG: file={file_path} -> module={module}", file=sys.stderr) - if module and module != ".": # Exclude root module to prevent full builds - modules.add(module) - print(f"DEBUG: Added module: {module}", file=sys.stderr) - elif module == ".": - print(f"DEBUG: Excluded root module for file: {file_path}", file=sys.stderr) - - print(f"DEBUG: Final modules before return: {sorted(list(modules))}", file=sys.stderr) - return sorted(list(modules)) - - def _find_module_for_file(self, file_path: str) -> Optional[str]: - """Find the Maven module that contains a given file""" - # Skip non-relevant files - if not self._is_relevant_file(file_path): - return None - - # Walk up the path looking for pom.xml - path_parts = file_path.split('/') - - for i in range(len(path_parts), 0, -1): - potential_module = '/'.join(path_parts[:i]) - # Handle root case - empty string becomes "." - if not potential_module: - potential_module = "." - pom_path = self.repo_root / potential_module / "pom.xml" - - if pom_path.exists(): - # Never return root module to prevent full builds - if potential_module == ".": - return None - # Found a module - return the relative path from repo root - return potential_module - - # Never return root module to prevent full builds - return None - - def _is_relevant_file(self, file_path: str) -> bool: - """Check if a file is relevant for module discovery""" - # Always exclude root pom.xml to prevent full builds - if file_path == 'pom.xml': - return False - - # Include Java source and test files - if file_path.endswith('.java'): - return True - - # Include build files (but not root pom.xml - handled above) - if file_path.endswith('pom.xml'): - return True - - # Include resource files - if '/src/main/resources/' in file_path or '/src/test/resources/' in file_path: - return True - - # Include Spring Boot configuration files - if file_path.endswith('.yml') or file_path.endswith('.yaml'): - if '/src/main/resources/' in file_path or '/src/test/resources/' in file_path: - return True - - # Include properties files in resources - if file_path.endswith('.properties'): - if '/src/main/resources/' in file_path or '/src/test/resources/' in file_path: - return True - - # Skip documentation, root configs, etc. - if file_path.endswith('.md') or file_path.endswith('.adoc'): - return False - - if file_path in ['README.md', 'LICENSE.txt', 'CONTRIBUTING.adoc']: - return False - - return False - - def _detect_default_base(self) -> str: - """Detect the default base reference for verbose logging""" - pr_base = os.environ.get('GITHUB_BASE_REF') - branch = os.environ.get('GITHUB_REF_NAME') - - # Show the actual strategy being used - if (branch and branch.endswith('.x')) or (branch == 'main'): - branch_type = "maintenance" if branch.endswith('.x') else "main" - return f"git diff HEAD~1 HEAD ({branch_type} branch {branch})" - elif pr_base: - return f"origin/{pr_base} (PR base)" - elif branch: - return f"HEAD~1 (single commit - branch {branch})" - else: - return "HEAD~1 (single commit - fallback)" - - def _get_last_git_command(self) -> str: - """Get the last git command executed for debugging""" - return self._last_git_command or "No git command executed yet" - - -def modules_from_diff_cli(): - """Get affected modules from git diff (for GitHub Actions)""" - base = None - verbose = False - - # Parse command line arguments - args = sys.argv[2:] # Skip script name and command - i = 0 - while i < len(args): - if args[i] == "--base" and i + 1 < len(args): - base = args[i + 1] - i += 2 - elif args[i] == "--verbose": - verbose = True - i += 1 - else: - print(f"Unknown argument: {args[i]}", file=sys.stderr) - i += 1 - - discovery = CITestDiscovery() - result = discovery.modules_from_diff(base_ref=base, verbose=verbose) - print(result) # Print to stdout for GitHub Actions to capture - - -def main(): - """CLI entry point""" - if len(sys.argv) < 2: - print("Usage: test_discovery.py [options]", file=sys.stderr) - print("Commands:", file=sys.stderr) - print(" modules-from-diff [--base ] [--verbose] - Output comma-separated list of affected Maven modules", file=sys.stderr) - print("", file=sys.stderr) - print("Options:", file=sys.stderr) - print(" --base - Explicit base reference for git diff (e.g., origin/1.0.x)", file=sys.stderr) - print(" --verbose - Show detailed logging to stderr", file=sys.stderr) - sys.exit(1) - - command = sys.argv[1] - - if command == "modules-from-diff": - modules_from_diff_cli() - else: - print(f"Unknown command: {command}", file=sys.stderr) - print("Available commands: modules-from-diff", file=sys.stderr) - sys.exit(1) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/.github/scripts/test_local.sh b/.github/scripts/test_local.sh deleted file mode 100755 index 302d54a1da9..00000000000 --- a/.github/scripts/test_local.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -# Local testing script to simulate GitHub Actions maintenance-fast.yml workflow -# Usage: ./test_local.sh [branch_name] - -set -e - -BRANCH_NAME=${1:-"1.0.x"} -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -echo "=== Local GitHub Actions Simulation ===" -echo "Branch: $BRANCH_NAME" -echo "Script dir: $SCRIPT_DIR" -echo "" - -# Simulate GitHub Actions environment variables -export GITHUB_REF_NAME="$BRANCH_NAME" -export GITHUB_REF="refs/heads/$BRANCH_NAME" - -echo "=== Step 1: Show commit range ===" -echo "Base ref: origin/$GITHUB_REF_NAME" -if git rev-parse "origin/$GITHUB_REF_NAME" >/dev/null 2>&1; then - git log --oneline "origin/$GITHUB_REF_NAME...HEAD" | head -5 -else - echo "WARNING: origin/$GITHUB_REF_NAME not found, using HEAD~1" - git log --oneline HEAD~1..HEAD -fi -echo "" - -echo "=== Step 2: Compute impacted modules ===" -cd "$SCRIPT_DIR/../.." -echo "Working directory: $(pwd)" - -# Test different git diff strategies locally -echo "--- Testing git diff strategies ---" - -echo "Strategy 1: origin/$GITHUB_REF_NAME...HEAD (three-dot)" -FILES_3DOT=$(git diff --name-only "origin/$GITHUB_REF_NAME...HEAD" 2>/dev/null || echo "") -echo "Files found: $(echo "$FILES_3DOT" | wc -l)" -echo "$FILES_3DOT" | head -3 - -echo "" -echo "Strategy 2: origin/$GITHUB_REF_NAME..HEAD (two-dot)" -FILES_2DOT=$(git diff --name-only "origin/$GITHUB_REF_NAME..HEAD" 2>/dev/null || echo "") -echo "Files found: $(echo "$FILES_2DOT" | wc -l)" -echo "$FILES_2DOT" | head -3 - -echo "" -echo "Strategy 3: HEAD~1..HEAD (single commit)" -FILES_1COMMIT=$(git diff --name-only "HEAD~1..HEAD" 2>/dev/null || echo "") -echo "Files found: $(echo "$FILES_1COMMIT" | wc -l)" -echo "$FILES_1COMMIT" | head -3 - -echo "" -echo "--- Running test_discovery.py ---" -MODS=$(python3 .github/scripts/test_discovery.py modules-from-diff --base "origin/$GITHUB_REF_NAME" --verbose 2>&1) -MODULE_LIST=$(echo "$MODS" | tail -1) - -echo "Script output:" -echo "$MODS" -echo "" -echo "Final modules: '$MODULE_LIST'" - -echo "" -echo "=== Step 3: Test build logic ===" -if [ -z "$MODULE_LIST" ]; then - echo "ERROR: No modules detected - git diff failed to find changes" - echo "This likely indicates a problem with the git diff strategy" - echo "Failing fast to avoid wasted resources and investigate the issue" - echo "Check the 'Compute impacted modules' step output for debugging info" - exit 1 -else - echo "SUCCESS: Would build modules: $MODULE_LIST" - echo "Build command would be:" - echo "./mvnw -B -T 1C -Pintegration-tests -DfailIfNoTests=false -pl \"$MODULE_LIST\" -amd verify" -fi - -echo "" -echo "=== Local test complete ===" \ No newline at end of file diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ca4792882f2..3676e86f883 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -1,33 +1,63 @@ -name: CI/CD build +name: Build + Deploy on development branches on: - # DISABLED: Replaced by fast-continuous-integration.yml workflow - # This workflow is kept for reference but will not run automatically - # To re-enable, uncomment the schedule and workflow_dispatch sections below - workflow_call: # Dummy trigger - workflow can only be called by other workflows (effectively disabled) - # schedule: - # # Combined schedule covering both EST and CET working hours - # # Morning/Early builds - # - cron: '30 6 * * 1-5' # 7:30 AM CET / 1:30 AM EST - # - cron: '30 11 * * 1-5' # 12:30 PM CET / 6:30 AM EST - # # Midday builds - # - cron: '30 16 * * 1-5' # 7:30 PM CET / 11:30 AM EST - # # Afternoon/Evening builds - # - cron: '30 21 * * 1-5' # 10:30 PM CET / 4:30 PM EST - # workflow_dispatch: - # Note: If push triggers are added in the future, they should include: - # push: - # paths-ignore: - # - '.github/**' - # - 'spring-ai-docs/**' - # - '*.md' - # - 'docs/**' + push: + branches: + - 'main' + - '[0-9].[0-9].x' + schedule: + - cron: '30 11 * * 1-5' # 12:30 PM CET / 6:30 AM EST + +concurrency: + group: ${{ github.workflow_ref }} + cancel-in-progress: true jobs: - build: - name: Build branch + build-all: + name: Build all modules + if: ${{ github.repository_owner == 'spring-projects' }} runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + - name: Setup Maven Build-Cache (~/.m2/build-cache) + if: ${{ github.event_name != 'schedule' }} + uses: actions/cache@v4 + with: + path: ~/.m2/build-cache + key: build-cache-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + restore-keys: | + build-cache-${{ runner.os }}- + + - name: Build all modules with unit tests + run: | + ./mvnw --batch-mode -ntp --update-snapshots clean install + + - name: Upload Spring-AI Built Artifacts + uses: actions/upload-artifact@v4 + with: + name: build-artifacts + path: ~/.m2/repository/org/springframework/ai + retention-days: 1 # Intent is to share only with downstream jobs in this workflow + + - name: Purge Spring AI Built Artifacts # We don't want the setup-java m2 cache to capture our products, only our deps + run: | + rm -fr ~/.m2/repository/org/springframework/ai + + + test-ollama: + name: Test Ollama if: ${{ github.repository_owner == 'spring-projects' }} + runs-on: ubuntu-latest + needs: build-all services: ollama: image: ollama/ollama:latest @@ -35,15 +65,56 @@ jobs: - 11434:11434 env: OLLAMA_WITH_REUSE: true + OLLAMA_AUTOCONF_TESTS_ENABLED: "true" steps: - name: Checkout source code uses: actions/checkout@v4 - - name: Free Disk Space - uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 + - name: Set up JDK + uses: actions/setup-java@v4 with: - large-packages: false - docker-images: false + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + - name: Setup Maven Build-Cache (~/.m2/build-cache) + uses: actions/cache@v4 + if: ${{ github.event_name != 'schedule' }} + with: + path: ~/.m2/build-cache + key: build-cache-${{ runner.os }}-ollama-${{ hashFiles('**/pom.xml') }} + restore-keys: | + build-cache-${{ runner.os }}- + + - name: Download Spring-AI Built Artifacts + uses: actions/download-artifact@v4 + with: + name: build-artifacts + path: ~/.m2/repository/org/springframework/ai + + - name: Configure Testcontainers + run: | + echo "testcontainers.reuse.enable=true" > $HOME/.testcontainers.properties + + - name: Test Ollama modules + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + ./mvnw --batch-mode -ntp --no-snapshot-updates \ + -pl models/spring-ai-ollama,auto-configurations/models/spring-ai-autoconfigure-model-ollama \ + -Pci-fast-integration-tests \ + -Dfailsafe.rerunFailingTestsCount=3 \ + verify + + test-openai: + name: Test OpenAI + if: ${{ github.repository_owner == 'spring-projects' }} + runs-on: ubuntu-latest + needs: build-all + steps: + - name: Checkout source code + uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v4 @@ -52,66 +123,150 @@ jobs: distribution: 'temurin' cache: 'maven' + - name: Setup Maven Build-Cache (~/.m2/build-cache) + uses: actions/cache@v4 + with: + path: ~/.m2/build-cache + key: build-cache-${{ runner.os }}-openai-${{ hashFiles('**/pom.xml') }} + restore-keys: | + build-cache-${{ runner.os }}- + + - name: Download Spring-AI Built Artifacts + uses: actions/download-artifact@v4 + with: + name: build-artifacts + path: ~/.m2/repository/org/springframework/ai + - name: Configure Testcontainers run: | echo "testcontainers.reuse.enable=true" > $HOME/.testcontainers.properties - # - name: Cache Docker images. - # uses: ScribeMD/docker-cache@0.3.7 - # with: - # key: docker-${{ runner.os }}-${{ hashFiles('**/OllamaImage.java') }} + - name: Test OpenAI modules + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + ./mvnw --batch-mode -ntp --no-snapshot-updates \ + -pl models/spring-ai-openai,auto-configurations/models/spring-ai-autoconfigure-model-openai \ + -Pci-fast-integration-tests \ + -Dfailsafe.rerunFailingTestsCount=3 \ + verify + + test-remaining: + name: Test Remaining (MCP, Google GenAI, Chroma, PgVector) + if: ${{ github.repository_owner == 'spring-projects' }} + runs-on: ubuntu-latest + needs: build-all + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + - name: Setup Maven Build-Cache (~/.m2/build-cache) + uses: actions/cache@v4 + with: + path: ~/.m2/build-cache + key: build-cache-${{ runner.os }}-other-${{ hashFiles('**/pom.xml') }} + restore-keys: | + build-cache-${{ runner.os }}- + + - name: Download Spring-AI Built Artifacts + uses: actions/download-artifact@v4 + with: + name: build-artifacts + path: ~/.m2/repository/org/springframework/ai - - name: Build and test with Maven + - name: Configure Testcontainers + run: | + echo "testcontainers.reuse.enable=true" > $HOME/.testcontainers.properties + + - name: Test remaining modules env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }} - ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }} - OLLAMA_AUTOCONF_TESTS_ENABLED: "true" - OLLAMA_WITH_REUSE: true - # Branch-specific Maven goals: deploy artifacts only from main, verify-only for maintenance branches - # This prevents maintenance branch snapshots from conflicting with main branch artifacts run: | - if [ "${{ github.ref }}" = "refs/heads/main" ]; then - ./mvnw -s settings.xml -ntp -Pci-fast-integration-tests -Pjavadoc -Dfailsafe.rerunFailingTestsCount=3 \ - --batch-mode --update-snapshots deploy - else - ./mvnw -s settings.xml -ntp -Pci-fast-integration-tests -Pjavadoc -Dfailsafe.rerunFailingTestsCount=3 \ - --batch-mode --update-snapshots verify - fi + ./mvnw --batch-mode -ntp --no-snapshot-updates \ + -pl models/spring-ai-google-genai,auto-configurations/models/spring-ai-autoconfigure-model-google-genai,mcp/common,mcp/mcp-annotations-spring,auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common,auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient,auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux,auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-common,auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webmvc,auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux,vector-stores/spring-ai-chroma-store,vector-stores/spring-ai-pgvector-store,spring-ai-integration-tests \ + -Pci-fast-integration-tests \ + -Dfailsafe.rerunFailingTestsCount=3 \ + verify - - name: Generate Java docs - if: github.ref == 'refs/heads/main' - run: ./mvnw --batch-mode -ntp javadoc:aggregate + handle-documentation: + name: Generate and upload javadocs, trigger antora reference doc + if: ${{ github.repository_owner == 'spring-projects' && github.event_name != 'schedule'}} + runs-on: ubuntu-latest + needs: [build-all, test-ollama, test-openai, test-remaining] + steps: + - name: Trigger Antora build + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh workflow run deploy-docs.yml -r docs-build - - name: Generate assembly - if: github.ref == 'refs/heads/main' - working-directory: spring-ai-docs - run: ../mvnw --batch-mode -ntp assembly:single + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + # NOT setting up maven build-cache b/c javadoc:aggregate-jar is forking lifecyle and can't benefit from it anyway + + - name: Generate Java docs + run: ./mvnw --batch-mode -ntp javadoc:aggregate-jar - name: Capture project version - if: github.ref == 'refs/heads/main' run: echo PROJECT_VERSION=$(./mvnw help:evaluate -Dexpression=project.version --quiet -DforceStdout) >> $GITHUB_ENV - name: Setup SSH key - if: github.ref == 'refs/heads/main' - env: - DOCS_SSH_KEY: ${{ secrets.DOCS_SSH_KEY }} - DOCS_SSH_HOST_KEY: ${{ secrets.DOCS_SSH_HOST_KEY }} run: | mkdir "$HOME/.ssh" - echo "$DOCS_SSH_KEY" > "$HOME/.ssh/key" + echo "${{ secrets.DOCS_SSH_KEY }}" > "$HOME/.ssh/key" chmod 600 "$HOME/.ssh/key" - echo "$DOCS_SSH_HOST_KEY" > "$HOME/.ssh/known_hosts" + echo "${{ secrets.DOCS_SSH_HOST_KEY }}" > "$HOME/.ssh/known_hosts" - name: Deploy docs - if: github.ref == 'refs/heads/main' + run: | + ssh -i $HOME/.ssh/key ${{ secrets.DOCS_USERNAME }}@${{ secrets.DOCS_HOST }} "cd ${{ secrets.DOCS_PATH }} && rm -fr $PROJECT_VERSION && mkdir -p $PROJECT_VERSION" + scp -i $HOME/.ssh/key target/spring-ai-parent-${PROJECT_VERSION}-javadoc.jar ${{ secrets.DOCS_USERNAME }}@${{ secrets.DOCS_HOST }}:${{ secrets.DOCS_PATH }}/$PROJECT_VERSION + ssh -i $HOME/.ssh/key ${{ secrets.DOCS_USERNAME }}@${{ secrets.DOCS_HOST }} "cd ${{ secrets.DOCS_PATH }}/${PROJECT_VERSION} && unzip spring-ai-parent-${PROJECT_VERSION}-javadoc.jar -d api && rm spring-ai-parent-${PROJECT_VERSION}-javadoc.jar" + + + deploy-artifactory: + name: Deploy to Artifactory + runs-on: ubuntu-latest + needs: [build-all, test-ollama, test-openai, test-remaining] + if: ${{ github.repository_owner == 'spring-projects' && github.event_name != 'schedule' && false}} + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + - name: Setup Maven Build-Cache (~/.m2/build-cache) + uses: actions/cache@v4 + with: + path: ~/.m2/build-cache + key: build-cache-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + restore-keys: | + build-cache-${{ runner.os }}- + + - name: Deploy to Artifactory env: - DOCS_HOST: ${{ secrets.DOCS_HOST }} - DOCS_PATH: ${{ secrets.DOCS_PATH }} - DOCS_USERNAME: ${{ secrets.DOCS_USERNAME }} - working-directory: spring-ai-docs/target + ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }} + ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }} run: | - unzip spring-ai-$PROJECT_VERSION-docs.zip - ssh -i $HOME/.ssh/key $DOCS_USERNAME@$DOCS_HOST "cd $DOCS_PATH && mkdir -p $PROJECT_VERSION" - scp -i $HOME/.ssh/key -r api $DOCS_USERNAME@$DOCS_HOST:$DOCS_PATH/$PROJECT_VERSION + ./mvnw -s settings.xml --batch-mode -ntp -Dmaven.test.skip deploy diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 8992f12fabc..958971a5e3f 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -2,16 +2,6 @@ name: Deploy Docs run-name: ${{ github.event_name == 'workflow_dispatch' && 'Deploy Docs (Build)' || 'Deploy Docs (Dispatcher)' }} on: workflow_dispatch: - push: - branches: [main, '[0-9].[0-9].x' ] - tags: ['v[0-9].[0-9].[0-9]', 'v[0-9].[0-9].[0-9]-*'] - paths: - - 'spring-ai-docs/**/*.adoc' - - 'spring-ai-docs/**/antora.yml' - - 'spring-ai-docs/**/antora-playbook.yml' - - 'spring-ai-docs/pom.xml' - - 'spring-ai-docs/src/main/javadoc/**' - - '.github/workflows/deploy-docs.yml' permissions: actions: write jobs: @@ -19,20 +9,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'spring-projects' }} steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: docs-build - fetch-depth: 1 - - name: Dispatch (partial build) - if: github.ref_type == 'branch' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GIT_TRACE: 1 - GIT_CURL_VERBOSE: 1 - run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }} - name: Dispatch (full build) - if: github.ref_type == 'tag' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) + run: gh workflow run deploy-docs.yml -r docs-build diff --git a/.github/workflows/fast-continuous-integration.yml b/.github/workflows/fast-continuous-integration.yml deleted file mode 100644 index cc9dce0f982..00000000000 --- a/.github/workflows/fast-continuous-integration.yml +++ /dev/null @@ -1,289 +0,0 @@ -name: Fast CI/CD build - -on: - schedule: - # Combined schedule covering both EST and CET working hours - # Morning/Early builds - - cron: '30 6 * * 1-5' # 7:30 AM CET / 1:30 AM EST - - cron: '30 11 * * 1-5' # 12:30 PM CET / 6:30 AM EST - # Midday builds - - cron: '30 16 * * 1-5' # 7:30 PM CET / 11:30 AM EST - # Afternoon/Evening builds - - cron: '30 21 * * 1-5' # 10:30 PM CET / 4:30 PM EST - workflow_dispatch: - -jobs: - build-all: - name: Build all modules - runs-on: ubuntu-latest - if: ${{ github.repository_owner == 'spring-projects' }} - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Free Disk Space - uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 - with: - large-packages: false - docker-images: false - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - cache: 'maven' - - - name: Build all modules with unit tests - run: | - ./mvnw --batch-mode -ntp --update-snapshots clean install - - - name: Upload Maven repository - uses: actions/upload-artifact@v4 - with: - name: maven-repo - path: ~/.m2/repository - retention-days: 1 - - test-ollama: - name: Test Ollama - runs-on: ubuntu-latest - needs: build-all - if: ${{ github.repository_owner == 'spring-projects' }} - services: - ollama: - image: ollama/ollama:latest - ports: - - 11434:11434 - env: - OLLAMA_WITH_REUSE: true - OLLAMA_AUTOCONF_TESTS_ENABLED: "true" - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - cache: 'maven' - - - name: Download Maven repository - uses: actions/download-artifact@v4 - with: - name: maven-repo - path: ~/.m2/repository - - - name: Configure Testcontainers - run: | - echo "testcontainers.reuse.enable=true" > $HOME/.testcontainers.properties - - - name: Test Ollama modules - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - run: | - ./mvnw --batch-mode -ntp --no-snapshot-updates \ - -pl models/spring-ai-ollama,auto-configurations/models/spring-ai-autoconfigure-model-ollama \ - -Pci-fast-integration-tests \ - -Dfailsafe.rerunFailingTestsCount=3 \ - verify - - test-openai: - name: Test OpenAI - runs-on: ubuntu-latest - needs: build-all - if: ${{ github.repository_owner == 'spring-projects' }} - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - cache: 'maven' - - - name: Download Maven repository - uses: actions/download-artifact@v4 - with: - name: maven-repo - path: ~/.m2/repository - - - name: Configure Testcontainers - run: | - echo "testcontainers.reuse.enable=true" > $HOME/.testcontainers.properties - - - name: Test OpenAI modules - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - run: | - ./mvnw --batch-mode -ntp --no-snapshot-updates \ - -pl models/spring-ai-openai,auto-configurations/models/spring-ai-autoconfigure-model-openai \ - -Pci-fast-integration-tests \ - -Dfailsafe.rerunFailingTestsCount=3 \ - verify - - test-remaining: - name: Test Remaining (MCP, Google GenAI, Chroma, PgVector) - runs-on: ubuntu-latest - needs: build-all - if: ${{ github.repository_owner == 'spring-projects' }} - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - cache: 'maven' - - - name: Download Maven repository - uses: actions/download-artifact@v4 - with: - name: maven-repo - path: ~/.m2/repository - - - name: Configure Testcontainers - run: | - echo "testcontainers.reuse.enable=true" > $HOME/.testcontainers.properties - - - name: Test remaining modules - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - run: | - ./mvnw --batch-mode -ntp --no-snapshot-updates \ - -pl models/spring-ai-google-genai,auto-configurations/models/spring-ai-autoconfigure-model-google-genai,mcp/common,mcp/mcp-annotations-spring,auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common,auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient,auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux,auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-common,auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webmvc,auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux,vector-stores/spring-ai-chroma-store,vector-stores/spring-ai-pgvector-store,spring-ai-integration-tests \ - -Pci-fast-integration-tests \ - -Dfailsafe.rerunFailingTestsCount=3 \ - verify - - generate-artifacts: - name: Generate documentation artifacts - runs-on: ubuntu-latest - needs: [build-all, test-ollama, test-openai, test-remaining] - if: ${{ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/1.1.x') && github.repository_owner == 'spring-projects' }} - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - cache: 'maven' - - - name: Download Maven repository - uses: actions/download-artifact@v4 - with: - name: maven-repo - path: ~/.m2/repository - - - name: Generate Java docs - run: ./mvnw --batch-mode -ntp javadoc:aggregate - - - name: Generate assembly - working-directory: spring-ai-docs - run: ../mvnw --batch-mode -ntp assembly:single - - - name: Upload Javadoc - uses: actions/upload-artifact@v4 - with: - name: javadoc - path: target/site/apidocs - retention-days: 1 - - - name: Upload assembly - uses: actions/upload-artifact@v4 - with: - name: assembly - path: spring-ai-docs/target/*.zip - retention-days: 1 - - deploy-artifactory: - name: Deploy to Artifactory - runs-on: ubuntu-latest - needs: [build-all, test-ollama, test-openai, test-remaining] - if: ${{ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/1.1.x') && github.repository_owner == 'spring-projects' }} - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - cache: 'maven' - - - name: Download Maven repository - uses: actions/download-artifact@v4 - with: - name: maven-repo - path: ~/.m2/repository - - - name: Deploy to Artifactory - env: - ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }} - ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }} - run: | - ./mvnw -s settings.xml --batch-mode -ntp -DskipTests deploy - - deploy-docs: - name: Deploy documentation - runs-on: ubuntu-latest - needs: [generate-artifacts] - if: ${{ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/1.1.x') && github.repository_owner == 'spring-projects' }} - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - cache: 'maven' - - - name: Download Javadoc - uses: actions/download-artifact@v4 - with: - name: javadoc - path: target/site/apidocs - - - name: Download assembly - uses: actions/download-artifact@v4 - with: - name: assembly - path: spring-ai-docs/target - - - name: Capture project version - run: echo PROJECT_VERSION=$(./mvnw help:evaluate -Dexpression=project.version --quiet -DforceStdout) >> $GITHUB_ENV - - - name: Setup SSH key - env: - DOCS_SSH_KEY: ${{ secrets.DOCS_SSH_KEY }} - DOCS_SSH_HOST_KEY: ${{ secrets.DOCS_SSH_HOST_KEY }} - run: | - mkdir "$HOME/.ssh" - echo "$DOCS_SSH_KEY" > "$HOME/.ssh/key" - chmod 600 "$HOME/.ssh/key" - echo "$DOCS_SSH_HOST_KEY" > "$HOME/.ssh/known_hosts" - - - name: Deploy docs - env: - DOCS_HOST: ${{ secrets.DOCS_HOST }} - DOCS_PATH: ${{ secrets.DOCS_PATH }} - DOCS_USERNAME: ${{ secrets.DOCS_USERNAME }} - working-directory: spring-ai-docs/target - run: | - unzip spring-ai-$PROJECT_VERSION-docs.zip - ssh -i $HOME/.ssh/key $DOCS_USERNAME@$DOCS_HOST "cd $DOCS_PATH && mkdir -p $PROJECT_VERSION" - scp -i $HOME/.ssh/key -r api $DOCS_USERNAME@$DOCS_HOST:$DOCS_PATH/$PROJECT_VERSION diff --git a/.github/workflows/main-push-fast.yml b/.github/workflows/main-push-fast.yml deleted file mode 100644 index 15e3bfc9603..00000000000 --- a/.github/workflows/main-push-fast.yml +++ /dev/null @@ -1,158 +0,0 @@ -name: Main Push - Fast -run-name: ${{ github.event.inputs.commit_sha && format('Manual Test - {0}', github.event.inputs.commit_sha) || format('Fast Build - {0}', github.sha) }} - -on: - push: - branches: ['main'] - paths-ignore: - - 'spring-ai-docs/**' - - '*.md' - - 'docs/**' - - '.github/**' - workflow_dispatch: - inputs: - commit_sha: - description: 'Specific commit SHA to test (optional - defaults to latest)' - required: false - type: string - -jobs: - fast-impacted: - name: Fast Build - Affected Modules - runs-on: ubuntu-latest - if: ${{ github.repository_owner == 'spring-projects' }} - permissions: - contents: read - concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-${{ github.sha }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Always use full history for manual runs to find any commit - - - name: Checkout specific commit - if: github.event.inputs.commit_sha - run: | - echo "Checking out specific commit: ${{ github.event.inputs.commit_sha }}" - # Save the latest main reference - LATEST_MAIN=$(git rev-parse origin/main) - - # Checkout the target commit - git checkout ${{ github.event.inputs.commit_sha }} - - # Preserve all latest GitHub Actions scripts from main - echo "Using latest GitHub Actions scripts from main..." - # Copy all scripts from main's .github/scripts directory (excluding __pycache__) - for script in $(git ls-tree -r --name-only ${LATEST_MAIN} .github/scripts/ | grep -v __pycache__); do - echo " Updating ${script}" - mkdir -p $(dirname ${script}) - git show ${LATEST_MAIN}:${script} > ${script} - done - # Note: The workflow itself is already from main (since that's what's running) - - - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - # cache: 'maven' # Disabled for fast workflow - reduces post-job noise - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Configure Testcontainers - run: | - echo "testcontainers.reuse.enable=true" > $HOME/.testcontainers.properties - - - name: Show commit range - run: | - if [ -n "${{ github.event.inputs.commit_sha }}" ]; then - echo "๐Ÿงช MANUAL TEST RUN" - echo "Testing specific commit: ${{ github.event.inputs.commit_sha }}" - echo "" - echo "๐Ÿ“‹ Commit Details:" - git log --format=" Author: %an <%ae>%n Date: %ad%n Message: %s" -1 HEAD - echo "" - echo "๐Ÿ“ Changed Files:" - git diff --name-only HEAD~1..HEAD | head -10 - if [ $(git diff --name-only HEAD~1..HEAD | wc -l) -gt 10 ]; then - echo " ... and $(( $(git diff --name-only HEAD~1..HEAD | wc -l) - 10 )) more files" - fi - else - echo "๐Ÿš€ AUTOMATIC BUILD" - echo "Testing latest commit on main branch" - echo "" - echo "๐Ÿ“‹ Commit Details:" - git log --format=" Author: %an <%ae>%n Date: %ad%n Message: %s" -1 HEAD - fi - - - name: Compute impacted modules - id: mods - run: | - echo "=== Detecting affected modules ===" - echo "=== DEBUG: Changed files ===" - git diff --name-only HEAD~1..HEAD - echo "=== DEBUG: Running test discovery with full output ===" - MODULE_LIST=$(python3 .github/scripts/test_discovery.py modules-from-diff --verbose) - echo "=== DEBUG: Raw module list: '$MODULE_LIST' ===" - echo "modules=$MODULE_LIST" >> "$GITHUB_OUTPUT" - - if [ -n "$MODULE_LIST" ]; then - echo "Affected modules detected: $MODULE_LIST" - # Only start Ollama if we're testing Ollama-specific modules - if echo "$MODULE_LIST" | grep -q "ollama"; then - echo "Ollama-related modules detected - Ollama service needed" - echo "needs_ollama=true" >> "$GITHUB_OUTPUT" - else - echo "Non-Ollama modules detected - Ollama service not needed" - echo "needs_ollama=false" >> "$GITHUB_OUTPUT" - fi - else - echo "No affected modules detected - only workflow/docs changes" - echo "needs_ollama=false" >> "$GITHUB_OUTPUT" - fi - - - name: Start Ollama service for integration tests - if: steps.mods.outputs.needs_ollama == 'true' - run: | - echo "Starting Ollama for integration tests..." - docker run -d --name ollama-test -p 11434:11434 ollama/ollama:latest - echo "Ollama container started" - - - name: Test affected modules with integration tests - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }} - ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }} - OLLAMA_AUTOCONF_TESTS_ENABLED: "true" - OLLAMA_WITH_REUSE: true - run: | - MODS="${{ steps.mods.outputs.modules }}" - if [ -z "$MODS" ]; then - echo "INFO: No affected modules detected - skipping build" - echo "Only workflow, documentation, or non-build files were changed" - echo "Fast workflow optimization: no compilation needed" - exit 0 - else - echo "INFO: Running tests for affected modules: $MODS" - # Build dependencies without tests, then test only the affected modules - echo "INFO: Phase 1 - Building dependencies (this may take a few minutes)..." - ./mvnw -B -ntp -q -T 1C -DskipTests -pl "$MODS" -am install - echo "INFO: Phase 2 - Running tests for affected modules..." - ./mvnw -B -ntp -q -T 1C -Pci-fast-integration-tests -DfailIfNoTests=false -pl "$MODS" verify - echo "INFO: Testing complete" - fi - - - name: Deploy to Artifactory (affected modules only) - if: steps.mods.outputs.modules != '' - env: - ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }} - ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }} - run: | - MODS="${{ steps.mods.outputs.modules }}" - echo "INFO: Deploying affected modules to Artifactory: $MODS" - # Skip tests during deploy since we already ran them - ./mvnw -B -q -s settings.xml -DskipTests -pl "$MODS" deploy \ No newline at end of file diff --git a/.github/workflows/maintenance-fast.yml b/.github/workflows/maintenance-fast.yml deleted file mode 100644 index 4e3d0541d9b..00000000000 --- a/.github/workflows/maintenance-fast.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: Maintenance Push โ€“ Fast -run-name: ${{ github.event.inputs.commit_sha && format('Manual Test - {0}', github.event.inputs.commit_sha) || format('Fast Build - {0}', github.sha) }} - -on: - push: - branches: ['*.*.x'] - -jobs: - fast-impacted: - if: contains(github.event.head_commit.message, '(cherry picked from commit') - runs-on: ubuntu-latest - permissions: - contents: read - concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-${{ github.sha }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - with: { fetch-depth: 2 } # Need HEAD and HEAD~1 for single commit diff - - - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - cache: 'maven' - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Show commit range - run: | - echo "Base ref: origin/$GITHUB_REF_NAME" - git log --oneline "origin/$GITHUB_REF_NAME...HEAD" - - - name: Compute impacted modules - id: mods - run: | - echo "=== Module Detection Debug Info ===" - echo "Environment variables:" - echo " GITHUB_REF_NAME: $GITHUB_REF_NAME" - echo " GITHUB_REF: $GITHUB_REF" - echo " PWD: $(pwd)" - echo "" - - echo "Git state verification:" - echo " HEAD: $(git rev-parse HEAD 2>/dev/null || echo 'FAILED')" - echo " HEAD~1: $(git rev-parse HEAD~1 2>/dev/null || echo 'NOT AVAILABLE')" - echo " Branch: $(git branch --show-current 2>/dev/null || echo 'DETACHED')" - echo "" - - echo "Testing different git diff approaches:" - echo "1. HEAD~1..HEAD:" - git diff --name-only HEAD~1..HEAD 2>&1 | head -10 || echo " FAILED: $?" - - echo "2. git show HEAD:" - git show --name-only --format= HEAD 2>&1 | head -10 || echo " FAILED: $?" - - echo "3. Recent commits:" - git log --oneline -3 2>/dev/null || echo " Git log failed" - echo "" - - echo "=== Running test_discovery.py with full debugging ===" - set -x # Enable bash debug mode - MODS=$(python3 .github/scripts/test_discovery.py modules-from-diff --base "origin/$GITHUB_REF_NAME" --verbose 2>&1) - EXIT_CODE=$? - set +x # Disable bash debug mode - - echo "" - echo "=== Test Discovery Results ===" - echo "Exit code: $EXIT_CODE" - echo "Output:" - echo "$MODS" - echo "" - - # Extract just the module list (last line that isn't stderr logging) - MODULE_LIST=$(echo "$MODS" | grep -v "^Detected base ref:" | grep -v "^Changed files" | grep -v "^Final module list:" | tail -1) - echo "Extracted modules: '$MODULE_LIST'" - echo "modules=$MODULE_LIST" >> "$GITHUB_OUTPUT" - - - name: Test affected modules with integration tests - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - SPRING_AI_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - run: | - MODS="${{ steps.mods.outputs.modules }}" - if [ -z "$MODS" ]; then - echo "ERROR: No modules detected - git diff failed to find changes" - echo "This likely indicates a problem with the git diff strategy" - echo "Failing fast to avoid wasted resources and investigate the issue" - echo "Check the 'Compute impacted modules' step output for debugging info" - exit 1 - fi - ./mvnw -B -ntp -q -T 1C -Pintegration-tests -DfailIfNoTests=false -pl "$MODS" -amd verify \ No newline at end of file diff --git a/.github/workflows/new-maven-central-release.yml b/.github/workflows/maven-central-release.yml similarity index 100% rename from .github/workflows/new-maven-central-release.yml rename to .github/workflows/maven-central-release.yml diff --git a/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/package-info.java b/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/package-info.java index 0c21c09c610..f96dbe525bf 100644 --- a/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/package-info.java +++ b/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/package-info.java @@ -15,7 +15,7 @@ */ /** - * Chat client. + * Chat client API. */ @NonNullApi @NonNullFields diff --git a/spring-ai-docs/src/assembly/javadocs.xml b/spring-ai-docs/src/assembly/javadocs.xml deleted file mode 100644 index 709caaddc4f..00000000000 --- a/spring-ai-docs/src/assembly/javadocs.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - docs - - zip - - false - - - ../target/site/apidocs - api - - -