From 93360b7bb6a50622fe2ad91aa246a47b8591ace2 Mon Sep 17 00:00:00 2001 From: bailey <bailey.pearson@mongodb.com> Date: Thu, 6 Feb 2025 13:59:54 -0700 Subject: [PATCH 1/4] alll changes --- .github/docker/Dockerfile.musl | 18 ++++++++++ .github/scripts/utils.mjs | 63 ++++++++++++++++++++++++---------- .github/workflows/build.yml | 33 +++++++++++++++++- .github/workflows/test.yml | 41 +++++++++++++++++++++- etc/docker.sh | 46 +++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 20 deletions(-) create mode 100644 .github/docker/Dockerfile.musl create mode 100644 etc/docker.sh diff --git a/.github/docker/Dockerfile.musl b/.github/docker/Dockerfile.musl new file mode 100644 index 0000000..11a894d --- /dev/null +++ b/.github/docker/Dockerfile.musl @@ -0,0 +1,18 @@ +ARG PLATFORM=arm64 +ARG NODE_VERSION=16.20.1 + +FROM ${PLATFORM}/node:${NODE_VERSION}-alpine AS build + +WORKDIR /zstd +COPY . . + +RUN apk --no-cache add make g++ libc-dev curl bash python3 py3-pip vim cmake git +RUN npm run install:libmongocrypt +RUN npm run prebuild + +ARG RUN_TEST +RUN if [ -n "$RUN_TEST" ]; then npm test ; else echo "skipping tests" ; fi + +FROM scratch + +COPY --from=build /zstd/prebuilds/ / diff --git a/.github/scripts/utils.mjs b/.github/scripts/utils.mjs index 1a54385..45d408d 100644 --- a/.github/scripts/utils.mjs +++ b/.github/scripts/utils.mjs @@ -1,10 +1,10 @@ // @ts-check -import { execSync } from "child_process"; import path from "path"; import url from 'node:url'; import { spawn } from "node:child_process"; import { once } from "node:events"; +import { execSync } from "child_process"; const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); @@ -55,22 +55,25 @@ export function buildLibmongocryptDownloadUrl(ref, platform) { } export function getLibmongocryptPrebuildName() { - const platformMatrix = { - ['darwin-arm64']: 'macos', - ['darwin-x64']: 'macos', - ['linux-ppc64']: 'rhel-71-ppc64el', - ['linux-s390x']: 'rhel72-zseries-test', - ['linux-arm64']: 'ubuntu1804-arm64', - ['linux-x64']: 'rhel-70-64-bit', - ['win32-x64']: 'windows-test' - }; - - const detectedPlatform = `${process.platform}-${process.arch}`; - const prebuild = platformMatrix[detectedPlatform]; - - if (prebuild == null) throw new Error(`Unsupported: ${detectedPlatform}`); - - return prebuild; + const prebuildIdentifierFactory = { + 'darwin': () => 'macos', + 'win32': () => 'windows-test', + 'linux': () => { + const key = `${getLibc()}-${process.arch}`; + return { + ['musl-x64']: 'alpine-amd64-earthly', + ['musl-arm64']: 'alpine-arm64-earthly', + ['glibc-ppc64']: 'rhel-71-ppc64el', + ['glibc-s390x']: 'rhel72-zseries-test', + ['glibc-arm64']: 'ubuntu1804-arm64', + ['glibc-x64']: 'rhel-70-64-bit', + }[key] + } + }[process.platform] ?? (() => { + throw new Error(`Unsupported platform`); + }); + + return prebuildIdentifierFactory(); } /** `xtrace` style command runner, uses spawn so that stdio is inherited */ @@ -86,4 +89,28 @@ export async function run(command, args = [], options = {}) { await once(proc, 'exit'); if (proc.exitCode != 0) throw new Error(`CRASH(${proc.exitCode}): ${commandDetails}`); -} \ No newline at end of file +} + +/** + * @returns the libc (`musl` or `glibc`), if the platform is linux, otherwise null. + */ +function getLibc() { + if (process.platform !== 'linux') return null; + + /** + * executes `ldd --version`. on Alpine linux, `ldd` and `ldd --version` return exit code 1 and print the version + * info to stderr, but on other platforms, `ldd --version` prints to stdout and returns exit code 0. + * + * So, this script works on both by return stderr if the command returns a non-zero exit code, otherwise stdout. + */ + function lddVersion() { + try { + return execSync('ldd --version', { encoding: 'utf-8' }); + } catch (error) { + return error.stderr; + } + } + + console.error({ ldd: lddVersion() }); + return lddVersion().includes('musl') ? 'musl' : 'glibc'; +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2e37bd..36d67ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: retention-days: 1 compression-level: 0 - container_builds: + container_builds_glibc: outputs: artifact_id: ${{ steps.upload.outputs.artifact-id }} runs-on: ubuntu-latest @@ -69,3 +69,34 @@ jobs: if-no-files-found: 'error' retention-days: 1 compression-level: 0 + + container_tests_musl: + runs-on: ubuntu-latest + strategy: + matrix: + linux_arch: [amd64, arm64] + fail-fast: false + steps: + - uses: actions/checkout@v4 + + - name: Get Full Node.js Version + id: get_nodejs_version + shell: bash + run: | + echo "version=$(node --print 'process.version.slice(1)')" >> "$GITHUB_OUTPUT" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Run Buildx + run: | + docker buildx create --name builder --bootstrap --use + docker --debug buildx build --progress=plain --no-cache \ + --platform linux/${{ matrix.linux_arch }} \ + --build-arg="PLATFORM=${{ matrix.linux_arch == 'arm64' && 'arm64v8' || matrix.linux_arch }}" \ + --output type=local,dest=./prebuilds,platform-split=false \ + -f ./.github/docker/Dockerfile.musl \ + . \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3c74b60..7b73c26 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,7 @@ jobs: shell: bash run: npm run test - container_tests: + container_tests_glibc: runs-on: ubuntu-latest strategy: matrix: @@ -71,3 +71,42 @@ jobs: --output type=local,dest=./prebuilds,platform-split=false \ -f ./.github/docker/Dockerfile.glibc \ . + + + container_tests_musl: + runs-on: ubuntu-latest + strategy: + matrix: + linux_arch: [amd64, arm64] + node: [16.20.1, 18.x, 20.x, 22.x] + fail-fast: false + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + + - name: Get Full Node.js Version + id: get_nodejs_version + shell: bash + run: | + echo "version=$(node --print 'process.version.slice(1)')" >> "$GITHUB_OUTPUT" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Run Buildx + run: | + docker buildx create --name builder --bootstrap --use + docker --debug buildx build --progress=plain --no-cache \ + --platform linux/${{ matrix.linux_arch }} \ + --build-arg="PLATFORM=${{ matrix.linux_arch == 'arm64' && 'arm64v8' || matrix.linux_arch }}" \ + --build-arg="NODE_VERSION=${{ steps.get_nodejs_version.outputs.version }}" \ + --build-arg="RUN_TEST=true" \ + --output type=local,dest=./prebuilds,platform-split=false \ + -f ./.github/docker/Dockerfile.musl \ + . \ No newline at end of file diff --git a/etc/docker.sh b/etc/docker.sh new file mode 100644 index 0000000..880ee72 --- /dev/null +++ b/etc/docker.sh @@ -0,0 +1,46 @@ +#! /bin/bash + +# script to aid in local testing of linux platforms +# requires a running docker instance + +# s390x, arm64, amd64 for ubuntu +# amd64 or arm64v8 for alpine +LINUX_ARCH=amd64 + +# 16.20.1+, default 16.20.1 +NODE_VERSION=20.0.0 + +SCRIPT_DIR=$(dirname ${BASH_SOURCE:-$0}) +PROJECT_DIR=$SCRIPT_DIR/.. + +build_and_test_musl() { + docker buildx create --name builder --bootstrap --use + + docker --debug buildx build --load --progress=plain --no-cache \ + --platform linux/$LINUX_ARCH --output=type=docker \ + --build-arg="PLATFORM=$LINUX_ARCH" \ + --build-arg="NODE_VERSION=$NODE_VERSION" \ + --build-arg="RUN_TEST=true" \ + -f ./.github/docker/Dockerfile.musl -t musl-zstd-base \ + . +} + +build_and_test_glibc() { + docker buildx create --name builder --bootstrap --use + + UBUNTU_VERSION=$(node --print 'Number(process.argv[1].split(`.`).at(0)) > 16 ? `noble` : `bionic`' $NODE_VERSION) + NODE_ARCH=$(node -p 'process.argv[1] === `amd64` && `x64` || process.argv[1]' $LINUX_ARCH) + echo $UBUNTU_VERSION + docker buildx build --progress=plain --no-cache \ + --platform linux/$LINUX_ARCH \ + --build-arg="NODE_ARCH=$NODE_ARCH" \ + --build-arg="NODE_VERSION=$NODE_VERSION" \ + --build-arg="UBUNTU_VERSION=$UBUNTU_VERSION" \ + --build-arg="RUN_TEST=true" \ + --output type=local,dest=./prebuilds,platform-split=false \ + -f ./.github/docker/Dockerfile.glibc \ + $PROJECT_DIR +} + +build_and_test_musl +# build_and_test_glibc From 29a230ee47bd22a867d03eaac9604717848ec5a9 Mon Sep 17 00:00:00 2001 From: bailey <bailey.pearson@mongodb.com> Date: Thu, 6 Feb 2025 16:35:31 -0700 Subject: [PATCH 2/4] last changes --- .github/docker/Dockerfile.musl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/docker/Dockerfile.musl b/.github/docker/Dockerfile.musl index 11a894d..dda49ea 100644 --- a/.github/docker/Dockerfile.musl +++ b/.github/docker/Dockerfile.musl @@ -3,7 +3,7 @@ ARG NODE_VERSION=16.20.1 FROM ${PLATFORM}/node:${NODE_VERSION}-alpine AS build -WORKDIR /zstd +WORKDIR /mongodb-client-encryption COPY . . RUN apk --no-cache add make g++ libc-dev curl bash python3 py3-pip vim cmake git @@ -15,4 +15,4 @@ RUN if [ -n "$RUN_TEST" ]; then npm test ; else echo "skipping tests" ; fi FROM scratch -COPY --from=build /zstd/prebuilds/ / +COPY --from=build /mongodb-client-encryption/prebuilds/ / From 4fdade3d922a95766f28c8bce2e40cc5512d26a8 Mon Sep 17 00:00:00 2001 From: bailey <bailey.pearson@mongodb.com> Date: Fri, 7 Feb 2025 10:39:04 -0700 Subject: [PATCH 3/4] comments --- .github/workflows/build.yml | 13 +++++++++++-- README.md | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 36d67ca..07c2461 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: name: Upload prebuild uses: actions/upload-artifact@v4 with: - name: build-linux-${{ matrix.linux_arch }} + name: build-linux-glibc-${{ matrix.linux_arch }} path: prebuilds/ if-no-files-found: 'error' retention-days: 1 @@ -99,4 +99,13 @@ jobs: --build-arg="PLATFORM=${{ matrix.linux_arch == 'arm64' && 'arm64v8' || matrix.linux_arch }}" \ --output type=local,dest=./prebuilds,platform-split=false \ -f ./.github/docker/Dockerfile.musl \ - . \ No newline at end of file + . + - id: upload + name: Upload prebuild + uses: actions/upload-artifact@v4 + with: + name: build-linux-musl-${{ matrix.linux_arch }} + path: prebuilds/ + if-no-files-found: "error" + retention-days: 1 + compression-level: 0 \ No newline at end of file diff --git a/README.md b/README.md index 66c7521..1b04ee8 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,9 @@ Below are the platforms that are available as prebuilds on each github release. - s390x - arm64 - x64 +- Linux MUSL 1.1.20 + - arm64 + - x64 - MacOS universal binary - x64 - arm64 From c4b4131a58297737bb573f0788d9f84877a10566 Mon Sep 17 00:00:00 2001 From: bailey <bailey.pearson@mongodb.com> Date: Fri, 7 Feb 2025 12:41:44 -0700 Subject: [PATCH 4/4] remove vim --- .github/docker/Dockerfile.musl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/docker/Dockerfile.musl b/.github/docker/Dockerfile.musl index dda49ea..6bee364 100644 --- a/.github/docker/Dockerfile.musl +++ b/.github/docker/Dockerfile.musl @@ -6,7 +6,7 @@ FROM ${PLATFORM}/node:${NODE_VERSION}-alpine AS build WORKDIR /mongodb-client-encryption COPY . . -RUN apk --no-cache add make g++ libc-dev curl bash python3 py3-pip vim cmake git +RUN apk --no-cache add make g++ libc-dev curl bash python3 py3-pip cmake git RUN npm run install:libmongocrypt RUN npm run prebuild