diff --git a/.github/docker/Dockerfile.glibc b/.github/docker/Dockerfile.glibc new file mode 100644 index 0000000..5b801e3 --- /dev/null +++ b/.github/docker/Dockerfile.glibc @@ -0,0 +1,12 @@ +ARG NODE_BUILD_IMAGE=node:16.20.1-bullseye +FROM $NODE_BUILD_IMAGE AS build + +WORKDIR /mongodb-client-encryption +COPY . . + +RUN node ./.github/scripts/libmongocrypt.mjs +RUN mkdir -p /out && cp -R /mongodb-client-encryption/prebuilds/ /out + +FROM scratch + +COPY --from=build /out / diff --git a/.github/scripts/build_linux.mjs b/.github/scripts/build_linux.mjs new file mode 100644 index 0000000..cc204a5 --- /dev/null +++ b/.github/scripts/build_linux.mjs @@ -0,0 +1,54 @@ +import child_process from 'node:child_process'; +import events from 'node:events'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import url from 'node:url'; + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); + +/** Resolves to the root of this repository */ +function resolveRoot(...paths) { + return path.resolve(__dirname, '..', '..', ...paths); +} + +/** `xtrace` style command runner, uses spawn so that stdio is inherited */ +async function run(command, args = [], options = {}) { + const commandDetails = `+ ${command} ${args.join(' ')}${options.cwd ? ` (in: ${options.cwd})` : ''}` + console.error(commandDetails); + const proc = child_process.spawn(command, args, { + stdio: 'inherit', + cwd: resolveRoot('.'), + ...options + }); + await events.once(proc, 'exit'); + + if (proc.exitCode != 0) throw new Error(`CRASH(${proc.exitCode}): ${commandDetails}`); +} + +async function main() { + await fs.rm(resolveRoot('build'), { recursive: true, force: true }); + await fs.rm(resolveRoot('prebuilds'), { recursive: true, force: true }); + + try { + // Locally you probably already have this + await run('docker', ['buildx', 'use', 'builder']); + } catch { + // But if not, create one + await run('docker', ['buildx', 'create', '--name', 'builder', '--bootstrap', '--use']); + } + + await run('docker', [ + 'buildx', + 'build', + // '--progress=plain', // By default buildx detects tty and does some fancy collapsing, set progress=plain for debugging + '--platform', + 'linux/s390x,linux/arm64,linux/amd64', + '--output', + 'type=local,dest=.,platform-split=false', + '-f', + resolveRoot('./.github/docker/Dockerfile.glibc'), + resolveRoot('.') + ]); +} + +await main(); diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce15838..b4fb22c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,23 +8,46 @@ on: name: build jobs: - build: - runs-on: ubuntu-latest + builds: + outputs: + artifact_id: ${{ steps.upload.outputs.artifact-id }} strategy: - matrix: - node: ['20.x'] # '16.x', '18.x', - name: Node.js ${{ matrix.node }} build + matrix: + os: [ubuntu-latest, windows-2019, macos-11, macos-latest] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - if: ${{ runner.os == 'Linux' }} + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - if: ${{ runner.os == 'Linux' }} + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - if: ${{ runner.os == 'Linux' }} + name: Build ${{ runner.os }} Prebuild + run: node .github/scripts/build_linux.mjs + + - if: ${{ runner.os != 'Linux' }} + name: Setup nodejs + uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node }} + node-version: 'lts/*' cache: 'npm' registry-url: 'https://registry.npmjs.org' - - run: npm install -g npm@latest + - if: ${{ runner.os != 'Linux' }} + name: Build ${{ runner.os }} Prebuild + run: node .github/scripts/libmongocrypt.mjs shell: bash - - run: node .github/scripts/libmongocrypt.mjs - shell: bash + - id: upload + name: Upload prebuild + uses: actions/upload-artifact@v4 + with: + name: build-${{ matrix.os }} + path: prebuilds/ + if-no-files-found: 'error' + retention-days: 1