Skip to content

Commit

Permalink
feat(NODE-6727): Add builds on Alpine linux (#65)
Browse files Browse the repository at this point in the history
Co-authored-by: Neal Beeken <neal.beeken@mongodb.com>
  • Loading branch information
baileympearson and nbbeeken authored Feb 10, 2025
1 parent 3787edd commit f4274d7
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 21 deletions.
18 changes: 18 additions & 0 deletions .github/docker/Dockerfile.musl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
ARG PLATFORM=arm64
ARG NODE_VERSION=16.20.1

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 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 /mongodb-client-encryption/prebuilds/ /
63 changes: 45 additions & 18 deletions .github/scripts/utils.mjs
Original file line number Diff line number Diff line change
@@ -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));

Expand Down Expand Up @@ -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 */
Expand All @@ -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}`);
}
}

/**
* @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';
}
44 changes: 42 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -64,8 +64,48 @@ 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
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 \
.
- 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
41 changes: 40 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
shell: bash
run: npm run test

container_tests:
container_tests_glibc:
runs-on: ubuntu-latest
strategy:
matrix:
Expand Down Expand Up @@ -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 \
.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
46 changes: 46 additions & 0 deletions etc/docker.sh
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit f4274d7

Please sign in to comment.