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