Added maven compatibility test #22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Maven Compatibility Test | |
| # This workflow proactively tests build-info-extractor-maven3 compatibility with multiple Maven versions | |
| # to catch breaking changes early (e.g., new required fields in DefaultMavenPluginManager). | |
| # | |
| # When a compatibility issue is detected: | |
| # 1. Check error logs for NullPointerException, NoSuchMethodError, etc. | |
| # 2. Compare Maven's DefaultMavenPluginManager with our components.xml | |
| # 3. Add missing field declarations to components.xml | |
| # 4. See CHECK-MAVEN-FIELDS.sh for a helper script to identify missing fields | |
| on: | |
| # Run on PRs for testing (remove after initial validation) | |
| pull_request: | |
| branches: [ master, main ] | |
| # Run nightly to catch new Maven releases | |
| schedule: | |
| - cron: '0 2 * * *' # Every night at 2 AM UTC | |
| # Allow manual trigger for testing | |
| workflow_dispatch: | |
| jobs: | |
| check-latest-maven-versions: | |
| name: Determine Maven versions to test | |
| runs-on: ubuntu-latest | |
| outputs: | |
| maven-versions: ${{ steps.set-versions.outputs.maven-versions }} | |
| steps: | |
| - name: Get latest Maven versions from Maven Central | |
| id: maven-versions | |
| run: | | |
| echo "Fetching latest Maven versions from Maven Central..." | |
| # Fetch Maven metadata | |
| curl -s https://repo.maven.apache.org/maven2/org/apache/maven/maven-core/maven-metadata.xml > maven-metadata.xml | |
| # Extract all versions | |
| VERSIONS=$(grep -oP '(?<=<version>)[^<]+' maven-metadata.xml | grep -E '^[0-9]+\.[0-9]+\.[0-9]+' | sort -V) | |
| echo "Available Maven versions:" | |
| echo "$VERSIONS" | |
| # Get latest versions for each major.minor series | |
| LATEST_3_8=$(echo "$VERSIONS" | grep '^3\.8\.' | tail -1) | |
| LATEST_3_9=$(echo "$VERSIONS" | grep '^3\.9\.' | tail -1) | |
| LATEST_4_0=$(echo "$VERSIONS" | grep '^4\.0\.' | tail -1) | |
| echo "" | |
| echo "Latest versions by series:" | |
| echo " 3.8.x: $LATEST_3_8" | |
| echo " 3.9.x: $LATEST_3_9" | |
| echo " 4.0.x: ${LATEST_4_0:-not released yet}" | |
| # Export for next step | |
| echo "LATEST_3_8=$LATEST_3_8" >> $GITHUB_ENV | |
| echo "LATEST_3_9=$LATEST_3_9" >> $GITHUB_ENV | |
| echo "LATEST_4_0=$LATEST_4_0" >> $GITHUB_ENV | |
| - name: Build version matrix | |
| id: set-versions | |
| run: | | |
| # Build JSON array of versions to test | |
| # Include: first 3.9, some mid versions, and all latest versions | |
| VERSIONS='["3.9.0"' # First 3.9.x | |
| # Add latest 3.8.x | |
| if [ -n "$LATEST_3_8" ]; then | |
| VERSIONS+=', "'$LATEST_3_8'"' | |
| fi | |
| # Add latest 3.9.x | |
| if [ -n "$LATEST_3_9" ]; then | |
| VERSIONS+=', "'$LATEST_3_9'"' | |
| fi | |
| # Add latest 4.0.x if it exists | |
| if [ -n "$LATEST_4_0" ]; then | |
| VERSIONS+=', "'$LATEST_4_0'"' | |
| else | |
| # Fallback to beta if no stable 4.0 yet | |
| VERSIONS+=', "4.0.0-beta-4"' | |
| fi | |
| VERSIONS+=']' | |
| echo "Maven versions to test: $VERSIONS" | |
| echo "maven-versions=$VERSIONS" >> $GITHUB_OUTPUT | |
| test-maven-compatibility: | |
| name: Test with Maven ${{ matrix.maven-version }} | |
| runs-on: ubuntu-latest | |
| needs: check-latest-maven-versions | |
| strategy: | |
| fail-fast: false # Test all versions even if one fails | |
| matrix: | |
| # Dynamically fetched versions from previous job | |
| maven-version: ${{ fromJSON(needs.check-latest-maven-versions.outputs.maven-versions) }} | |
| java-version: | |
| - '8' | |
| - '11' | |
| - '17' | |
| - '21' | |
| exclude: | |
| # Maven 4 requires Java 17+ | |
| - maven-version: '4.0.0-beta-4' | |
| java-version: '8' | |
| - maven-version: '4.0.0-beta-4' | |
| java-version: '11' | |
| steps: | |
| - name: Set up test JDK ${{ matrix.java-version }} | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: ${{ matrix.java-version }} | |
| - name: Set up Maven ${{ matrix.maven-version }} | |
| uses: stCarolas/setup-maven@v5 | |
| with: | |
| maven-version: ${{ matrix.maven-version }} | |
| - name: Verify Maven version | |
| run: | | |
| mvn --version | |
| echo "M2_HOME=$M2_HOME" | |
| - name: Set up JFrog CLI | |
| uses: jfrog/setup-jfrog-cli@v4 | |
| env: | |
| JF_URL: ${{ secrets.PLATFORM_URL }} | |
| JF_USER: ${{ secrets.PLATFORM_USER }} | |
| JF_ACCESS_TOKEN: ${{ secrets.PLATFORM_ADMIN_TOKEN }} | |
| with: | |
| version: latest | |
| # JFrog CLI will automatically download the latest released build-info extractor | |
| # This tests the version users actually use in production | |
| - name: Create test Maven project | |
| run: | | |
| # Create project structure | |
| mkdir -p ${{ github.workspace }}/test-project/src/main/java/com/test | |
| cd ${{ github.workspace }}/test-project | |
| cat > pom.xml << 'EOF' | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 | |
| http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| <modelVersion>4.0.0</modelVersion> | |
| <groupId>com.jfrog.test</groupId> | |
| <artifactId>maven-compatibility-test</artifactId> | |
| <version>1.0.0-SNAPSHOT</version> | |
| <packaging>jar</packaging> | |
| <properties> | |
| <maven.compiler.source>8</maven.compiler.source> | |
| <maven.compiler.target>8</maven.compiler.target> | |
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
| </properties> | |
| <!-- Use Maven Central as fallback to ensure plugins can be downloaded --> | |
| <repositories> | |
| <repository> | |
| <id>central</id> | |
| <url>https://repo.maven.apache.org/maven2</url> | |
| <releases><enabled>true</enabled></releases> | |
| </repository> | |
| </repositories> | |
| <pluginRepositories> | |
| <pluginRepository> | |
| <id>central</id> | |
| <url>https://repo.maven.apache.org/maven2</url> | |
| <releases><enabled>true</enabled></releases> | |
| </pluginRepository> | |
| </pluginRepositories> | |
| <dependencies> | |
| <dependency> | |
| <groupId>org.apache.commons</groupId> | |
| <artifactId>commons-lang3</artifactId> | |
| <version>3.14.0</version> | |
| </dependency> | |
| </dependencies> | |
| </project> | |
| EOF | |
| cat > src/main/java/com/test/App.java << 'EOF' | |
| package com.test; | |
| import org.apache.commons.lang3.StringUtils; | |
| public class App { | |
| public static void main(String[] args) { | |
| System.out.println("Maven version test: " + | |
| StringUtils.capitalize("success")); | |
| } | |
| } | |
| EOF | |
| # Verify project was created | |
| echo "" | |
| echo "Test project structure:" | |
| ls -la ${{ github.workspace }}/test-project/ | |
| echo "" | |
| echo "✅ Test project created successfully" | |
| - name: Configure JFrog CLI server | |
| env: | |
| JF_URL: ${{ secrets.PLATFORM_URL }} | |
| JF_USER: ${{ secrets.PLATFORM_USER }} | |
| JF_ACCESS_TOKEN: ${{ secrets.PLATFORM_ADMIN_TOKEN }} | |
| run: | | |
| # Create JFrog CLI server configuration | |
| echo "Creating JFrog CLI server configuration..." | |
| if [ -z "$JF_URL" ] || [ -z "$JF_ACCESS_TOKEN" ]; then | |
| echo "⚠️ Missing Artifactory credentials in secrets" | |
| echo "Creating dummy config for testing (will fail, but we can still test extractor loading)" | |
| jf config add artifactory --url=http://localhost:8081/artifactory --access-token=dummy --interactive=false || true | |
| else | |
| echo "Using real Artifactory instance from secrets" | |
| jf config add artifactory \ | |
| --url="$JF_URL" \ | |
| --access-token="$JF_ACCESS_TOKEN" \ | |
| --interactive=false | |
| fi | |
| # Verify configuration | |
| echo "" | |
| echo "JFrog CLI servers:" | |
| jf config show | |
| - name: Configure Maven with JFrog | |
| working-directory: ${{ github.workspace }}/test-project | |
| run: | | |
| # Configure Maven to use JFrog Platform | |
| jf mvn-config \ | |
| --server-id-resolve=artifactory \ | |
| --server-id-deploy=artifactory \ | |
| --repo-resolve-releases=maven-remote \ | |
| --repo-resolve-snapshots=maven-remote \ | |
| --repo-deploy-releases=maven-local \ | |
| --repo-deploy-snapshots=maven-local 2>&1 | tee config-output.log | |
| if grep -q "maven build config successfully created" config-output.log; then | |
| echo "✅ Maven configured with JFrog Platform" | |
| else | |
| echo "⚠️ Maven config had issues:" | |
| cat config-output.log | |
| fi | |
| - name: Test Maven with build-info extractor | |
| id: maven_test | |
| continue-on-error: true | |
| working-directory: ${{ github.workspace }}/test-project | |
| run: | | |
| echo "Testing Maven ${{ matrix.maven-version }} with build-info extractor" | |
| echo "" | |
| # Run jf mvn - this will: | |
| # 1. Download the build-info extractor (if not cached) | |
| # 2. Load it as a Maven extension | |
| # 3. If Maven incompatibility exists, NullPointerException happens immediately | |
| # 4. Otherwise, will build successfully with real Artifactory | |
| jf mvn clean compile 2>&1 | tee maven-output.log || true | |
| echo "" | |
| echo "Build complete - checking for compatibility issues..." | |
| - name: Check for compatibility errors | |
| if: always() | |
| working-directory: ${{ github.workspace }}/test-project | |
| run: | | |
| # Check if the test actually ran | |
| if [ ! -f maven-output.log ]; then | |
| echo "❌ ERROR: No output log found!" | |
| echo "This usually means configuration or test setup failed." | |
| echo "Check previous steps for errors." | |
| exit 1 | |
| fi | |
| echo "Analyzing Maven output..." | |
| echo "" | |
| # Check for success | |
| if grep -q "BUILD SUCCESS" maven-output.log; then | |
| echo "✅ Maven build completed successfully" | |
| echo "Maven ${{ matrix.maven-version }} with Java ${{ matrix.java-version }} - COMPATIBLE" | |
| exit 0 | |
| fi | |
| # Build failed - check for Maven COMPATIBILITY issues (not build issues) | |
| echo "⚠️ Maven build failed - analyzing for compatibility errors..." | |
| echo "" | |
| # Check for Maven version compatibility issues | |
| if grep -q "NullPointerException.*is null" maven-output.log; then | |
| echo "❌ COMPATIBILITY ERROR: NullPointerException detected!" | |
| echo "Maven ${{ matrix.maven-version }} is INCOMPATIBLE with build-info extractor" | |
| echo "" | |
| echo "This indicates missing field declarations in components.xml" | |
| echo "" | |
| echo "Error details:" | |
| grep -B3 -A5 "NullPointerException" maven-output.log | head -20 | |
| exit 1 | |
| elif grep -q "NoSuchMethodError\|NoClassDefFoundError\|IncompatibleClassChangeError" maven-output.log; then | |
| echo "❌ COMPATIBILITY ERROR: Class/method compatibility issue detected!" | |
| echo "Maven ${{ matrix.maven-version }} is INCOMPATIBLE with build-info extractor" | |
| echo "" | |
| echo "Error details:" | |
| grep -B3 -A5 "NoSuchMethodError\|NoClassDefFoundError\|IncompatibleClassChangeError" maven-output.log | head -20 | |
| exit 1 | |
| else | |
| # Build failed but not due to Maven compatibility - could be: | |
| # - Compilation errors (test code issue) | |
| # - Network issues (transient) | |
| # - Missing dependencies (test setup issue) | |
| echo "⚠️ Build failed, but NO Maven compatibility errors detected" | |
| echo "" | |
| # Check if extractor actually ran | |
| if grep -q "Running Mvn\|Running mvn command\|Downloading JFrog's Dependency\|Initializing Artifactory Build-Info Recording" maven-output.log; then | |
| echo "✅ Build-info extractor loaded and ran successfully" | |
| echo "Maven ${{ matrix.maven-version }} with Java ${{ matrix.java-version }} - COMPATIBLE" | |
| echo "" | |
| echo "Note: Build failed for other reasons (not Maven compatibility)" | |
| echo "Last 30 lines of output:" | |
| tail -30 maven-output.log | |
| exit 0 | |
| else | |
| echo "❓ Extractor may not have loaded - manual review needed" | |
| echo "Last 50 lines of output:" | |
| tail -50 maven-output.log | |
| exit 1 | |
| fi | |
| fi | |
| - name: Upload logs (on failure) | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: failure-logs-maven-${{ matrix.maven-version }}-java-${{ matrix.java-version }} | |
| path: | | |
| ${{ github.workspace }}/test-project/maven-output.log | |
| ${{ github.workspace }}/test-project/config-output.log | |
| retention-days: 30 | |
| if-no-files-found: ignore | |
| create-issue-on-failure: | |
| name: Create issue if compatibility test fails | |
| needs: [test-maven-compatibility] | |
| # Only run on main branch or non-fork PRs (to avoid permission errors on fork PRs) | |
| if: failure() && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| steps: | |
| - name: Create issue | |
| uses: actions/github-script@v7 | |
| continue-on-error: true | |
| with: | |
| script: | | |
| const title = '🚨 Maven Compatibility Test Failed'; | |
| const body = `## Maven Compatibility Test Failure | |
| The automated Maven compatibility test has detected a failure with one or more Maven versions. | |
| **Details:** | |
| - Run: ${{ github.run_number }} | |
| - Workflow: ${{ github.workflow }} | |
| - Triggered by: ${{ github.event_name }} | |
| - Link: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| **Possible Causes:** | |
| 1. A new Maven version has been released with breaking changes | |
| 2. New required fields have been added to DefaultMavenPluginManager | |
| 3. Method signatures have changed in Maven core classes | |
| 4. New dependencies or requirements were introduced | |
| 5. Our Plexus components.xml is missing field declarations | |
| **Action Required:** | |
| 1. Check the "Check for new Maven versions" job output to see if new versions were detected | |
| 2. Check the workflow logs for specific error messages | |
| 3. Download the failure logs artifact from the workflow run | |
| 4. Look for error patterns: | |
| - \`NullPointerException\` → Missing field in components.xml | |
| - \`NoSuchMethodError\` → Method signature changed | |
| - \`NoClassDefFoundError\` → Missing dependency | |
| 5. If NullPointerException on a field: | |
| - Identify the missing field name from the error | |
| - Download Maven source for that version | |
| - Use \`CHECK-MAVEN-FIELDS.sh <maven-version>\` to compare fields | |
| - Update \`build-info-extractor-maven3/src/main/resources/META-INF/plexus/components.xml\` | |
| - Add missing field requirements to the ArtifactoryEclipsePluginManager component | |
| 6. If a new Maven version was detected, update the workflow matrix to include it | |
| **Helpful Commands:** | |
| \`\`\`bash | |
| # Check for missing fields in a specific Maven version | |
| ./CHECK-MAVEN-FIELDS.sh <maven-version> | |
| # Compare Maven's DefaultMavenPluginManager with our components.xml | |
| # Look for new fields that need to be added | |
| \`\`\` | |
| **Resources:** | |
| - Maven release notes: https://maven.apache.org/docs/history.html | |
| - Our components.xml: \`build-info-extractor-maven3/src/main/resources/META-INF/plexus/components.xml\` | |
| - DefaultMavenPluginManager source: Search in Maven's GitHub repository | |
| `; | |
| // Check if issue already exists | |
| const issues = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| labels: 'maven-compatibility' | |
| }); | |
| if (issues.data.length === 0) { | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: title, | |
| body: body, | |
| labels: ['bug', 'maven-compatibility', 'automated'] | |
| }); | |
| } else { | |
| console.log('Issue already exists, adding comment instead'); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issues.data[0].number, | |
| body: `Another failure detected in run #${{ github.run_number }}\n\nLink: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` | |
| }); | |
| } |