✨[Feature] Digital Signature Verification for HDF5 Plugins #580
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 Staging Repository Test | |
| # Triggers on pull requests that modify Maven-related files | |
| on: | |
| pull_request: | |
| branches: [ develop, main ] | |
| paths: | |
| - 'java/src/hdf/hdf5lib/**' | |
| - 'HDF5Examples/JAVA/**' | |
| - '.github/workflows/maven-*.yml' | |
| - '.github/workflows/java-examples-*.yml' | |
| - 'CMakePresets.json' | |
| - '**/CMakeLists.txt' | |
| - 'java/src/hdf/hdf5lib/pom.xml.in' | |
| - 'HDF5Examples/JAVA/pom-examples.xml.in' | |
| workflow_call: | |
| inputs: | |
| test_maven_deployment: | |
| description: 'Test Maven deployment to staging' | |
| type: boolean | |
| required: false | |
| default: true | |
| use_snapshot_version: | |
| description: 'Use snapshot version (-SNAPSHOT suffix)' | |
| type: boolean | |
| required: false | |
| default: true | |
| platforms: | |
| description: 'Build platforms for Maven artifacts' | |
| type: string | |
| required: false | |
| default: 'all-platforms' | |
| java_implementation: | |
| description: 'Java implementation to test' | |
| type: string | |
| required: false | |
| default: 'both' | |
| outputs: | |
| version: | |
| description: 'Maven artifact version that was built' | |
| value: ${{ jobs.test-maven-deployment.outputs.version }} | |
| workflow_dispatch: | |
| inputs: | |
| test_maven_deployment: | |
| description: 'Test Maven deployment to staging' | |
| type: boolean | |
| required: false | |
| default: true | |
| use_snapshot_version: | |
| description: 'Use snapshot version (-SNAPSHOT suffix)' | |
| type: boolean | |
| required: false | |
| default: true | |
| platforms: | |
| description: 'Build platforms for Maven artifacts' | |
| type: choice | |
| required: false | |
| default: 'all-platforms' | |
| options: | |
| - 'linux-only' | |
| - 'linux-windows' | |
| - 'linux-macos' | |
| - 'all-platforms' | |
| java_implementation: | |
| description: 'Java implementation to test' | |
| type: choice | |
| required: false | |
| default: 'both' | |
| options: | |
| - 'both' | |
| - 'ffm' | |
| - 'jni' | |
| - 'auto' | |
| permissions: | |
| contents: read | |
| packages: write | |
| pull-requests: write | |
| jobs: | |
| detect-changes: | |
| name: Detect Maven-related Changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| maven-changes: ${{ steps.changes.outputs.maven }} | |
| should-test: ${{ steps.should-test.outputs.result }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect changes in Maven-related files | |
| id: changes | |
| run: | | |
| # Check if this is a manual trigger | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then | |
| echo "maven=true" >> $GITHUB_OUTPUT | |
| echo "Manual workflow dispatch - Maven testing enabled" | |
| exit 0 | |
| fi | |
| # For push events, check the last commit | |
| if [ "${{ github.event_name }}" == "push" ]; then | |
| echo "maven=true" >> $GITHUB_OUTPUT | |
| echo "Push event - Maven testing enabled" | |
| exit 0 | |
| fi | |
| # For pull requests, check changed files | |
| if [ -n "${{ github.base_ref }}" ]; then | |
| git fetch origin ${{ github.base_ref }} | |
| MAVEN_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -E "(java/src/hdf/hdf5lib/|HDF5Examples/JAVA/|maven|pom\.xml|CMakePresets\.json)" || true) | |
| else | |
| # Fallback: assume changes if base_ref not available | |
| MAVEN_FILES="true" | |
| fi | |
| if [ -n "$MAVEN_FILES" ]; then | |
| echo "maven=true" >> $GITHUB_OUTPUT | |
| echo "Maven-related changes detected" | |
| else | |
| echo "maven=false" >> $GITHUB_OUTPUT | |
| echo "No Maven-related changes detected" | |
| fi | |
| - name: Determine if Maven testing should run | |
| id: should-test | |
| run: | | |
| if [ "${{ steps.changes.outputs.maven }}" == "true" ] || [ "${{ inputs.test_maven_deployment }}" == "true" ]; then | |
| echo "result=true" >> $GITHUB_OUTPUT | |
| echo "Maven testing will be performed" | |
| else | |
| echo "result=false" >> $GITHUB_OUTPUT | |
| echo "Maven testing will be skipped" | |
| fi | |
| determine-matrix: | |
| name: Determine Build Matrix | |
| needs: detect-changes | |
| if: ${{ needs.detect-changes.outputs.should-test == 'true' }} | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.set-matrix.outputs.matrix }} | |
| steps: | |
| - name: Determine build matrix | |
| id: set-matrix | |
| run: | | |
| JAVA_IMPL="${{ inputs.java_implementation || 'auto' }}" | |
| PLATFORMS="${{ inputs.platforms || 'all-platforms' }}" | |
| # Determine which implementations to build | |
| case "$JAVA_IMPL" in | |
| "ffm") | |
| IMPLEMENTATIONS='["ffm"]' | |
| ;; | |
| "jni") | |
| IMPLEMENTATIONS='["jni"]' | |
| ;; | |
| "both") | |
| IMPLEMENTATIONS='["ffm", "jni"]' | |
| ;; | |
| "auto"|*) | |
| # Auto means build based on Java version (defaults handled by presets) | |
| # We'll build once with auto setting | |
| IMPLEMENTATIONS='["auto"]' | |
| ;; | |
| esac | |
| # Determine which platforms to build | |
| case "$PLATFORMS" in | |
| "linux-only") | |
| PLATFORM_LIST='[{"name": "Linux", "os": "ubuntu-latest", "compiler": "GNUC", "arch": "x86_64"}]' | |
| ;; | |
| "linux-windows") | |
| PLATFORM_LIST='[{"name": "Linux", "os": "ubuntu-latest", "compiler": "GNUC", "arch": "x86_64"}, {"name": "Windows", "os": "windows-latest", "compiler": "MSVC", "arch": "x86_64"}]' | |
| ;; | |
| "linux-macos") | |
| PLATFORM_LIST='[{"name": "Linux", "os": "ubuntu-latest", "compiler": "GNUC", "arch": "x86_64"}, {"name": "macOS-x86_64", "os": "macos-14", "compiler": "Clang", "arch": "x86_64"}, {"name": "macOS-aarch64", "os": "macos-latest", "compiler": "Clang", "arch": "aarch64"}]' | |
| ;; | |
| "all-platforms"|*) | |
| PLATFORM_LIST='[{"name": "Linux", "os": "ubuntu-latest", "compiler": "GNUC", "arch": "x86_64"}, {"name": "Windows", "os": "windows-latest", "compiler": "MSVC", "arch": "x86_64"}, {"name": "macOS-x86_64", "os": "macos-14", "compiler": "Clang", "arch": "x86_64"}, {"name": "macOS-aarch64", "os": "macos-latest", "compiler": "Clang", "arch": "aarch64"}]' | |
| ;; | |
| esac | |
| # Create the full matrix combining platforms and implementations | |
| MATRIX="{\"include\":[" | |
| FIRST=true | |
| for platform in $(echo "$PLATFORM_LIST" | jq -c '.[]'); do | |
| for impl in $(echo "$IMPLEMENTATIONS" | jq -r '.[]'); do | |
| if [ "$FIRST" = true ]; then | |
| FIRST=false | |
| else | |
| MATRIX+="," | |
| fi | |
| # Extract platform details | |
| PLATFORM_NAME=$(echo "$platform" | jq -r '.name') | |
| PLATFORM_OS=$(echo "$platform" | jq -r '.os') | |
| PLATFORM_COMPILER=$(echo "$platform" | jq -r '.compiler') | |
| PLATFORM_ARCH=$(echo "$platform" | jq -r '.arch') | |
| # Create artifact name suffix | |
| # Platform name already includes arch for macOS (e.g., macOS-x86_64) | |
| PLATFORM_LOWER="${PLATFORM_NAME,,}" | |
| if [[ "$PLATFORM_LOWER" == *"-"* ]]; then | |
| # Platform name already has arch suffix (macOS case) | |
| BASE_NAME="$PLATFORM_LOWER" | |
| else | |
| # Add arch suffix (Linux, Windows case) | |
| BASE_NAME="${PLATFORM_LOWER}-${PLATFORM_ARCH}" | |
| fi | |
| if [ "$impl" = "auto" ]; then | |
| ARTIFACT_SUFFIX="$BASE_NAME" | |
| else | |
| ARTIFACT_SUFFIX="${BASE_NAME}-${impl}" | |
| fi | |
| MATRIX+="{\"platform\":\"$PLATFORM_NAME\",\"os\":\"$PLATFORM_OS\",\"compiler\":\"$PLATFORM_COMPILER\",\"arch\":\"$PLATFORM_ARCH\",\"implementation\":\"$impl\",\"artifact-suffix\":\"$ARTIFACT_SUFFIX\"}" | |
| done | |
| done | |
| MATRIX+="]}" | |
| echo "matrix=$MATRIX" >> $GITHUB_OUTPUT | |
| echo "Generated matrix:" | |
| echo "$MATRIX" | jq '.' | |
| build-maven-artifacts: | |
| name: "Build Maven Artifacts (${{ matrix.platform }} - ${{ matrix.implementation }})" | |
| needs: [detect-changes, determine-matrix] | |
| if: ${{ needs.detect-changes.outputs.should-test == 'true' }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.determine-matrix.outputs.matrix) }} | |
| steps: | |
| - name: Install Dependencies (Linux) | |
| if: ${{ runner.os == 'Linux' }} | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install ninja-build graphviz | |
| - name: Install Dependencies (macOS) | |
| if: ${{ runner.os == 'macOS' }} | |
| run: | | |
| brew install ninja graphviz | |
| - name: Install Dependencies (Windows) | |
| if: ${{ runner.os == 'Windows' }} | |
| run: | | |
| choco install ninja | |
| choco install graphviz | |
| - name: Enable Developer Command Prompt (Windows) | |
| if: ${{ runner.os == 'Windows' }} | |
| uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 | |
| - name: Set up JDK (Java 25 for FFM builds) | |
| uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 | |
| with: | |
| java-version: ${{ matrix.implementation == 'ffm' && '25' || '21' }} | |
| distribution: ${{ matrix.implementation == 'ffm' && 'oracle' || 'temurin' }} | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup jextract (FFM builds only) | |
| if: ${{ matrix.implementation == 'ffm' }} | |
| uses: ./.github/actions/setup-jextract | |
| with: | |
| java-version: '25' | |
| - name: Set preset name | |
| id: set-preset | |
| shell: bash | |
| run: | | |
| # Determine implementation suffix for preset based on matrix | |
| JAVA_IMPL="${{ matrix.implementation }}" | |
| case "$JAVA_IMPL" in | |
| "ffm") | |
| IMPL_SUFFIX="-FFM" | |
| ;; | |
| "jni"|"auto"|*) | |
| # JNI and auto use default Maven presets (JNI is default as of HDF5 2.0) | |
| IMPL_SUFFIX="" | |
| ;; | |
| esac | |
| # Determine snapshot setting | |
| SNAPSHOT_SUFFIX="" | |
| if [ "${{ inputs.use_snapshot_version }}" == "true" ] || [ "${{ github.event_name }}" == "pull_request" ]; then | |
| SNAPSHOT_SUFFIX="-Snapshot" | |
| fi | |
| # Build preset name: ci-MinShar-{COMPILER}-Maven{-FFM}{-Snapshot} | |
| # Note: Generic Maven presets default to JNI (no suffix needed) | |
| PRESET_NAME="ci-MinShar-${{ matrix.compiler }}-Maven${IMPL_SUFFIX}${SNAPSHOT_SUFFIX}" | |
| echo "preset=$PRESET_NAME" >> $GITHUB_OUTPUT | |
| echo "Using preset: $PRESET_NAME" | |
| echo "Platform: ${{ matrix.platform }}, Compiler: ${{ matrix.compiler }}, Implementation: $JAVA_IMPL" | |
| - name: Build HDF5 with Maven support | |
| id: buildhdf5 | |
| shell: bash | |
| run: | | |
| # macOS: Disable Spotlight indexing to prevent file locking during CPack | |
| if [[ "$RUNNER_OS" == "macOS" ]]; then | |
| echo "Disabling Spotlight indexing temporarily" | |
| sudo mdutil -i off / || true | |
| fi | |
| cd "${{ github.workspace }}" | |
| # Run workflow with retry logic for macOS CPack failures | |
| if [[ "$RUNNER_OS" == "macOS" ]]; then | |
| echo "Running CMake workflow with macOS retry logic..." | |
| if ! cmake --workflow --preset="${{ steps.set-preset.outputs.preset }}" --fresh; then | |
| echo "⚠️ First attempt failed, retrying in 5 seconds..." | |
| sleep 5 | |
| cmake --workflow --preset="${{ steps.set-preset.outputs.preset }}" --fresh | |
| fi | |
| else | |
| # Non-macOS: run normally without retry | |
| cmake --workflow --preset="${{ steps.set-preset.outputs.preset }}" --fresh | |
| fi | |
| - name: Extract version information | |
| id: version-info | |
| shell: bash | |
| run: | | |
| # Find the generated POM file across all possible locations | |
| BUILD_ROOT="${{ runner.workspace }}/build/${{ steps.set-preset.outputs.preset }}" | |
| echo "Looking for POM file in: $BUILD_ROOT" | |
| # Try multiple search patterns for cross-platform compatibility | |
| POM_FILE="" | |
| # Search patterns in order of preference | |
| if [ -z "$POM_FILE" ]; then | |
| POM_FILE=$(find "$BUILD_ROOT" -name "pom.xml" -path "*/java/*" 2>/dev/null | head -1) | |
| fi | |
| if [ -z "$POM_FILE" ]; then | |
| POM_FILE=$(find "$BUILD_ROOT" -name "pom.xml" 2>/dev/null | head -1) | |
| fi | |
| # Try Maven artifacts directory if build structure differs | |
| if [ -z "$POM_FILE" ] && [ -d "${{ runner.workspace }}/maven-artifacts" ]; then | |
| POM_FILE=$(find "${{ runner.workspace }}/maven-artifacts" -name "pom.xml" 2>/dev/null | head -1) | |
| fi | |
| if [ -n "$POM_FILE" ] && [ -f "$POM_FILE" ]; then | |
| # Extract version using robust pattern that works across platforms | |
| VERSION=$(grep -o '<version>[^<]*</version>' "$POM_FILE" | head -1 | sed 's/<[^>]*>//g' | tr -d '\r\n') | |
| # Validate version format | |
| if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "✓ Detected version: $VERSION" | |
| echo "✓ POM file: $POM_FILE" | |
| else | |
| echo "version=unknown" >> $GITHUB_OUTPUT | |
| echo "❌ Invalid version format detected: $VERSION" | |
| exit 1 | |
| fi | |
| else | |
| echo "version=unknown" >> $GITHUB_OUTPUT | |
| echo "❌ Could not find POM file" | |
| echo "Available files in build root:" | |
| find "$BUILD_ROOT" -name "*.xml" -o -name "pom*" 2>/dev/null | head -10 || echo "No XML files found" | |
| exit 1 | |
| fi | |
| - name: Collect Maven artifacts | |
| shell: bash | |
| run: | | |
| echo "Collecting Maven artifacts for testing..." | |
| mkdir -p "${{ runner.workspace }}/maven-artifacts" | |
| BUILD_ROOT="${{ runner.workspace }}/build/${{ steps.set-preset.outputs.preset }}" | |
| echo "Looking for Maven artifacts in build root: $BUILD_ROOT" | |
| if [ ! -d "$BUILD_ROOT" ]; then | |
| echo "ERROR: Build root directory does not exist: $BUILD_ROOT" | |
| exit 1 | |
| fi | |
| # Debug: Show what's in the build directory | |
| echo "Build directory contents:" | |
| find "$BUILD_ROOT" -maxdepth 3 -type d 2>/dev/null | head -20 || echo "Directory listing completed" | |
| # Find build directory (try multiple patterns) | |
| BUILD_DIR=$(find "$BUILD_ROOT" -name "*Maven*" -type d 2>/dev/null | head -1) | |
| if [ -z "$BUILD_DIR" ]; then | |
| # Try looking for java directory as fallback (more specific patterns) | |
| BUILD_DIR=$(find "$BUILD_ROOT" -path "*/java/*" -type d 2>/dev/null | head -1) | |
| if [ -z "$BUILD_DIR" ]; then | |
| BUILD_DIR=$(find "$BUILD_ROOT" -name "java" -type d 2>/dev/null | head -1) | |
| fi | |
| fi | |
| if [ -z "$BUILD_DIR" ]; then | |
| # Try looking for any directory with JAR files | |
| JAR_FILE=$(find "$BUILD_ROOT" -name "*.jar" -type f 2>/dev/null | head -1) | |
| if [ -n "$JAR_FILE" ]; then | |
| BUILD_DIR=$(dirname "$JAR_FILE") | |
| echo "Found JAR file in: $BUILD_DIR" | |
| fi | |
| fi | |
| if [ -z "$BUILD_DIR" ]; then | |
| # Last resort: look for the build directory itself if it contains artifacts | |
| if find "$BUILD_ROOT" -name "*.jar" -o -name "pom.xml" | grep -q .; then | |
| BUILD_DIR="$BUILD_ROOT" | |
| echo "Using build root as artifacts directory" | |
| fi | |
| fi | |
| if [ -z "$BUILD_DIR" ]; then | |
| echo "ERROR: Could not find Maven build directory with any of the patterns" | |
| echo "Available directories:" | |
| find "$BUILD_ROOT" -maxdepth 2 -type d | |
| exit 1 | |
| fi | |
| echo "Looking for artifacts in: $BUILD_DIR" | |
| # Debug: Show all JAR files in build directory | |
| echo "All JAR files in build directory:" | |
| find "$BUILD_DIR" -name "*.jar" -type f 2>/dev/null | head -20 || true | |
| # Debug: Show specifically what we're looking for | |
| echo "SLF4J JAR files in build directory:" | |
| find "$BUILD_DIR" -name "*slf4j*.jar" -type f | |
| # Copy JAR files (excluding test and H5Ex_ example JARs) | |
| find "$BUILD_DIR" -name "*.jar" -not -name "*test*" -not -name "*H5Ex_*" -exec cp {} "${{ runner.workspace }}/maven-artifacts/" \; | |
| # Also look for Maven dependencies in common locations | |
| echo "Looking for Maven dependencies in additional locations..." | |
| # Check if there's a Maven repository in the build area | |
| if [ -d "$BUILD_ROOT" ]; then | |
| find "$BUILD_ROOT" -name "*slf4j*.jar" -type f -exec cp {} "${{ runner.workspace }}/maven-artifacts/" \; | |
| fi | |
| # Check common Maven local repository locations | |
| MAVEN_REPO_PATHS=( | |
| "$HOME/.m2/repository" | |
| "$BUILD_ROOT/.m2/repository" | |
| "${{ runner.workspace }}/.m2/repository" | |
| ) | |
| for repo_path in "${MAVEN_REPO_PATHS[@]}"; do | |
| if [ -d "$repo_path" ]; then | |
| echo "Checking Maven repository: $repo_path" | |
| find "$repo_path" -name "slf4j-api*.jar" -o -name "slf4j-simple*.jar" 2>/dev/null | head -2 | while read jar_file; do | |
| if [ -f "$jar_file" ]; then | |
| echo "Found dependency: $jar_file" | |
| cp "$jar_file" "${{ runner.workspace }}/maven-artifacts/" | |
| fi | |
| done | |
| fi | |
| done | |
| # Copy POM files | |
| find "$BUILD_DIR" -name "pom.xml" -exec cp {} "${{ runner.workspace }}/maven-artifacts/" \; | |
| # List collected artifacts | |
| echo "Collected Maven artifacts:" | |
| ls -la "${{ runner.workspace }}/maven-artifacts/" | |
| - name: Validate artifacts | |
| id: artifacts-check | |
| shell: bash | |
| run: | | |
| ARTIFACT_COUNT=$(find "${{ runner.workspace }}/maven-artifacts" -name "*.jar" | wc -l) | |
| POM_COUNT=$(find "${{ runner.workspace }}/maven-artifacts" -name "pom.xml" | wc -l) | |
| echo "Found $ARTIFACT_COUNT JAR files and $POM_COUNT POM files" | |
| if [ $ARTIFACT_COUNT -gt 0 ] && [ $POM_COUNT -gt 0 ]; then | |
| echo "created=true" >> $GITHUB_OUTPUT | |
| echo "✅ Artifacts successfully created" | |
| else | |
| echo "created=false" >> $GITHUB_OUTPUT | |
| echo "❌ Artifact creation failed" | |
| exit 1 | |
| fi | |
| - name: Verify JAR contents (CRITICAL - Prevents Incomplete Packages) | |
| shell: bash | |
| run: | | |
| echo "::group::Verify JAR contents" | |
| echo "============================================" | |
| echo "Verifying Maven artifacts contain HDF5 classes" | |
| echo "============================================" | |
| ARTIFACT_DIR="${{ runner.workspace }}/maven-artifacts" | |
| VERIFICATION_FAILED=false | |
| IMPL="${{ matrix.implementation }}" | |
| # Find the main HDF5 JAR (not slf4j dependencies) | |
| HDF5_JAR=$(find "$ARTIFACT_DIR" -name "*hdf5*.jar" ! -name "*sources*" ! -name "*javadoc*" ! -name "*slf4j*" | head -1) | |
| if [ -z "$HDF5_JAR" ]; then | |
| # Try alternative naming (jarhdf5) | |
| HDF5_JAR=$(find "$ARTIFACT_DIR" -name "jarhdf5*.jar" ! -name "*sources*" ! -name "*javadoc*" | head -1) | |
| fi | |
| if [ -z "$HDF5_JAR" ]; then | |
| echo "::error::Could not find HDF5 JAR file in artifacts" | |
| echo "Available JAR files:" | |
| find "$ARTIFACT_DIR" -name "*.jar" -ls | |
| exit 1 | |
| fi | |
| echo "Found HDF5 JAR: $HDF5_JAR" | |
| echo "JAR size: $(du -h "$HDF5_JAR" | cut -f1)" | |
| echo "" | |
| # Determine implementation type (FFM or JNI) | |
| if [ "$IMPL" = "ffm" ]; then | |
| echo "Verifying FFM implementation..." | |
| echo "Expected package: org.hdfgroup.javahdf5.*" | |
| # Check for key FFM classes | |
| if jar tf "$HDF5_JAR" | grep -q "org/hdfgroup/javahdf5/hdf5_h.class"; then | |
| CLASS_COUNT=$(jar tf "$HDF5_JAR" | grep "org/hdfgroup/javahdf5.*\.class" | wc -l) | |
| echo "✅ JAR contains HDF5 FFM classes" | |
| echo " Found $CLASS_COUNT FFM classes" | |
| # List some sample classes to verify content | |
| echo " Sample classes:" | |
| jar tf "$HDF5_JAR" | grep "org/hdfgroup/javahdf5.*\.class" | head -5 2>/dev/null | sed 's/^/ - /' || true | |
| else | |
| echo "::error::JAR does not contain HDF5 FFM classes!" | |
| echo "JAR only contains:" | |
| jar tf "$HDF5_JAR" | head -30 2>/dev/null || true | |
| VERIFICATION_FAILED=true | |
| fi | |
| else | |
| # JNI or auto (defaults to JNI) | |
| echo "Verifying JNI implementation..." | |
| echo "Expected package: hdf.hdf5lib.*" | |
| # Check for key JNI classes | |
| if jar tf "$HDF5_JAR" | grep -q "hdf/hdf5lib/H5.class"; then | |
| CLASS_COUNT=$(jar tf "$HDF5_JAR" | grep "hdf/hdf5lib.*\.class" | wc -l) | |
| echo "✅ JAR contains HDF5 JNI classes" | |
| echo " Found $CLASS_COUNT JNI classes" | |
| # Verify key classes are present | |
| MISSING_CLASSES="" | |
| for key_class in "hdf/hdf5lib/H5.class" "hdf/hdf5lib/HDF5Constants.class" "hdf/hdf5lib/HDFNativeData.class"; do | |
| if ! jar tf "$HDF5_JAR" | grep -q "$key_class"; then | |
| MISSING_CLASSES="$MISSING_CLASSES $key_class" | |
| fi | |
| done | |
| if [ -n "$MISSING_CLASSES" ]; then | |
| echo "::warning::Some expected classes are missing:$MISSING_CLASSES" | |
| fi | |
| # List some sample classes to verify content | |
| echo " Sample classes:" | |
| jar tf "$HDF5_JAR" | grep "hdf/hdf5lib.*\.class" | head -5 2>/dev/null | sed 's/^/ - /' || true | |
| else | |
| echo "::error::JAR does not contain HDF5 JNI classes!" | |
| echo "JAR only contains:" | |
| jar tf "$HDF5_JAR" | head -30 2>/dev/null || true | |
| VERIFICATION_FAILED=true | |
| fi | |
| fi | |
| # Additional checks | |
| echo "" | |
| echo "Additional JAR checks:" | |
| # Check JAR size (should be > 50KB for real HDF5 classes) | |
| JAR_SIZE=$(stat -f%z "$HDF5_JAR" 2>/dev/null || stat -c%s "$HDF5_JAR" 2>/dev/null || echo "0") | |
| if [ "$JAR_SIZE" -lt 51200 ]; then | |
| echo "::warning::JAR size is suspiciously small: $JAR_SIZE bytes (expected > 50KB)" | |
| echo " This may indicate the JAR only contains dependencies." | |
| else | |
| echo "✅ JAR size is reasonable: $(($JAR_SIZE / 1024)) KB" | |
| fi | |
| # Count total files in JAR | |
| TOTAL_FILES=$(jar tf "$HDF5_JAR" | wc -l) | |
| echo "✅ Total files in JAR: $TOTAL_FILES" | |
| echo "::endgroup::" | |
| if [ "$VERIFICATION_FAILED" = true ]; then | |
| echo "" | |
| echo "::error::❌ JAR verification failed!" | |
| echo "::error::The Maven artifact does not contain HDF5 classes." | |
| echo "::error::This would result in an incomplete/broken package being deployed." | |
| echo "::error::" | |
| echo "::error::Possible causes:" | |
| echo "::error:: 1. CMake add_jar not executing correctly" | |
| echo "::error:: 2. Wrong JAR file being collected" | |
| echo "::error:: 3. Build failure that went undetected" | |
| echo "::error::" | |
| echo "::error::Please review the build logs and CMake configuration." | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "✅ JAR verification passed - artifact is complete!" | |
| - name: Run validation script | |
| shell: bash | |
| run: | | |
| if [ -f .github/scripts/validate-maven-artifacts.sh ]; then | |
| echo "Running Maven artifact validation..." | |
| .github/scripts/validate-maven-artifacts.sh "${{ runner.workspace }}/maven-artifacts" | |
| else | |
| echo "Validation script not found - skipping validation" | |
| fi | |
| - name: Install HDF5 binaries for testing | |
| shell: bash | |
| run: | | |
| BUILD_ROOT="${{ runner.workspace }}/build/${{ steps.set-preset.outputs.preset }}" | |
| INSTALL_DIR="${{ runner.workspace }}/hdf5-install" | |
| echo "Installing HDF5 binaries to: $INSTALL_DIR" | |
| cd "$BUILD_ROOT" | |
| cmake --install . --prefix "$INSTALL_DIR" | |
| echo "Installation contents:" | |
| ls -la "$INSTALL_DIR" | |
| if [ -d "$INSTALL_DIR/lib" ]; then | |
| echo "Libraries:" | |
| ls -la "$INSTALL_DIR/lib" | grep -E '\.so|\.dylib|\.dll' || echo "No shared libraries found" | |
| fi | |
| - name: Upload Maven artifacts | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v5 | |
| with: | |
| name: maven-staging-artifacts-${{ matrix.artifact-suffix }} | |
| path: ${{ runner.workspace }}/maven-artifacts | |
| retention-days: 7 | |
| - name: Upload HDF5 installation for testing | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v5 | |
| with: | |
| name: hdf5-install-${{ matrix.artifact-suffix }} | |
| path: ${{ runner.workspace }}/hdf5-install | |
| retention-days: 7 | |
| test-maven-deployment: | |
| name: Test Maven Deployment | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes, determine-matrix, build-maven-artifacts] | |
| if: ${{ always() && needs.detect-changes.outputs.should-test == 'true' }} | |
| outputs: | |
| version: ${{ steps.extract-version.outputs.version }} | |
| steps: | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 | |
| with: | |
| java-version: '21' | |
| distribution: 'temurin' | |
| - name: Download all Maven artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v6 | |
| with: | |
| pattern: maven-staging-artifacts-* | |
| path: ./artifacts | |
| merge-multiple: false | |
| - name: Checkout code for version extraction | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| path: source | |
| - name: Extract version for testing | |
| id: extract-version | |
| run: | | |
| # Extract version from first available POM file | |
| POM_FILE=$(find ./artifacts -name "pom.xml" 2>/dev/null | head -1) | |
| if [ -n "$POM_FILE" ]; then | |
| VERSION=$(grep -o '<version>[^<]*</version>' "$POM_FILE" | head -1 | sed 's/<[^>]*>//g') | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "✅ Extracted version from POM: $VERSION" | |
| else | |
| # Fallback: Extract version from H5public.h | |
| echo "⚠️ No POM file found, extracting version from source..." | |
| if [ -f "source/src/H5public.h" ]; then | |
| H5_MAJOR=$(grep '#define H5_VERS_MAJOR' source/src/H5public.h | awk '{print $3}') | |
| H5_MINOR=$(grep '#define H5_VERS_MINOR' source/src/H5public.h | awk '{print $3}') | |
| H5_RELEASE=$(grep '#define H5_VERS_RELEASE' source/src/H5public.h | awk '{print $3}') | |
| # Determine if this should be a SNAPSHOT version | |
| if [ "${{ inputs.use_snapshot_version }}" == "true" ] || [ "${{ github.event_name }}" == "pull_request" ]; then | |
| VERSION="${H5_MAJOR}.${H5_MINOR}.${H5_RELEASE}-SNAPSHOT" | |
| else | |
| VERSION="${H5_MAJOR}.${H5_MINOR}.${H5_RELEASE}" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "✅ Extracted version from H5public.h: $VERSION" | |
| else | |
| echo "::error::Could not extract version from POM or source files" | |
| exit 1 | |
| fi | |
| fi | |
| - name: Test Maven deployment (dry run) | |
| run: | | |
| echo "=== Maven Deployment Test (Dry Run) ===" | |
| echo "Version: ${{ steps.extract-version.outputs.version }}" | |
| echo "Repository: GitHub Packages (staging)" | |
| # List artifacts to be deployed by platform | |
| echo "Artifacts ready for deployment:" | |
| for platform_dir in ./artifacts/*/; do | |
| if [ -d "$platform_dir" ]; then | |
| platform_name=$(basename "$platform_dir") | |
| echo "Platform: $platform_name" | |
| find "$platform_dir" -name "*.jar" -exec basename {} \; | sed 's/^/ - /' | |
| fi | |
| done | |
| # Create test Maven settings | |
| mkdir -p ~/.m2 | |
| cat > ~/.m2/settings.xml << 'EOF' | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"> | |
| <servers> | |
| <server> | |
| <id>github</id> | |
| <username>${env.GITHUB_ACTOR}</username> | |
| <password>${env.GITHUB_TOKEN}</password> | |
| </server> | |
| </servers> | |
| </settings> | |
| EOF | |
| # Simulate deployment validation for each platform | |
| total_jars=0 | |
| valid_jars=0 | |
| for jar_file in $(find ./artifacts -name "*.jar"); do | |
| jar_name=$(basename "$jar_file") | |
| platform=$(dirname "$jar_file" | sed 's|./artifacts/||') | |
| total_jars=$((total_jars + 1)) | |
| echo "[$platform] Testing: $jar_name" | |
| # Test JAR integrity | |
| if jar tf "$jar_file" > /dev/null 2>&1; then | |
| echo " ✓ JAR integrity verified" | |
| valid_jars=$((valid_jars + 1)) | |
| else | |
| echo " ❌ JAR integrity check failed" | |
| fi | |
| done | |
| echo "Validation summary: $valid_jars/$total_jars JARs passed integrity check" | |
| if [ $valid_jars -ne $total_jars ]; then | |
| echo "❌ Some artifacts failed validation" | |
| exit 1 | |
| fi | |
| echo "🎉 Dry run deployment test passed!" | |
| test-java-examples-maven: | |
| name: "Test Java Examples with Maven Artifacts (${{ matrix.platform }} - ${{ matrix.implementation }})" | |
| runs-on: ${{ matrix.os }} | |
| needs: [detect-changes, determine-matrix, build-maven-artifacts] | |
| if: ${{ always() && needs.detect-changes.outputs.should-test == 'true' && needs.build-maven-artifacts.result == 'success' }} | |
| continue-on-error: true # Non-blocking failures | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.determine-matrix.outputs.matrix) }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up JDK (Java 25 for FFM builds) | |
| uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 | |
| with: | |
| java-version: ${{ matrix.implementation == 'ffm' && '25' || '21' }} | |
| distribution: ${{ matrix.implementation == 'ffm' && 'oracle' || 'temurin' }} | |
| - name: Install timeout command on macOS | |
| if: ${{ startsWith(matrix.platform, 'macOS') }} | |
| run: | | |
| # Install GNU coreutils which includes gtimeout | |
| brew install coreutils | |
| echo "Installed gtimeout: $(which gtimeout)" | |
| - name: Download Maven artifacts (${{ matrix.platform }} - ${{ matrix.implementation }}) | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v6 | |
| with: | |
| name: maven-staging-artifacts-${{ matrix.artifact-suffix }} | |
| path: ./maven-artifacts/${{ matrix.platform }} | |
| continue-on-error: true | |
| - name: Cache Maven dependencies | |
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4 | |
| with: | |
| path: ~/.m2/repository | |
| key: ${{ runner.os }}-maven-examples-${{ hashFiles('**/pom-examples.xml*') }} | |
| restore-keys: | | |
| ${{ runner.os }}-maven-examples- | |
| ${{ runner.os }}-maven- | |
| - name: Test Java Examples (Unix) | |
| if: ${{ matrix.platform != 'Windows' }} | |
| id: test-examples-unix | |
| shell: bash | |
| run: | | |
| echo "=== Java Examples Maven Integration Test (${{ matrix.platform }}) ===" | |
| # Test one representative example from each category | |
| cd HDF5Examples/JAVA | |
| # Get absolute path to maven artifacts for this platform | |
| MAVEN_ARTIFACTS_DIR="$(realpath ../../maven-artifacts/${{ matrix.platform }})" | |
| echo "Maven artifacts directory (${{ matrix.platform }}): $MAVEN_ARTIFACTS_DIR" | |
| # Find HDF5 JAR files (not dependencies like slf4j) - use platform-specific artifacts | |
| HDF5_JAR=$(find "$MAVEN_ARTIFACTS_DIR" -name "*hdf5*.jar" -o -name "jarhdf5*.jar" 2>/dev/null | head -1) | |
| if [ -z "$HDF5_JAR" ]; then | |
| echo "❌ No HDF5 JAR files found for testing" | |
| echo "Available JAR files:" | |
| find "$MAVEN_ARTIFACTS_DIR" -name "*.jar" | |
| exit 1 | |
| fi | |
| echo "Using HDF5 JAR file: $HDF5_JAR" | |
| # Detect JAR type by checking for FFM-specific classes | |
| # FFM JARs contain org.hdfgroup.javahdf5 package, JNI JARs contain hdf.hdf5lib | |
| if jar tf "$HDF5_JAR" | grep -q "org/hdfgroup/javahdf5/hdf5_h.class"; then | |
| JAR_TYPE="ffm" | |
| echo "Detected FFM JAR (contains org.hdfgroup.javahdf5 package)" | |
| else | |
| JAR_TYPE="jni" | |
| echo "Detected JNI JAR (contains hdf.hdf5lib package)" | |
| fi | |
| # Determine which examples directory to use based on detected JAR type | |
| # This ensures we test with examples that match the JAR's implementation | |
| if [ "$JAR_TYPE" = "jni" ]; then | |
| EXAMPLES_DIR="compat" | |
| echo "Using JNI-compatible examples from compat/ directory" | |
| else | |
| EXAMPLES_DIR="." | |
| echo "Using FFM examples from root directory" | |
| fi | |
| # Also find any dependency JARs - only include slf4j-api and slf4j-simple, exclude slf4j-nop to avoid conflicts | |
| DEP_JARS=$(find "$MAVEN_ARTIFACTS_DIR" -name "slf4j-api*.jar" -o -name "slf4j-simple*.jar") | |
| if [ -n "$DEP_JARS" ]; then | |
| echo "Found dependency JARs:" | |
| echo "$DEP_JARS" | |
| else | |
| echo "No SLF4J dependency JARs found in artifacts, downloading them directly..." | |
| # Download SLF4J dependencies directly using Maven | |
| SLF4J_VERSION="2.0.16" | |
| TEMP_POM="$MAVEN_ARTIFACTS_DIR/temp-pom.xml" | |
| # Create a minimal POM to download dependencies using echo | |
| echo '<?xml version="1.0" encoding="UTF-8"?>' > "$TEMP_POM" | |
| echo '<project xmlns="http://maven.apache.org/POM/4.0.0"' >> "$TEMP_POM" | |
| echo ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' >> "$TEMP_POM" | |
| echo ' xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">' >> "$TEMP_POM" | |
| echo ' <modelVersion>4.0.0</modelVersion>' >> "$TEMP_POM" | |
| echo ' <groupId>temp</groupId>' >> "$TEMP_POM" | |
| echo ' <artifactId>temp</artifactId>' >> "$TEMP_POM" | |
| echo ' <version>1.0</version>' >> "$TEMP_POM" | |
| echo ' <dependencies>' >> "$TEMP_POM" | |
| echo ' <dependency>' >> "$TEMP_POM" | |
| echo ' <groupId>org.slf4j</groupId>' >> "$TEMP_POM" | |
| echo ' <artifactId>slf4j-api</artifactId>' >> "$TEMP_POM" | |
| echo ' <version>2.0.16</version>' >> "$TEMP_POM" | |
| echo ' </dependency>' >> "$TEMP_POM" | |
| echo ' <dependency>' >> "$TEMP_POM" | |
| echo ' <groupId>org.slf4j</groupId>' >> "$TEMP_POM" | |
| echo ' <artifactId>slf4j-simple</artifactId>' >> "$TEMP_POM" | |
| echo ' <version>2.0.16</version>' >> "$TEMP_POM" | |
| echo ' </dependency>' >> "$TEMP_POM" | |
| echo ' </dependencies>' >> "$TEMP_POM" | |
| echo '</project>' >> "$TEMP_POM" | |
| # Download dependencies | |
| if (cd "$MAVEN_ARTIFACTS_DIR" && mvn -f temp-pom.xml dependency:copy-dependencies -DoutputDirectory=. -DincludeScope=runtime -q); then | |
| # Clean up temporary POM | |
| rm -f "$TEMP_POM" | |
| # Re-scan for dependency JARs | |
| DEP_JARS=$(find "$MAVEN_ARTIFACTS_DIR" -name "slf4j-api*.jar" -o -name "slf4j-simple*.jar") | |
| if [ -n "$DEP_JARS" ]; then | |
| echo "Successfully downloaded SLF4J dependencies:" | |
| echo "$DEP_JARS" | |
| else | |
| echo "Failed to download SLF4J dependencies" | |
| fi | |
| else | |
| echo "Error downloading dependencies with Maven" | |
| rm -f "$TEMP_POM" | |
| fi | |
| fi | |
| FAILED_EXAMPLES="" | |
| TOTAL_EXAMPLES=0 | |
| PASSED_EXAMPLES=0 | |
| # Save current directory (HDF5Examples/JAVA) | |
| JAVA_DIR="$(pwd)" | |
| # Test representative examples from each category | |
| for category in H5D H5T H5G TUTR; do | |
| if [ -d "$EXAMPLES_DIR/$category" ]; then | |
| cd "$JAVA_DIR/$EXAMPLES_DIR/$category" | |
| # Find first .java file in category | |
| EXAMPLE_FILE=$(ls *.java | head -1) | |
| if [ -f "$EXAMPLE_FILE" ]; then | |
| TOTAL_EXAMPLES=$((TOTAL_EXAMPLES + 1)) | |
| example_name=$(basename "$EXAMPLE_FILE" .java) | |
| echo "--- Testing $category/$example_name ---" | |
| # Build classpath with HDF5 JAR and dependencies (already absolute paths) | |
| CLASSPATH="$HDF5_JAR" | |
| if [ -n "$DEP_JARS" ]; then | |
| for dep_jar in $DEP_JARS; do | |
| CLASSPATH="$CLASSPATH:$dep_jar" | |
| done | |
| fi | |
| echo "Using classpath: $CLASSPATH" | |
| # Set compilation flags based on detected JAR type | |
| # This ensures we use correct flags for the actual JAR implementation | |
| if [ "$JAR_TYPE" = "ffm" ]; then | |
| # FFM requires preview features (Java 25) | |
| JAVAC_FLAGS="--enable-preview --release 25" | |
| JAVA_FLAGS="--enable-preview" | |
| else | |
| # JNI uses standard Java | |
| JAVAC_FLAGS="" | |
| JAVA_FLAGS="" | |
| fi | |
| # Test compilation | |
| COMPILE_OUTPUT=$(javac $JAVAC_FLAGS -cp "$CLASSPATH" "$EXAMPLE_FILE" 2>&1) | |
| if [ $? -eq 0 ]; then | |
| echo "✓ Compilation successful for $category/$example_name" | |
| # Test execution (with cross-platform timeout) | |
| if command -v gtimeout >/dev/null 2>&1; then | |
| # macOS with GNU coreutils timeout | |
| EXEC_RESULT=$(gtimeout 10s java $JAVA_FLAGS -cp ".:$CLASSPATH" "$example_name" >/tmp/${example_name}.out 2>&1; echo $?) | |
| elif command -v timeout >/dev/null 2>&1; then | |
| # Linux/Windows with timeout command | |
| EXEC_RESULT=$(timeout 10s java $JAVA_FLAGS -cp ".:$CLASSPATH" "$example_name" >/tmp/${example_name}.out 2>&1; echo $?) | |
| else | |
| # Fallback - run without timeout (Java examples should complete quickly) | |
| echo "Note: Running without timeout" | |
| java $JAVA_FLAGS -cp ".:$CLASSPATH" "$example_name" >/tmp/${example_name}.out 2>&1 | |
| EXEC_RESULT=$? | |
| fi | |
| if [ "$EXEC_RESULT" -eq 0 ]; then | |
| # Basic output validation | |
| if grep -q -i -E "(dataset|datatype|group|success|created|written|read)" /tmp/${example_name}.out && \ | |
| ! grep -q -i -E "(error|exception|failed|cannot)" /tmp/${example_name}.out; then | |
| echo "✓ Execution and validation successful for $category/$example_name" | |
| PASSED_EXAMPLES=$((PASSED_EXAMPLES + 1)) | |
| else | |
| echo "✗ Output validation failed for $category/$example_name" | |
| echo "Output:" | |
| cat /tmp/${example_name}.out | |
| FAILED_EXAMPLES="$FAILED_EXAMPLES $category/$example_name" | |
| fi | |
| else | |
| # Check if failure is due to expected native library issue (acceptable for Maven-only testing) | |
| if grep -q "UnsatisfiedLinkError.*hdf5_java.*java.library.path" /tmp/${example_name}.out; then | |
| echo "✓ Expected native library error for Maven-only testing: $category/$example_name" | |
| echo " (This confirms JAR structure is correct)" | |
| PASSED_EXAMPLES=$((PASSED_EXAMPLES + 1)) | |
| else | |
| echo "✗ Unexpected execution failure for $category/$example_name" | |
| echo "Output:" | |
| cat /tmp/${example_name}.out | |
| FAILED_EXAMPLES="$FAILED_EXAMPLES $category/$example_name" | |
| fi | |
| fi | |
| else | |
| echo "✗ Compilation failed for $category/$example_name" | |
| echo "Compilation error output:" | |
| echo "$COMPILE_OUTPUT" | |
| FAILED_EXAMPLES="$FAILED_EXAMPLES $category/$example_name" | |
| fi | |
| fi | |
| fi | |
| done | |
| echo "=== Java Examples Test Summary (${{ matrix.platform }}) ===" | |
| echo "Total representative examples tested: $TOTAL_EXAMPLES" | |
| echo "Passed: $PASSED_EXAMPLES" | |
| echo "Failed: $((TOTAL_EXAMPLES - PASSED_EXAMPLES))" | |
| if [ -n "$FAILED_EXAMPLES" ]; then | |
| echo "Failed examples:$FAILED_EXAMPLES" | |
| echo "❌ Some Java examples failed - but continuing (non-blocking)" | |
| echo "test-status=FAILED" >> $GITHUB_OUTPUT | |
| else | |
| echo "✅ All representative Java examples passed!" | |
| echo "test-status=PASSED" >> $GITHUB_OUTPUT | |
| fi | |
| echo "total-examples=$TOTAL_EXAMPLES" >> $GITHUB_OUTPUT | |
| echo "passed-examples=$PASSED_EXAMPLES" >> $GITHUB_OUTPUT | |
| - name: Test Java Examples (Windows) | |
| if: ${{ matrix.platform == 'Windows' }} | |
| id: test-examples-windows | |
| shell: pwsh | |
| run: | | |
| Write-Host "=== Java Examples Maven Integration Test (Windows) ===" | |
| # Test one representative example from each category | |
| Set-Location HDF5Examples/JAVA | |
| # Get absolute path to maven artifacts for this platform | |
| $MAVEN_ARTIFACTS_DIR = Resolve-Path "../../maven-artifacts/${{ matrix.platform }}" | |
| Write-Host "Maven artifacts directory (Windows): $MAVEN_ARTIFACTS_DIR" | |
| # Find HDF5 JAR files (not dependencies like slf4j) | |
| $HDF5_JAR = Get-ChildItem -Path $MAVEN_ARTIFACTS_DIR -Filter "*hdf5*.jar" -Recurse | Select-Object -First 1 | |
| if (-not $HDF5_JAR) { | |
| $HDF5_JAR = Get-ChildItem -Path $MAVEN_ARTIFACTS_DIR -Filter "jarhdf5*.jar" -Recurse | Select-Object -First 1 | |
| } | |
| if (-not $HDF5_JAR) { | |
| Write-Host "❌ No HDF5 JAR files found for testing" | |
| Write-Host "Available JAR files:" | |
| Get-ChildItem -Path $MAVEN_ARTIFACTS_DIR -Filter "*.jar" -Recurse | |
| exit 1 | |
| } | |
| Write-Host "Using HDF5 JAR file: $($HDF5_JAR.FullName)" | |
| # Detect JAR type by checking for FFM-specific classes | |
| # FFM JARs contain org.hdfgroup.javahdf5 package, JNI JARs contain hdf.hdf5lib | |
| $jar_contents = & jar tf $HDF5_JAR.FullName | |
| if ($jar_contents -match "org/hdfgroup/javahdf5/hdf5_h.class") { | |
| $JAR_TYPE = "ffm" | |
| Write-Host "Detected FFM JAR (contains org.hdfgroup.javahdf5 package)" | |
| } else { | |
| $JAR_TYPE = "jni" | |
| Write-Host "Detected JNI JAR (contains hdf.hdf5lib package)" | |
| } | |
| # Determine which examples directory to use based on detected JAR type | |
| # This ensures we test with examples that match the JAR's implementation | |
| if ($JAR_TYPE -eq "jni") { | |
| $EXAMPLES_DIR = "compat" | |
| Write-Host "Using JNI-compatible examples from compat/ directory" | |
| } else { | |
| $EXAMPLES_DIR = "." | |
| Write-Host "Using FFM examples from root directory" | |
| } | |
| # Find dependency JARs - only include slf4j-api and slf4j-simple | |
| $DEP_JARS = @() | |
| $DEP_JARS += Get-ChildItem -Path $MAVEN_ARTIFACTS_DIR -Filter "slf4j-api*.jar" -Recurse | |
| $DEP_JARS += Get-ChildItem -Path $MAVEN_ARTIFACTS_DIR -Filter "slf4j-simple*.jar" -Recurse | |
| if ($DEP_JARS.Count -gt 0) { | |
| Write-Host "Found dependency JARs:" | |
| $DEP_JARS | ForEach-Object { Write-Host " $($_.FullName)" } | |
| } else { | |
| Write-Host "No SLF4J dependency JARs found in artifacts, downloading them directly..." | |
| # Download SLF4J dependencies directly using Maven | |
| $SLF4J_VERSION = "2.0.16" | |
| $TEMP_POM = "$MAVEN_ARTIFACTS_DIR\temp-pom.xml" | |
| # Create a minimal POM to download dependencies using PowerShell strings | |
| Write-Host "Creating temporary POM at: $TEMP_POM" | |
| '<?xml version="1.0" encoding="UTF-8"?>' | Out-File $TEMP_POM -Encoding UTF8 | |
| '<project xmlns="http://maven.apache.org/POM/4.0.0"' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <modelVersion>4.0.0</modelVersion>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <groupId>temp</groupId>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <artifactId>temp</artifactId>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <version>1.0</version>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <dependencies>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <dependency>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <groupId>org.slf4j</groupId>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <artifactId>slf4j-api</artifactId>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <version>2.0.16</version>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' </dependency>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <dependency>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <groupId>org.slf4j</groupId>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <artifactId>slf4j-simple</artifactId>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' <version>2.0.16</version>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' </dependency>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| ' </dependencies>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| '</project>' | Out-File $TEMP_POM -Append -Encoding UTF8 | |
| # Download dependencies | |
| try { | |
| Write-Host "Running Maven command: mvn -f $TEMP_POM dependency:copy-dependencies -DoutputDirectory=$MAVEN_ARTIFACTS_DIR -DincludeScope=runtime -q" | |
| $mvn_result = & mvn -f $TEMP_POM dependency:copy-dependencies "-DoutputDirectory=$MAVEN_ARTIFACTS_DIR" -DincludeScope=runtime -q | |
| $mvn_exit_code = $LASTEXITCODE | |
| # Clean up temporary POM | |
| Remove-Item $TEMP_POM -Force -ErrorAction SilentlyContinue | |
| if ($mvn_exit_code -eq 0) { | |
| # Re-scan for dependency JARs | |
| $DEP_JARS = @() | |
| $DEP_JARS += Get-ChildItem -Path $MAVEN_ARTIFACTS_DIR -Filter "slf4j-api*.jar" -Recurse | |
| $DEP_JARS += Get-ChildItem -Path $MAVEN_ARTIFACTS_DIR -Filter "slf4j-simple*.jar" -Recurse | |
| if ($DEP_JARS.Count -gt 0) { | |
| Write-Host "Successfully downloaded SLF4J dependencies:" | |
| $DEP_JARS | ForEach-Object { Write-Host " $($_.FullName)" } | |
| } else { | |
| Write-Host "Maven succeeded but no SLF4J JARs found" | |
| } | |
| } else { | |
| Write-Host "Maven command failed with exit code: $mvn_exit_code" | |
| Write-Host "Maven output: $mvn_result" | |
| } | |
| } catch { | |
| Write-Host "Error downloading dependencies: $($_.Exception.Message)" | |
| } | |
| } | |
| $FAILED_EXAMPLES = @() | |
| $TOTAL_EXAMPLES = 0 | |
| $PASSED_EXAMPLES = 0 | |
| # Save current directory (HDF5Examples/JAVA) | |
| $JAVA_DIR = Get-Location | |
| # Test representative examples from each category | |
| foreach ($category in @("H5D", "H5T", "H5G", "TUTR")) { | |
| $category_path = Join-Path $EXAMPLES_DIR $category | |
| $full_category_path = Join-Path $JAVA_DIR $category_path | |
| if (Test-Path $full_category_path) { | |
| Set-Location $full_category_path | |
| # Find first .java file in category | |
| $EXAMPLE_FILE = Get-ChildItem -Filter "*.java" | Select-Object -First 1 | |
| if ($EXAMPLE_FILE) { | |
| $TOTAL_EXAMPLES++ | |
| $example_name = $EXAMPLE_FILE.BaseName | |
| Write-Host "--- Testing $category/$example_name ---" | |
| # Build classpath with HDF5 JAR and dependencies | |
| $CLASSPATH = $HDF5_JAR.FullName | |
| foreach ($dep_jar in $DEP_JARS) { | |
| $CLASSPATH += ";$($dep_jar.FullName)" | |
| } | |
| Write-Host "Using classpath: $CLASSPATH" | |
| # Set compilation flags based on detected JAR type | |
| # This ensures we use correct flags for the actual JAR implementation | |
| if ($JAR_TYPE -eq "ffm") { | |
| # FFM requires preview features (Java 25) | |
| $JAVAC_FLAGS = @("--enable-preview", "--release", "25") | |
| $JAVA_FLAGS = @("--enable-preview") | |
| } else { | |
| # JNI uses standard Java | |
| $JAVAC_FLAGS = @() | |
| $JAVA_FLAGS = @() | |
| } | |
| # Test compilation | |
| $javac_args = $JAVAC_FLAGS + @("-cp", $CLASSPATH, $EXAMPLE_FILE.Name) | |
| $compileResult = & javac $javac_args 2>&1 | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Host "✓ Compilation successful for $category/$example_name" | |
| # Test execution (with PowerShell timeout) | |
| $output_file = "../../../$example_name.out" | |
| # Use PowerShell's Start-Process with timeout for Windows | |
| try { | |
| # Create temporary files for stdout and stderr | |
| $stdout_file = "$output_file.stdout" | |
| $stderr_file = "$output_file.stderr" | |
| # Build java arguments with optional FFM flags | |
| $java_args = $JAVA_FLAGS + @("-cp", ".;$CLASSPATH", $example_name) | |
| $process = Start-Process -FilePath "java" -ArgumentList $java_args -RedirectStandardOutput $stdout_file -RedirectStandardError $stderr_file -NoNewWindow -PassThru | |
| # Wait for the process with timeout | |
| if ($process.WaitForExit(10000)) { # 10 seconds in milliseconds | |
| $execResult = $process.ExitCode | |
| # Combine stdout and stderr into single output file | |
| $stdout_content = if (Test-Path $stdout_file) { | |
| try { Get-Content $stdout_file -Raw -ErrorAction SilentlyContinue } catch { "" } | |
| } else { "" } | |
| $stderr_content = if (Test-Path $stderr_file) { | |
| try { Get-Content $stderr_file -Raw -ErrorAction SilentlyContinue } catch { "" } | |
| } else { "" } | |
| $combined_content = "$stdout_content$stderr_content" | |
| if ([string]::IsNullOrWhiteSpace($combined_content)) { | |
| $combined_content = "No output generated" | |
| } | |
| $combined_content | Out-File $output_file -Encoding UTF8 | |
| # Clean up temporary files | |
| if (Test-Path $stdout_file) { Remove-Item $stdout_file -Force } | |
| if (Test-Path $stderr_file) { Remove-Item $stderr_file -Force } | |
| } else { | |
| # Process timed out | |
| try { $process.Kill() } catch { } | |
| $execResult = 1 | |
| "Process timed out after 10 seconds" | Out-File $output_file -Encoding UTF8 | |
| # Clean up temporary files | |
| if (Test-Path $stdout_file) { Remove-Item $stdout_file -Force } | |
| if (Test-Path $stderr_file) { Remove-Item $stderr_file -Force } | |
| } | |
| } catch { | |
| $execResult = 1 | |
| "Execution error: $($_.Exception.Message)" | Out-File $output_file -Encoding UTF8 | |
| } | |
| if ($execResult -eq 0) { | |
| # Basic output validation | |
| $content = Get-Content $output_file -Raw | |
| if (($content -match "(?i)(dataset|datatype|group|success|created|written|read)") -and | |
| ($content -notmatch "(?i)(error|exception|failed|cannot)")) { | |
| Write-Host "✓ Execution and validation successful for $category/$example_name" | |
| $PASSED_EXAMPLES++ | |
| } else { | |
| Write-Host "✗ Output validation failed for $category/$example_name" | |
| Write-Host "Output:" | |
| Get-Content $output_file | |
| $FAILED_EXAMPLES += "$category/$example_name" | |
| } | |
| } else { | |
| # Check if failure is due to expected native library issue | |
| $content = Get-Content $output_file -Raw | |
| if ($content -match "UnsatisfiedLinkError.*hdf5_java.*java.library.path") { | |
| Write-Host "✓ Expected native library error for Maven-only testing: $category/$example_name" | |
| Write-Host " (This confirms JAR structure is correct)" | |
| $PASSED_EXAMPLES++ | |
| } else { | |
| Write-Host "✗ Unexpected execution failure for $category/$example_name" | |
| Write-Host "Output:" | |
| Get-Content $output_file | |
| $FAILED_EXAMPLES += "$category/$example_name" | |
| } | |
| } | |
| } else { | |
| Write-Host "✗ Compilation failed for $category/$example_name" | |
| Write-Host "Compilation error output:" | |
| Write-Host $compileResult | |
| $FAILED_EXAMPLES += "$category/$example_name" | |
| } | |
| } | |
| } | |
| } | |
| Write-Host "=== Java Examples Test Summary (Windows) ===" | |
| Write-Host "Total representative examples tested: $TOTAL_EXAMPLES" | |
| Write-Host "Passed: $PASSED_EXAMPLES" | |
| Write-Host "Failed: $($TOTAL_EXAMPLES - $PASSED_EXAMPLES)" | |
| if ($FAILED_EXAMPLES.Count -gt 0) { | |
| Write-Host "Failed examples: $($FAILED_EXAMPLES -join ' ')" | |
| Write-Host "❌ Some Java examples failed - but continuing (non-blocking)" | |
| echo "test-status=FAILED" >> $env:GITHUB_OUTPUT | |
| } else { | |
| Write-Host "✅ All representative Java examples passed!" | |
| echo "test-status=PASSED" >> $env:GITHUB_OUTPUT | |
| } | |
| echo "total-examples=$TOTAL_EXAMPLES" >> $env:GITHUB_OUTPUT | |
| echo "passed-examples=$PASSED_EXAMPLES" >> $env:GITHUB_OUTPUT | |
| - name: Upload failure artifacts (Java Examples) | |
| if: steps.test-examples-unix.outputs.test-status == 'FAILED' || steps.test-examples-windows.outputs.test-status == 'FAILED' | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v5 | |
| with: | |
| name: java-examples-staging-failure-${{ matrix.platform }}-${{ github.run_id }} | |
| path: | | |
| /tmp/*.out | |
| *.out | |
| HDF5Examples/JAVA/*/*.class | |
| HDF5Examples/JAVA/*/*.h5 | |
| retention-days: 7 | |
| cleanup: | |
| name: Cleanup Staging Artifacts | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes, determine-matrix, build-maven-artifacts, test-maven-deployment] | |
| if: ${{ always() && needs.detect-changes.outputs.should-test == 'true' }} | |
| steps: | |
| - name: Cleanup summary | |
| run: | | |
| echo "=== Maven Staging Cleanup ===" | |
| echo "Artifacts will be automatically cleaned up after 7 days" | |
| echo "Build artifacts are stored in GitHub Actions artifacts" | |
| echo "No persistent staging repository cleanup needed" |