diff --git a/.github/workflows/build-native-image.yml b/.github/workflows/build-native-image.yml new file mode 100644 index 000000000..c5c066f28 --- /dev/null +++ b/.github/workflows/build-native-image.yml @@ -0,0 +1,101 @@ +name: Build native image +defaults: + run: + shell: bash -euo pipefail -O nullglob {0} +on: + workflow_dispatch: + inputs: + ref: + type: string + description: "Git ref from which to release" + required: true + default: "master" + workflow_call: + inputs: + ref: + type: string + description: "Git ref from which to release" + required: true + default: "master" +env: + INPUT_REF: ${{ github.event.inputs.ref }} + +jobs: + build_native_images: + name: Build native test server + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-latest + os_family: linux + arch: amd64 + musl: true + - runner: ubuntu-latest + os_family: linux + arch: amd64 + musl: false + - runner: macos-13 + os_family: macOS + arch: amd64 + - runner: macos-latest + os_family: macOS + arch: arm64 + - runner: ubuntu-24.04-arm + os_family: linux + arch: arm64 + - runner: windows-latest + os_family: windows + arch: amd64 + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + ref: ${{ env.INPUT_REF }} + + - name: Set up Java + if: matrix.os_family != 'Linux' + uses: actions/setup-java@v4 + with: + java-version: | + 21 + 23 + distribution: "graalvm" + + - name: Set up Gradle + if: matrix.os_family != 'Linux' + uses: gradle/actions/setup-gradle@v4 + + - name: Build native test server (non-Docker) + if: matrix.os_family != 'Linux' + run: | + ./gradlew -PnativeBuild :temporal-test-server:nativeCompile + + - name: Build native test server (Docker non-musl) + if: matrix.os_family == 'Linux' && matrix.musl == false + run: | + docker run \ + --rm -w /github/workspace -v "$(pwd):/github/workspace" \ + $(docker build -q ./docker/native-image) \ + sh -c "./gradlew -PnativeBuild :temporal-test-server:nativeCompile" + + - name: Build native test server (Docker musl) + if: matrix.os_family == 'Linux' && matrix.musl == true + run: | + docker run \ + --rm -w /github/workspace -v "$(pwd):/github/workspace" \ + $(docker build -q ./docker/native-image-musl) \ + sh -c "./gradlew -PnativeBuild -PnativeBuildMusl :temporal-test-server:nativeCompile" + # path ends in a wildcard because on windows the file ends in '.exe' + - name: Upload executable to workflow + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.musl && format('{0}_{1}_musl', matrix.os_family, matrix.arch) || format('{0}_{1}', matrix.os_family, matrix.arch)}} + path: | + temporal-test-server/build/native/nativeCompile/temporal-test-server* + if-no-files-found: error + retention-days: 1 + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54f03c912..5ed1f94fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Java uses: actions/setup-java@v4 with: - java-version: "21" + java-version: "23" distribution: "temurin" - name: Set up Gradle @@ -61,7 +61,7 @@ jobs: uses: actions/setup-java@v4 with: java-version: | - 21 + 23 11 distribution: "temurin" @@ -188,3 +188,9 @@ jobs: - name: Run copyright and code format checks run: ./gradlew --no-daemon checkLicenseMain checkLicenses spotlessCheck + + build_native_images: + name: Build native test server + uses: ./.github/workflows/build-native-image.yml + with: + ref: ${{ github.event.pull_request.head.sha }} \ No newline at end of file diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index e077e72f9..fba735306 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -124,70 +124,9 @@ jobs: name: Build native test server needs: create_draft_release if: github.event.inputs.do_build_native_images == 'true' - strategy: - fail-fast: false - matrix: - include: - - runner: ubuntu-latest - os_family: linux - arch: amd64 - - runner: macos-13 - os_family: macOS - arch: amd64 - - runner: macos-latest - os_family: macOS - arch: arm64 - - runner: ubuntu-24.04-arm - os_family: linux - arch: arm64 - - runner: windows-2019 - os_family: windows - arch: amd64 - runs-on: ${{ matrix.runner }} - steps: - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{ env.INPUT_REF }} - - # See comment on temporary tag above. tldr: this is a local tag; never - # gets pushed - - name: Temporary tag - run: git tag "$INPUT_TAG" - - - name: Set up Java - if: matrix.os_family != 'Linux' - uses: actions/setup-java@v4 - with: - java-version: "21" - distribution: "graalvm" - - - name: Set up Gradle - if: matrix.os_family != 'Linux' - uses: gradle/actions/setup-gradle@v4 - - - name: Build native test server (non-Docker) - if: matrix.os_family != 'Linux' - run: | - ./gradlew -PnativeBuild :temporal-test-server:nativeBuild - - - name: Build native test server (Docker) - if: matrix.os_family == 'Linux' - run: | - docker run \ - --rm -w /github/workspace -v "$(pwd):/github/workspace" \ - $(docker build -q ./docker/native-image) \ - sh -c "./gradlew -PnativeBuild :temporal-test-server:nativeBuild" - # path ends in a wildcard because on windows the file ends in '.exe' - - name: Upload executable to workflow - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.os_family }}_${{ matrix.arch }} - path: | - temporal-test-server/build/native/nativeCompile/temporal-test-server* - if-no-files-found: error - retention-days: 1 + uses: ./.github/workflows/build-native-image.yml + with: + ref: ${{ github.event.inputs.ref }} attach_to_release: name: Attach native executables to release diff --git a/docker/native-image-musl/dockerfile b/docker/native-image-musl/dockerfile new file mode 100644 index 000000000..47c9d7dfa --- /dev/null +++ b/docker/native-image-musl/dockerfile @@ -0,0 +1,18 @@ +# Use an old version of Ubuntu to build the test server to maintain compatibility with +# older versions of glibc, specifically glib 2.17. +FROM ubuntu:24.04 +ENV JAVA_HOME=/usr/lib64/graalvm/graalvm-community-java23 +COPY --from=ghcr.io/graalvm/native-image-community:23 $JAVA_HOME $JAVA_HOME +ENV PATH="${JAVA_HOME}/bin:${PATH}" +RUN apt-get update +RUN apt-get install -y git build-essential curl binutils +COPY install-musl.sh /opt/install-musl.sh +RUN chmod +x /opt/install-musl.sh +WORKDIR /opt +# We need to build musl and zlibc with musl to for a static build +# See https://www.graalvm.org/21.3/reference-manual/native-image/StaticImages/index.html +RUN ./install-musl.sh +ENV MUSL_HOME=/opt/musl-toolchain +ENV PATH="$MUSL_HOME/bin:$PATH" +# Avoid errors like: "fatal: detected dubious ownership in repository" +RUN git config --global --add safe.directory '*' \ No newline at end of file diff --git a/docker/native-image-musl/install-musl.sh b/docker/native-image-musl/install-musl.sh new file mode 100644 index 000000000..9000a1cda --- /dev/null +++ b/docker/native-image-musl/install-musl.sh @@ -0,0 +1,28 @@ +# Specify an installation directory for musl: +export MUSL_HOME=$PWD/musl-toolchain + +# Download musl and zlib sources: +curl -O https://musl.libc.org/releases/musl-1.2.5.tar.gz +curl -O https://zlib.net/fossils/zlib-1.2.13.tar.gz + +# Build musl from source +tar -xzvf musl-1.2.5.tar.gz +cd musl-1.2.5 +./configure --prefix=$MUSL_HOME --static +# The next operation may require privileged access to system resources, so use sudo +make && make install +cd .. + +# Install a symlink for use by native-image +ln -s $MUSL_HOME/bin/musl-gcc $MUSL_HOME/bin/x86_64-linux-musl-gcc + +# Extend the system path and confirm that musl is available by printing its version +export PATH="$MUSL_HOME/bin:$PATH" +x86_64-linux-musl-gcc --version + +# Build zlib with musl from source and install into the MUSL_HOME directory +tar -xzvf zlib-1.2.13.tar.gz +cd zlib-1.2.13 +CC=musl-gcc ./configure --prefix=$MUSL_HOME --static +make && make install +cd .. diff --git a/docker/native-image/dockerfile b/docker/native-image/dockerfile index 0b1a0c71f..931c737bc 100644 --- a/docker/native-image/dockerfile +++ b/docker/native-image/dockerfile @@ -1,10 +1,14 @@ # Use an old version of Ubuntu to build the test server to maintain compatibility with # older versions of glibc, specifically glib 2.17. FROM ubuntu:18.04 -ENV JAVA_HOME=/usr/lib64/graalvm/graalvm-community-java21 -COPY --from=ghcr.io/graalvm/jdk-community:21 $JAVA_HOME $JAVA_HOME +ENV JAVA_HOME=/usr/lib64/graalvm/graalvm-community-java23 +COPY --from=ghcr.io/graalvm/native-image-community:23 $JAVA_HOME $JAVA_HOME ENV PATH="${JAVA_HOME}/bin:${PATH}" +RUN apt-get update && apt-get install -y software-properties-common +RUN add-apt-repository ppa:ubuntu-toolchain-r/test RUN apt-get update -RUN apt-get install -y git build-essential zlib1g-dev +# We need to update gcc and g++ to 10 for Graal to work on ARM64 +RUN apt-get install -y git build-essential zlib1g-dev gcc-10 g++-10 +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 60 --slave /usr/bin/g++ g++ /usr/bin/g++-10 # Avoid errors like: "fatal: detected dubious ownership in repository" RUN git config --global --add safe.directory '*' \ No newline at end of file diff --git a/temporal-sdk/build.gradle b/temporal-sdk/build.gradle index 32bfcd832..31f8b36db 100644 --- a/temporal-sdk/build.gradle +++ b/temporal-sdk/build.gradle @@ -51,7 +51,7 @@ dependencies { tasks.named('compileJava21Java') { javaCompiler = javaToolchains.compilerFor { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(23) } options.release = 21 } diff --git a/temporal-test-server/build.gradle b/temporal-test-server/build.gradle index 5ef589bfc..2551b5ebf 100644 --- a/temporal-test-server/build.gradle +++ b/temporal-test-server/build.gradle @@ -120,9 +120,14 @@ if (project.hasProperty("nativeBuild")) { // If we're on linux, static link everything but libc. Otherwise link // everything dynamically (note the '-' rather than '+' in front of // StaticExecutable) - buildArgs.add(isLinux() ? "-H:+StaticExecutableWithDynamicLibC": "-H:-StaticExecutable") + if (isLinux() && !project.hasProperty("nativeBuildMusl")) { + buildArgs.add("-H:+StaticExecutableWithDynamicLibC") + } else if (isLinux() && project.hasProperty("nativeBuildMusl")) { + buildArgs.add("--static") + buildArgs.add("--libc=musl") + } buildArgs.add("-H:+UnlockExperimentalVMOptions") - buildArgs.add("-O4") + buildArgs.add("-Os") runtimeArgs.add("7233") } diff --git a/temporal-test-server/src/main/resources/META-INF/native-image/io.temporal/temporal-test-server/native-image.properties b/temporal-test-server/src/main/resources/META-INF/native-image/io.temporal/temporal-test-server/native-image.properties index 4e7bb5701..051d44569 100644 --- a/temporal-test-server/src/main/resources/META-INF/native-image/io.temporal/temporal-test-server/native-image.properties +++ b/temporal-test-server/src/main/resources/META-INF/native-image/io.temporal/temporal-test-server/native-image.properties @@ -23,5 +23,7 @@ Args = -H:+UnlockExperimentalVMOptions \ -H:JNIConfigurationResources=${.}/jni-config.json \ -H:ReflectionConfigurationResources=${.}/reflect-config.json \ -H:ResourceConfigurationResources=${.}/resource-config.json \ - -H:SerializationConfigurationResources=${.}/serialization-config.json + -H:SerializationConfigurationResources=${.}/serialization-config.json \ + --initialize-at-build-time=org.slf4j.helpers.SubstituteLoggerFactory \ + --initialize-at-build-time=org.slf4j.helpers.NOPLoggerFactory