diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a758e275..4eb01c37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,37 +12,64 @@ jobs: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} env: - RID: ${{ matrix.name }} + RID: ${{ matrix.rid }} + VARIANT: ${{ matrix.variant }} strategy: matrix: include: - os: windows-2022 name: win-x86 + rid: win-x86 param: -x86 - os: windows-2022 name: win-x64 + rid: win-x64 param: -x64 - os: windows-2022 name: win-arm64 + rid: win-arm64 param: -arm64 - os: ubuntu-24.04 name: linux-x64 + rid: linux-x64 + - os: ubuntu-24.04 + name: linux-x64-openssl1.1 + rid: linux-x64 + variant: openssl1.1 - os: ubuntu-24.04 name: linux-arm + rid: linux-arm + - os: ubuntu-24.04 + name: linux-arm-openssl1.1 + rid: linux-arm + variant: openssl1.1 - os: ubuntu-24.04 name: linux-arm64 + rid: linux-arm64 + - os: ubuntu-24.04 + name: linux-arm64-openssl1.1 + rid: linux-arm64 + variant: openssl1.1 + # debian:bullseye-slim has no ppc64le manifest, so the openssl1.1 + # variant is omitted for this RID. - os: ubuntu-24.04 name: linux-ppc64le + rid: linux-ppc64le - os: ubuntu-24.04 name: linux-musl-x64 + rid: linux-musl-x64 - os: ubuntu-24.04 name: linux-musl-arm + rid: linux-musl-arm - os: ubuntu-24.04 name: linux-musl-arm64 + rid: linux-musl-arm64 - os: macos-26-intel name: osx-x64 + rid: osx-x64 - os: macos-26 name: osx-arm64 + rid: osx-arm64 fail-fast: false steps: - name: Checkout @@ -56,7 +83,7 @@ jobs: if: runner.os == 'macOS' run: ./build.libgit2.sh - name: Setup QEMU - if: runner.os == 'Linux' && (matrix.name == 'linux-arm' || matrix.name == 'linux-arm64' || matrix.name == 'linux-ppc64le' || matrix.name == 'linux-musl-arm' || matrix.name == 'linux-musl-arm64') + if: runner.os == 'Linux' && (matrix.rid == 'linux-arm' || matrix.rid == 'linux-arm64' || matrix.rid == 'linux-ppc64le' || matrix.rid == 'linux-musl-arm' || matrix.rid == 'linux-musl-arm64') uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx if: runner.os == 'Linux' @@ -68,7 +95,7 @@ jobs: uses: actions/upload-artifact@v7.0.0 with: name: ${{ matrix.name }} - path: nuget.package/runtimes/${{ matrix.name }} + path: nuget.package/runtimes/${{ matrix.rid }} package: name: Create package needs: build @@ -103,6 +130,17 @@ jobs: uses: actions/download-artifact@v8.0.1 with: path: nuget.package/runtimes/ + - name: Merge variant artifacts into RID directories + shell: bash + run: | + cd nuget.package/runtimes + for d in *-openssl1.1; do + [[ -d "$d" ]] || continue + rid="${d%-openssl1.1}" + mkdir -p "$rid/native" + mv "$d/native/"* "$rid/native/" + rm -rf "$d" + done - name: Create package run: dotnet pack nuget.package ${{ steps.version.outputs.override && format('/p:MinVerVersionOverride={0}', steps.version.outputs.override) || '' }} - name: Upload NuGet package diff --git a/Dockerfile.linux-static-libssh2 b/Dockerfile.linux-static-libssh2 new file mode 100644 index 00000000..47e554fe --- /dev/null +++ b/Dockerfile.linux-static-libssh2 @@ -0,0 +1,34 @@ +ARG BASE_IMAGE=debian:bookworm-slim +FROM ${BASE_IMAGE} + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + pkg-config \ + ca-certificates \ + curl \ + git \ + libssl-dev \ + && rm -rf /var/lib/apt/lists/* + +# Build libssh2 from source as a PIC static archive. apt's libssh2.a is not +# compiled with -fPIC, which prevents linking into libgit2.so. Static linking +# lets us ship one libgit2--.so per OpenSSL ABI without +# colliding libssh2.so.1 SONAMEs across variants. +ARG LIBSSH2_VERSION=1.11.1 +RUN curl -fsSL https://github.com/libssh2/libssh2/releases/download/libssh2-${LIBSSH2_VERSION}/libssh2-${LIBSSH2_VERSION}.tar.gz -o libssh2.tar.gz \ + && tar xf libssh2.tar.gz \ + && cmake -S libssh2-${LIBSSH2_VERSION} -B libssh2-${LIBSSH2_VERSION}/build \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DCRYPTO_BACKEND=OpenSSL \ + -DBUILD_TESTING=OFF \ + -DBUILD_EXAMPLES=OFF \ + && cmake --build libssh2-${LIBSSH2_VERSION}/build --target install \ + && rm -rf libssh2-${LIBSSH2_VERSION} libssh2.tar.gz + +WORKDIR /nativebinaries +COPY . /nativebinaries/ + +CMD ["/bin/bash", "-c", "./build.libgit2.sh"] diff --git a/build.libgit2.sh b/build.libgit2.sh index d8edaaf9..9b5e3f3b 100755 --- a/build.libgit2.sh +++ b/build.libgit2.sh @@ -11,6 +11,14 @@ OSXARCHITECTURE=$ARCH EXTRA_CMAKE_FLAGS="" +# When OPENSSL_VARIANT is set, append it to the libgit2 filename so multiple +# variants can ship side by side and be selected at runtime +# This is only needed to support multiple versions of OpenSSL on Linux +LIBGIT2_FILENAME="git2-$SHORTSHA" +if [[ -n "$OPENSSL_VARIANT" ]]; then + LIBGIT2_FILENAME="$LIBGIT2_FILENAME-$OPENSSL_VARIANT" +fi + if [[ $OS == "Darwin" ]]; then USEHTTPS="ON" if [[ $RID == "osx-arm64" ]]; then @@ -32,7 +40,7 @@ export _BINPATH=`pwd` cmake -DCMAKE_BUILD_TYPE:STRING=Release \ -DBUILD_TESTS:BOOL=OFF \ -DUSE_SSH=ON \ - -DLIBGIT2_FILENAME=git2-$SHORTSHA \ + -DLIBGIT2_FILENAME=$LIBGIT2_FILENAME \ -DCMAKE_OSX_ARCHITECTURES=$OSXARCHITECTURE \ -DUSE_HTTPS=$USEHTTPS \ -DUSE_BUNDLED_ZLIB=ON \ @@ -56,10 +64,7 @@ fi rm -rf $PACKAGEPATH/$RID mkdir -p $PACKAGEPATH/$RID/native -cp libgit2/build/libgit2-$SHORTSHA.$LIBEXT $PACKAGEPATH/$RID/native - -# Bundle libssh2 shared library alongside libgit2 -LIBGIT2_PATH="$PACKAGEPATH/$RID/native/libgit2-$SHORTSHA.$LIBEXT" +cp libgit2/build/lib$LIBGIT2_FILENAME.$LIBEXT $PACKAGEPATH/$RID/native if [[ $OS == "Darwin" ]]; then # We don't run Octopus Server on Mac, so we can avoid the restriction of relying on the system crypto libraries @@ -107,8 +112,11 @@ if [[ $OS == "Darwin" ]]; then for DYLIB in "$NATIVE_DIR"/*.dylib; do codesign --force --sign - "$DYLIB" done +elif [[ -n "$OPENSSL_VARIANT" ]]; then + echo "$OPENSSL_VARIANT: libssh2 statically linked into libgit2" else - # Linux: find libssh2 via ldd + # Linux: bundle the dynamic libssh2 alongside libgit2. + LIBGIT2_PATH="$PACKAGEPATH/$RID/native/lib$LIBGIT2_FILENAME.$LIBEXT" LIBSSH2_PATH=$(ldd "$LIBGIT2_PATH" | grep libssh2 | awk '{print $3}') if [[ -z "$LIBSSH2_PATH" ]]; then echo "ERROR: libgit2 does not appear to link against libssh2" diff --git a/dockerbuild.sh b/dockerbuild.sh index 9240aef9..d4d783ff 100755 --- a/dockerbuild.sh +++ b/dockerbuild.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -echo "building for $RID" +echo "building for $RID variant=${VARIANT:-default}" # Map RID to Docker platform for native builds (no cross-compilation). if [[ $RID =~ arm64 ]]; then @@ -14,16 +14,20 @@ else platform="linux/amd64" fi +# Choose the Dockerfile (and OpenSSL 1.1 base image) based on RID and variant. +build_args=() if [[ $RID == linux-musl* ]]; then dockerfile="Dockerfile.linux-musl" +elif [[ "$VARIANT" == "openssl1.1" ]]; then + # Built on bullseye against OpenSSL 1.1, libssh2 statically linked. + dockerfile="Dockerfile.linux-static-libssh2" + build_args+=(--build-arg "BASE_IMAGE=debian:bullseye-slim") else + # Default: built on bookworm against OpenSSL 3, libssh2 bundled as a separate .so. dockerfile="Dockerfile.linux" fi -docker buildx build --platform "$platform" --load -t $RID -f $dockerfile . - -docker run --platform "$platform" -t -e RID=$RID --name=$RID $RID - -docker cp $RID:/nativebinaries/nuget.package/runtimes nuget.package - -docker rm $RID +docker buildx build --platform "$platform" --load -t "$RID" -f "$dockerfile" "${build_args[@]}" . +docker run --platform "$platform" -t -e RID=$RID -e OPENSSL_VARIANT="$VARIANT" --name="$RID" "$RID" +docker cp "$RID":/nativebinaries/nuget.package/runtimes nuget.package +docker rm "$RID"