Skip to content

Commit f4274d7

Browse files
feat(NODE-6727): Add builds on Alpine linux (#65)
Co-authored-by: Neal Beeken <[email protected]>
1 parent 3787edd commit f4274d7

File tree

6 files changed

+194
-21
lines changed

6 files changed

+194
-21
lines changed

.github/docker/Dockerfile.musl

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
ARG PLATFORM=arm64
2+
ARG NODE_VERSION=16.20.1
3+
4+
FROM ${PLATFORM}/node:${NODE_VERSION}-alpine AS build
5+
6+
WORKDIR /mongodb-client-encryption
7+
COPY . .
8+
9+
RUN apk --no-cache add make g++ libc-dev curl bash python3 py3-pip cmake git
10+
RUN npm run install:libmongocrypt
11+
RUN npm run prebuild
12+
13+
ARG RUN_TEST
14+
RUN if [ -n "$RUN_TEST" ]; then npm test ; else echo "skipping tests" ; fi
15+
16+
FROM scratch
17+
18+
COPY --from=build /mongodb-client-encryption/prebuilds/ /

.github/scripts/utils.mjs

+45-18
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// @ts-check
22

3-
import { execSync } from "child_process";
43
import path from "path";
54
import url from 'node:url';
65
import { spawn } from "node:child_process";
76
import { once } from "node:events";
7+
import { execSync } from "child_process";
88

99
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
1010

@@ -55,22 +55,25 @@ export function buildLibmongocryptDownloadUrl(ref, platform) {
5555
}
5656

5757
export function getLibmongocryptPrebuildName() {
58-
const platformMatrix = {
59-
['darwin-arm64']: 'macos',
60-
['darwin-x64']: 'macos',
61-
['linux-ppc64']: 'rhel-71-ppc64el',
62-
['linux-s390x']: 'rhel72-zseries-test',
63-
['linux-arm64']: 'ubuntu1804-arm64',
64-
['linux-x64']: 'rhel-70-64-bit',
65-
['win32-x64']: 'windows-test'
66-
};
67-
68-
const detectedPlatform = `${process.platform}-${process.arch}`;
69-
const prebuild = platformMatrix[detectedPlatform];
70-
71-
if (prebuild == null) throw new Error(`Unsupported: ${detectedPlatform}`);
72-
73-
return prebuild;
58+
const prebuildIdentifierFactory = {
59+
'darwin': () => 'macos',
60+
'win32': () => 'windows-test',
61+
'linux': () => {
62+
const key = `${getLibc()}-${process.arch}`;
63+
return {
64+
['musl-x64']: 'alpine-amd64-earthly',
65+
['musl-arm64']: 'alpine-arm64-earthly',
66+
['glibc-ppc64']: 'rhel-71-ppc64el',
67+
['glibc-s390x']: 'rhel72-zseries-test',
68+
['glibc-arm64']: 'ubuntu1804-arm64',
69+
['glibc-x64']: 'rhel-70-64-bit',
70+
}[key]
71+
}
72+
}[process.platform] ?? (() => {
73+
throw new Error(`Unsupported platform`);
74+
});
75+
76+
return prebuildIdentifierFactory();
7477
}
7578

7679
/** `xtrace` style command runner, uses spawn so that stdio is inherited */
@@ -86,4 +89,28 @@ export async function run(command, args = [], options = {}) {
8689
await once(proc, 'exit');
8790

8891
if (proc.exitCode != 0) throw new Error(`CRASH(${proc.exitCode}): ${commandDetails}`);
89-
}
92+
}
93+
94+
/**
95+
* @returns the libc (`musl` or `glibc`), if the platform is linux, otherwise null.
96+
*/
97+
function getLibc() {
98+
if (process.platform !== 'linux') return null;
99+
100+
/**
101+
* executes `ldd --version`. on Alpine linux, `ldd` and `ldd --version` return exit code 1 and print the version
102+
* info to stderr, but on other platforms, `ldd --version` prints to stdout and returns exit code 0.
103+
*
104+
* So, this script works on both by return stderr if the command returns a non-zero exit code, otherwise stdout.
105+
*/
106+
function lddVersion() {
107+
try {
108+
return execSync('ldd --version', { encoding: 'utf-8' });
109+
} catch (error) {
110+
return error.stderr;
111+
}
112+
}
113+
114+
console.error({ ldd: lddVersion() });
115+
return lddVersion().includes('musl') ? 'musl' : 'glibc';
116+
}

.github/workflows/build.yml

+42-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
retention-days: 1
3535
compression-level: 0
3636

37-
container_builds:
37+
container_builds_glibc:
3838
outputs:
3939
artifact_id: ${{ steps.upload.outputs.artifact-id }}
4040
runs-on: ubuntu-latest
@@ -64,8 +64,48 @@ jobs:
6464
name: Upload prebuild
6565
uses: actions/upload-artifact@v4
6666
with:
67-
name: build-linux-${{ matrix.linux_arch }}
67+
name: build-linux-glibc-${{ matrix.linux_arch }}
6868
path: prebuilds/
6969
if-no-files-found: 'error'
7070
retention-days: 1
7171
compression-level: 0
72+
73+
container_tests_musl:
74+
runs-on: ubuntu-latest
75+
strategy:
76+
matrix:
77+
linux_arch: [amd64, arm64]
78+
fail-fast: false
79+
steps:
80+
- uses: actions/checkout@v4
81+
82+
- name: Get Full Node.js Version
83+
id: get_nodejs_version
84+
shell: bash
85+
run: |
86+
echo "version=$(node --print 'process.version.slice(1)')" >> "$GITHUB_OUTPUT"
87+
88+
- name: Set up QEMU
89+
uses: docker/setup-qemu-action@v3
90+
91+
- name: Set up Docker Buildx
92+
uses: docker/setup-buildx-action@v3
93+
94+
- name: Run Buildx
95+
run: |
96+
docker buildx create --name builder --bootstrap --use
97+
docker --debug buildx build --progress=plain --no-cache \
98+
--platform linux/${{ matrix.linux_arch }} \
99+
--build-arg="PLATFORM=${{ matrix.linux_arch == 'arm64' && 'arm64v8' || matrix.linux_arch }}" \
100+
--output type=local,dest=./prebuilds,platform-split=false \
101+
-f ./.github/docker/Dockerfile.musl \
102+
.
103+
- id: upload
104+
name: Upload prebuild
105+
uses: actions/upload-artifact@v4
106+
with:
107+
name: build-linux-musl-${{ matrix.linux_arch }}
108+
path: prebuilds/
109+
if-no-files-found: "error"
110+
retention-days: 1
111+
compression-level: 0

.github/workflows/test.yml

+40-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
shell: bash
3333
run: npm run test
3434

35-
container_tests:
35+
container_tests_glibc:
3636
runs-on: ubuntu-latest
3737
strategy:
3838
matrix:
@@ -71,3 +71,42 @@ jobs:
7171
--output type=local,dest=./prebuilds,platform-split=false \
7272
-f ./.github/docker/Dockerfile.glibc \
7373
.
74+
75+
76+
container_tests_musl:
77+
runs-on: ubuntu-latest
78+
strategy:
79+
matrix:
80+
linux_arch: [amd64, arm64]
81+
node: [16.20.1, 18.x, 20.x, 22.x]
82+
fail-fast: false
83+
steps:
84+
- uses: actions/checkout@v4
85+
86+
- uses: actions/setup-node@v4
87+
with:
88+
node-version: ${{ matrix.node }}
89+
90+
- name: Get Full Node.js Version
91+
id: get_nodejs_version
92+
shell: bash
93+
run: |
94+
echo "version=$(node --print 'process.version.slice(1)')" >> "$GITHUB_OUTPUT"
95+
96+
- name: Set up QEMU
97+
uses: docker/setup-qemu-action@v3
98+
99+
- name: Set up Docker Buildx
100+
uses: docker/setup-buildx-action@v3
101+
102+
- name: Run Buildx
103+
run: |
104+
docker buildx create --name builder --bootstrap --use
105+
docker --debug buildx build --progress=plain --no-cache \
106+
--platform linux/${{ matrix.linux_arch }} \
107+
--build-arg="PLATFORM=${{ matrix.linux_arch == 'arm64' && 'arm64v8' || matrix.linux_arch }}" \
108+
--build-arg="NODE_VERSION=${{ steps.get_nodejs_version.outputs.version }}" \
109+
--build-arg="RUN_TEST=true" \
110+
--output type=local,dest=./prebuilds,platform-split=false \
111+
-f ./.github/docker/Dockerfile.musl \
112+
.

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ Below are the platforms that are available as prebuilds on each github release.
9494
- s390x
9595
- arm64
9696
- x64
97+
- Linux MUSL 1.1.20
98+
- arm64
99+
- x64
97100
- MacOS universal binary
98101
- x64
99102
- arm64

etc/docker.sh

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#! /bin/bash
2+
3+
# script to aid in local testing of linux platforms
4+
# requires a running docker instance
5+
6+
# s390x, arm64, amd64 for ubuntu
7+
# amd64 or arm64v8 for alpine
8+
LINUX_ARCH=amd64
9+
10+
# 16.20.1+, default 16.20.1
11+
NODE_VERSION=20.0.0
12+
13+
SCRIPT_DIR=$(dirname ${BASH_SOURCE:-$0})
14+
PROJECT_DIR=$SCRIPT_DIR/..
15+
16+
build_and_test_musl() {
17+
docker buildx create --name builder --bootstrap --use
18+
19+
docker --debug buildx build --load --progress=plain --no-cache \
20+
--platform linux/$LINUX_ARCH --output=type=docker \
21+
--build-arg="PLATFORM=$LINUX_ARCH" \
22+
--build-arg="NODE_VERSION=$NODE_VERSION" \
23+
--build-arg="RUN_TEST=true" \
24+
-f ./.github/docker/Dockerfile.musl -t musl-zstd-base \
25+
.
26+
}
27+
28+
build_and_test_glibc() {
29+
docker buildx create --name builder --bootstrap --use
30+
31+
UBUNTU_VERSION=$(node --print 'Number(process.argv[1].split(`.`).at(0)) > 16 ? `noble` : `bionic`' $NODE_VERSION)
32+
NODE_ARCH=$(node -p 'process.argv[1] === `amd64` && `x64` || process.argv[1]' $LINUX_ARCH)
33+
echo $UBUNTU_VERSION
34+
docker buildx build --progress=plain --no-cache \
35+
--platform linux/$LINUX_ARCH \
36+
--build-arg="NODE_ARCH=$NODE_ARCH" \
37+
--build-arg="NODE_VERSION=$NODE_VERSION" \
38+
--build-arg="UBUNTU_VERSION=$UBUNTU_VERSION" \
39+
--build-arg="RUN_TEST=true" \
40+
--output type=local,dest=./prebuilds,platform-split=false \
41+
-f ./.github/docker/Dockerfile.glibc \
42+
$PROJECT_DIR
43+
}
44+
45+
build_and_test_musl
46+
# build_and_test_glibc

0 commit comments

Comments
 (0)