Build Spring Native Maven #33
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
| # Build nmvn native images per platform: | |
| # - specialized Spring Boot images (nmvn-spring-<bootVersion>), plugin set from the catalog | |
| # - one generic image (nmvn-generic) baking only Maven's DEFAULT lifecycle plugin versions — | |
| # what a project with no version-pinning parent requests; the versions are resolved from | |
| # the dist's maven-core jar by build-nmvn-generic.sh, not hardcoded | |
| # | |
| # Same commands as local. Scripts write under build/ by default: | |
| # build/catalogs/ — catalogs | |
| # build/work/ — scratch | |
| # build/nmvn-spring-*, build/nmvn-generic — images | |
| # Maven dist stays in apache-maven/target/. | |
| # | |
| # Toolchain: download GraalVM CE 25.2.4, set JAVA_HOME (+ bin on PATH). Finding native-image | |
| # (including Windows native-image.cmd) and Crema checks live in build-nmvn-prebuilt.sh. | |
| # | |
| # Per platform, for Boot 4.1.0 and 4.0.7: | |
| # resolve_boot_catalog.py → build-nmvn-catalog.sh → zip to dist/ | |
| # then the generic image: | |
| # build-nmvn-generic.sh → zip to dist/ | |
| # | |
| name: Build Spring Native Maven | |
| on: | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: {} | |
| env: | |
| # https://github.com/graalvm/graalvm-ce-builds/releases/tag/graal-25.2.4 | |
| GRAAL_TAG: graal-25.2.4 | |
| jobs: | |
| build: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest-16-cores | |
| platform: linux-x86_64 | |
| graal_file: graalvm-community-jdk-25i2-25.0.4_linux-x64_bin.tar.gz | |
| - os: ubuntu-latest-32-cores-arm64 | |
| platform: linux-aarch64 | |
| graal_file: graalvm-community-jdk-25i2-25.0.4_linux-aarch64_bin.tar.gz | |
| - os: macos-arm64-cipool | |
| platform: macos-aarch64 | |
| graal_file: graalvm-community-jdk-25i2-25.0.4_macos-aarch64_bin.tar.gz | |
| - os: windows-latest-16-cores | |
| platform: windows-x86_64 | |
| graal_file: graalvm-community-jdk-25i2-25.0.4_windows-x64_bin.zip | |
| runs-on: ${{ matrix.os }} | |
| defaults: | |
| run: | |
| shell: bash | |
| permissions: | |
| contents: read | |
| env: | |
| BOOT_VERSIONS: "4.1.0 4.0.7" | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Download and install GraalVM 25.2 | |
| run: | | |
| set -euo pipefail | |
| GRAAL_URL="https://github.com/graalvm/graalvm-ce-builds/releases/download/${GRAAL_TAG}/${{ matrix.graal_file }}" | |
| INSTALL_ROOT="${RUNNER_TEMP:-/tmp}/graalvm-25.2" | |
| ARCHIVE="${RUNNER_TEMP:-/tmp}/${{ matrix.graal_file }}" | |
| echo ">>> Downloading ${GRAAL_URL}" | |
| rm -rf "${INSTALL_ROOT}" | |
| mkdir -p "${INSTALL_ROOT}" | |
| curl -fsSL "${GRAAL_URL}" -o "${ARCHIVE}" | |
| case "${{ matrix.graal_file }}" in | |
| *.zip) | |
| if command -v unzip >/dev/null 2>&1; then | |
| unzip -q "${ARCHIVE}" -d "${INSTALL_ROOT}" | |
| else | |
| pwsh -NoProfile -Command "Expand-Archive -Path '${ARCHIVE}' -DestinationPath '${INSTALL_ROOT}' -Force" | |
| fi | |
| ;; | |
| *) | |
| tar -xzf "${ARCHIVE}" -C "${INSTALL_ROOT}" | |
| ;; | |
| esac | |
| # Locate JAVA_HOME (macOS: Contents/Home; Linux/Windows: unpacked root). | |
| JAVA_HOME_CANDIDATE="" | |
| for d in "${INSTALL_ROOT}"/*/Contents/Home "${INSTALL_ROOT}"/*/*/Contents/Home; do | |
| if [ -f "${d}/bin/native-image" ] || [ -f "${d}/bin/native-image.cmd" ] || [ -f "${d}/bin/native-image.exe" ]; then | |
| JAVA_HOME_CANDIDATE="$d" | |
| break | |
| fi | |
| done | |
| if [ -z "${JAVA_HOME_CANDIDATE}" ]; then | |
| for d in "${INSTALL_ROOT}"/*; do | |
| if [ -d "$d" ] && { [ -f "${d}/bin/native-image" ] || [ -f "${d}/bin/native-image.cmd" ] || [ -f "${d}/bin/native-image.exe" ]; }; then | |
| JAVA_HOME_CANDIDATE="$d" | |
| break | |
| fi | |
| done | |
| fi | |
| if [ -z "${JAVA_HOME_CANDIDATE}" ]; then | |
| echo "::error::Could not find native-image under ${INSTALL_ROOT}" | |
| find "${INSTALL_ROOT}" -maxdepth 5 -type f \( -name 'native-image' -o -name 'native-image.cmd' -o -name 'native-image.exe' \) 2>/dev/null | head -20 || true | |
| exit 1 | |
| fi | |
| # Git Bash on Windows: store Unix-style JAVA_HOME for later bash steps. | |
| if command -v cygpath >/dev/null 2>&1; then | |
| JAVA_HOME_CANDIDATE="$(cygpath -u "${JAVA_HOME_CANDIDATE}")" | |
| fi | |
| # Subsequent steps inherit these (GITHUB_ENV / GITHUB_PATH). | |
| echo "JAVA_HOME=${JAVA_HOME_CANDIDATE}" >> "$GITHUB_ENV" | |
| echo "${JAVA_HOME_CANDIDATE}/bin" >> "$GITHUB_PATH" | |
| echo ">>> JAVA_HOME=${JAVA_HOME_CANDIDATE}" | |
| - name: Strip SNAPSHOT from versions | |
| run: LC_ALL=C find . -name 'pom.xml' -exec sed -i'' -e 's/-SNAPSHOT//g' {} + | |
| - name: Build Maven distribution | |
| run: | | |
| chmod +x ./mvnw | |
| ./mvnw clean package -B -DskipTests -Drat.skip=true -Dcheckstyle.skip=true | |
| - name: Resolve catalogs and build Spring specialized + generic images | |
| run: | | |
| set -euo pipefail | |
| chmod +x catalog/resolve_boot_catalog.py build-nmvn-catalog.sh build-nmvn-prebuilt.sh build-nmvn-generic.sh | |
| # Fresh build tree (same layout as local defaults). | |
| rm -rf build dist | |
| mkdir -p dist | |
| package_zip() { | |
| local zip_name="$1" | |
| shift | |
| if command -v zip >/dev/null 2>&1; then | |
| zip -j "${zip_name}" "$@" | |
| else | |
| pwsh -NoProfile -Command \ | |
| "Compress-Archive -Path @($(printf "'%s'," "$@" | sed 's/,$//')) -DestinationPath '${zip_name}' -Force" | |
| fi | |
| } | |
| for BOOT in $BOOT_VERSIONS; do | |
| CATALOG="build/catalogs/nmvn-spring-${BOOT}.json" | |
| echo "==========================================" | |
| echo ">>> Boot ${BOOT}" | |
| echo "==========================================" | |
| ./catalog/resolve_boot_catalog.py \ | |
| --boot-version "${BOOT}" \ | |
| --language java | |
| ./build-nmvn-catalog.sh "${CATALOG}" | |
| BIN="build/nmvn-spring-${BOOT}" | |
| if [ -f "${BIN}.exe" ]; then | |
| BIN="${BIN}.exe" | |
| fi | |
| if [ ! -f "${BIN}" ]; then | |
| echo "::error::Expected binary ${BIN} after build for Boot ${BOOT}" | |
| ls -la build/ || true | |
| exit 1 | |
| fi | |
| PLUGINS="build/nmvn-spring-${BOOT}.plugins" | |
| ZIP="nmvn-spring-${BOOT}-${{ matrix.platform }}-${{ github.sha }}.zip" | |
| package_zip "dist/${ZIP}" "${BIN}" "${PLUGINS}" | |
| echo ">>> Packaged dist/${ZIP}" | |
| done | |
| echo "==========================================" | |
| echo ">>> Generic image (Maven default lifecycle plugins)" | |
| echo "==========================================" | |
| # Plugin versions are resolved from the dist's maven-core jar by the script itself | |
| # (default lifecycle bindings — what a project without a version-pinning parent | |
| # requests), so there is nothing to keep in sync here when the bindings are bumped. | |
| ./build-nmvn-generic.sh | |
| GENERIC_BIN="build/nmvn-generic" | |
| if [ -f "${GENERIC_BIN}.exe" ]; then | |
| GENERIC_BIN="${GENERIC_BIN}.exe" | |
| fi | |
| if [ ! -f "${GENERIC_BIN}" ]; then | |
| echo "::error::Expected binary ${GENERIC_BIN} after generic build" | |
| ls -la build/ || true | |
| exit 1 | |
| fi | |
| ZIP="nmvn-generic-${{ matrix.platform }}-${{ github.sha }}.zip" | |
| package_zip "dist/${ZIP}" "${GENERIC_BIN}" build/nmvn-generic.plugins | |
| echo ">>> Packaged dist/${ZIP}" | |
| ls -la dist/ | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: nmvn-spring-${{ matrix.platform }} | |
| path: dist/*.zip | |
| if-no-files-found: error | |
| - name: Clean workspace outputs | |
| if: always() | |
| run: rm -rf build dist | |
| publish: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event_name == 'workflow_dispatch' }} | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| merge-multiple: true | |
| path: artifacts | |
| - name: List artifacts | |
| run: find artifacts -type f | sort | |
| - name: Publish to R2 | |
| uses: elide-tools/r2-upload-action@main | |
| with: | |
| path: artifacts | |
| target: native-maven | |
| bucket: elide-userdata-public | |
| account-id: ${{ secrets.R2_ACCOUNT_ID }} | |
| access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }} | |
| secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} | |
| - name: Clean downloaded artifacts | |
| if: always() | |
| run: rm -rf artifacts |