From 4292689ca4c455da4a0419559df68cc96b5c79d0 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 30 May 2024 15:59:46 -0400 Subject: [PATCH 01/11] chore(NODE-6186): add downloading to FLE build script (#5) Co-authored-by: Durran Jordan --- .github/docker/Dockerfile.glibc | 11 ++ .github/scripts/libmongocrypt.mjs | 193 ++++++++++++++++++++++++------ .github/workflows/build.yml | 81 +++++++++++-- .gitignore | 2 +- package.json | 1 - 5 files changed, 236 insertions(+), 52 deletions(-) create mode 100644 .github/docker/Dockerfile.glibc diff --git a/.github/docker/Dockerfile.glibc b/.github/docker/Dockerfile.glibc new file mode 100644 index 0000000..c114db0 --- /dev/null +++ b/.github/docker/Dockerfile.glibc @@ -0,0 +1,11 @@ +ARG NODE_BUILD_IMAGE=node:16.20.1-bullseye +FROM $NODE_BUILD_IMAGE AS build + +WORKDIR /mongodb-client-encryption +COPY . . + +RUN node /mongodb-client-encryption/.github/scripts/libmongocrypt.mjs + +FROM scratch + +COPY --from=build /mongodb-client-encryption/prebuilds/ / diff --git a/.github/scripts/libmongocrypt.mjs b/.github/scripts/libmongocrypt.mjs index 5846bd2..3566aa8 100644 --- a/.github/scripts/libmongocrypt.mjs +++ b/.github/scripts/libmongocrypt.mjs @@ -4,24 +4,42 @@ import fs from 'node:fs/promises'; import child_process from 'node:child_process'; import events from 'node:events'; import path from 'node:path'; +import https from 'node:https'; +import stream from 'node:stream/promises'; +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); +} + +async function exists(fsPath) { + try { + await fs.access(fsPath); + return true; + } catch { + return false; + } +} async function parseArguments() { - const jsonImport = { [process.version.split('.').at(0) === 'v16' ? 'assert' : 'with']: { type: 'json' } }; - const pkg = (await import('../../package.json', jsonImport)).default; - const libmongocryptVersion = pkg['mongodb:libmongocrypt']; + const pkg = JSON.parse(await fs.readFile(resolveRoot('package.json'), 'utf8')); const options = { - url: { short: 'u', type: 'string', default: 'https://github.com/mongodb/libmongocrypt.git' }, - libversion: { short: 'l', type: 'string', default: libmongocryptVersion }, - clean: { short: 'c', type: 'boolean' }, - help: { short: 'h', type: 'boolean' } + gitURL: { short: 'u', type: 'string', default: 'https://github.com/mongodb/libmongocrypt.git' }, + libVersion: { short: 'l', type: 'string', default: pkg['mongodb:libmongocrypt'] }, + clean: { short: 'c', type: 'boolean', default: false }, + build: { short: 'b', type: 'boolean', default: false }, + help: { short: 'h', type: 'boolean', default: false } }; const args = util.parseArgs({ args: process.argv.slice(2), options, allowPositionals: false }); if (args.values.help) { console.log( - `${process.argv[1]} ${[...Object.keys(options)] + `${path.basename(process.argv[1])} ${[...Object.keys(options)] .filter(k => k !== 'help') .map(k => `[--${k}=${options[k].type}]`) .join(' ')}` @@ -30,15 +48,25 @@ async function parseArguments() { } return { - libmongocrypt: { url: args.values.url, ref: args.values.libversion }, - clean: args.values.clean + libmongocrypt: { url: args.values.gitURL, ref: args.values.libVersion }, + clean: args.values.clean, + build: args.values.build }; } /** `xtrace` style command runner, uses spawn so that stdio is inherited */ async function run(command, args = [], options = {}) { - console.error(`+ ${command} ${args.join(' ')}`, options.cwd ? `(in: ${options.cwd})` : ''); - await events.once(child_process.spawn(command, args, { stdio: 'inherit', ...options }), 'exit'); + const commandDetails = `+ ${command} ${args.join(' ')}${options.cwd ? ` (in: ${options.cwd})` : ''}`; + console.error(commandDetails); + const proc = child_process.spawn(command, args, { + shell: process.platform === 'win32', + stdio: 'inherit', + cwd: resolveRoot('.'), + ...options + }); + await events.once(proc, 'exit'); + + if (proc.exitCode != 0) throw new Error(`CRASH(${proc.exitCode}): ${commandDetails}`); } /** CLI flag maker: `toFlags({a: 1, b: 2})` yields `['-a=1', '-b=2']` */ @@ -46,30 +74,21 @@ function toFlags(object) { return Array.from(Object.entries(object)).map(([k, v]) => `-${k}=${v}`); } -const args = await parseArguments(); -const libmongocryptRoot = path.resolve('_libmongocrypt'); - -const currentLibMongoCryptBranch = await fs.readFile(path.join(libmongocryptRoot, '.git', 'HEAD'), 'utf8').catch(() => '') -const libmongocryptAlreadyClonedAndCheckedOut = currentLibMongoCryptBranch.trim().endsWith(`r-${args.libmongocrypt.ref}`); - -if (args.clean || !libmongocryptAlreadyClonedAndCheckedOut) { - console.error('fetching libmongocrypt...', args.libmongocrypt); +export async function cloneLibMongoCrypt(libmongocryptRoot, { url, ref }) { + console.error('fetching libmongocrypt...', { url, ref }); await fs.rm(libmongocryptRoot, { recursive: true, force: true }); - await run('git', ['clone', args.libmongocrypt.url, libmongocryptRoot]); - await run('git', ['fetch', '--tags'], { cwd: libmongocryptRoot }); - await run('git', ['checkout', args.libmongocrypt.ref, '-b', `r-${args.libmongocrypt.ref}`], { cwd: libmongocryptRoot }); -} else { - console.error('libmongocrypt already up to date...', args.libmongocrypt); + await run('git', ['clone', url, libmongocryptRoot]); + if (ref !== 'latest') { + // Support "latest" as leaving the clone as-is so whatever the default branch name is works + await run('git', ['fetch', '--tags'], { cwd: libmongocryptRoot }); + await run('git', ['checkout', ref, '-b', `r-${ref}`], { cwd: libmongocryptRoot }); + } } -const libmongocryptBuiltVersion = await fs.readFile(path.join(libmongocryptRoot, 'VERSION_CURRENT'), 'utf8').catch(() => ''); -const libmongocryptAlreadyBuilt = libmongocryptBuiltVersion.trim() === args.libmongocrypt.ref; - -if (args.clean || !libmongocryptAlreadyBuilt) { - console.error('building libmongocrypt...\n', args); +export async function buildLibMongoCrypt(libmongocryptRoot, nodeDepsRoot) { + console.error('building libmongocrypt...'); - const nodeDepsRoot = path.resolve('deps'); - const nodeBuildRoot = path.resolve(nodeDepsRoot, 'tmp', 'libmongocrypt-build'); + const nodeBuildRoot = resolveRoot(nodeDepsRoot, 'tmp', 'libmongocrypt-build'); await fs.rm(nodeBuildRoot, { recursive: true, force: true }); await fs.mkdir(nodeBuildRoot, { recursive: true }); @@ -115,11 +134,109 @@ if (args.clean || !libmongocryptAlreadyBuilt) { ? toFlags({ DCMAKE_OSX_DEPLOYMENT_TARGET: '10.12' }) : []; - await run('cmake', [...CMAKE_FLAGS, ...WINDOWS_CMAKE_FLAGS, ...MACOS_CMAKE_FLAGS, libmongocryptRoot], { cwd: nodeBuildRoot }); - await run('cmake', ['--build', '.', '--target', 'install', '--config', 'RelWithDebInfo'], { cwd: nodeBuildRoot }); -} else { - console.error('libmongocrypt already built...'); + await run( + 'cmake', + [...CMAKE_FLAGS, ...WINDOWS_CMAKE_FLAGS, ...MACOS_CMAKE_FLAGS, libmongocryptRoot], + { cwd: nodeBuildRoot } + ); + await run('cmake', ['--build', '.', '--target', 'install', '--config', 'RelWithDebInfo'], { + cwd: nodeBuildRoot + }); +} + +export async function downloadLibMongoCrypt(nodeDepsRoot, { ref }) { + const downloadURL = + ref === 'latest' + ? 'https://mciuploads.s3.amazonaws.com/libmongocrypt/all/master/latest/libmongocrypt-all.tar.gz' + : `https://mciuploads.s3.amazonaws.com/libmongocrypt/all/${ref}/libmongocrypt-all.tar.gz`; + + console.error('downloading libmongocrypt...', downloadURL); + const destination = resolveRoot(`_libmongocrypt-${ref}`); + + await fs.rm(destination, { recursive: true, force: true }); + await fs.mkdir(destination); + + 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}`); + + console.error(`Platform: ${detectedPlatform} Prebuild: ${prebuild}`); + + const unzipArgs = ['-xzv', '-C', `_libmongocrypt-${ref}`, `${prebuild}/nocrypto`]; + console.error(`+ tar ${unzipArgs.join(' ')}`); + const unzip = child_process.spawn('tar', unzipArgs, { + stdio: ['pipe', 'inherit'], + cwd: resolveRoot('.') + }); + + const [response] = await events.once(https.get(downloadURL), 'response'); + + const start = performance.now(); + await stream.pipeline(response, unzip.stdin); + const end = performance.now(); + + console.error(`downloaded libmongocrypt in ${(end - start) / 1000} secs...`); + + await fs.rm(nodeDepsRoot, { recursive: true, force: true }); + await fs.cp(resolveRoot(destination, prebuild, 'nocrypto'), nodeDepsRoot, { recursive: true }); + const currentPath = path.join(nodeDepsRoot, 'lib64'); + try { + await fs.rename(currentPath, path.join(nodeDepsRoot, 'lib')); + } catch (error) { + console.error(`error renaming ${currentPath}: ${error.message}`); + } +} + +async function main() { + const { libmongocrypt, build, clean } = await parseArguments(); + + const nodeDepsDir = resolveRoot('deps'); + + if (build) { + const libmongocryptCloneDir = resolveRoot('_libmongocrypt'); + + const currentLibMongoCryptBranch = await fs + .readFile(path.join(libmongocryptCloneDir, '.git', 'HEAD'), 'utf8') + .catch(() => ''); + const isClonedAndCheckedOut = currentLibMongoCryptBranch + .trim() + .endsWith(`r-${libmongocrypt.ref}`); + + if (clean || !isClonedAndCheckedOut) { + await cloneLibMongoCrypt(libmongocryptCloneDir, libmongocrypt); + } + + const libmongocryptBuiltVersion = await fs + .readFile(path.join(libmongocryptCloneDir, 'VERSION_CURRENT'), 'utf8') + .catch(() => ''); + const isBuilt = libmongocryptBuiltVersion.trim() === libmongocrypt.ref; + + if (clean || !isBuilt) { + await buildLibMongoCrypt(libmongocryptCloneDir, nodeDepsDir); + } + } else { + // Download + await downloadLibMongoCrypt(nodeDepsDir, libmongocrypt); + } + + await fs.rm(resolveRoot('build'), { force: true, recursive: true }); + await fs.rm(resolveRoot('prebuilds'), { force: true, recursive: true }); + + // install with "ignore-scripts" so that we don't attempt to download a prebuild + await run('npm', ['install', '--ignore-scripts']); + // The prebuild command will make both a .node file in `./build` (local and CI testing will run on current code) + // it will also produce `./prebuilds/mongodb-client-encryption-vVERSION-napi-vNAPI_VERSION-OS-ARCH.tar.gz`. + await run('npm', ['run', 'prebuild']); } -await run('npm', ['install', '--ignore-scripts']); -await run('npm', ['run', 'rebuild'], { env: { ...process.env, BUILD_TYPE: 'static' } }); +await main(); diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 86d30cc..1270be7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,24 +1,81 @@ on: push: branches: [main] + pull_request: + branches: [main] workflow_dispatch: {} name: build jobs: - build: + host_builds: + strategy: + matrix: + os: [macos-11, macos-latest, windows-2019] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Build ${{ matrix.os }} Prebuild + run: node .github/scripts/libmongocrypt.mjs ${{ runner.os == 'Windows' && '--build' || '' }} + 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 + compression-level: 0 + + container_builds: + outputs: + artifact_id: ${{ steps.upload.outputs.artifact-id }} runs-on: ubuntu-latest strategy: - matrix: - node: ['20.x'] # '16.x', '18.x', - name: Node.js ${{ matrix.node }} build + matrix: + linux_arch: [s390x, arm64, amd64] steps: - - uses: actions/setup-node@v4 + - uses: actions/checkout@v4 + + - 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 buildx build --platform linux/${{ matrix.linux_arch }} --output type=local,dest=./prebuilds,platform-split=false -f ./.github/docker/Dockerfile.glibc . + + - id: upload + name: Upload prebuild + uses: actions/upload-artifact@v4 with: - node-version: ${{ matrix.node }} - cache: 'npm' - registry-url: 'https://registry.npmjs.org' - - run: npm install -g npm@latest - shell: bash - - run: node .github/scripts/libmongocrypt.mjs - shell: bash + name: build-linux-${{ matrix.linux_arch }} + path: prebuilds/ + if-no-files-found: 'error' + retention-days: 1 + compression-level: 0 + + collect: + needs: [host_builds, container_builds] + runs-on: ubunutu-latest + steps: + - uses: actions/download-artifact@v4 + + - name: Display structure of downloaded files + run: ls -R + + - id: upload + name: Upload all prebuilds + uses: actions/upload-artifact@v4 + with: + name: all-build + path: '*.tar.gz' + if-no-files-found: 'error' + retention-days: 1 + compression-level: 0 diff --git a/.gitignore b/.gitignore index 677c37f..6e615cf 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ xunit.xml lib prebuilds -_libmongocrypt/ +_libmongocrypt* diff --git a/package.json b/package.json index 76a75cd..090045a 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "check:clang-format": "clang-format --style=file:.clang-format --dry-run --Werror addon/*", "test": "mocha test", "prepare": "tsc", - "rebuild": "prebuild --compile", "prebuild": "prebuild --runtime napi --strip --verbose --all" }, "author": { From f7ae840cf8b5647db2908053e83fdaa081924d8a Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 5 Jun 2024 15:38:56 -0400 Subject: [PATCH 02/11] chore: remove unused `exists` function (#10) --- .github/scripts/libmongocrypt.mjs | 9 --------- .github/workflows/build.yml | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/scripts/libmongocrypt.mjs b/.github/scripts/libmongocrypt.mjs index 3566aa8..f07dea3 100644 --- a/.github/scripts/libmongocrypt.mjs +++ b/.github/scripts/libmongocrypt.mjs @@ -15,15 +15,6 @@ function resolveRoot(...paths) { return path.resolve(__dirname, '..', '..', ...paths); } -async function exists(fsPath) { - try { - await fs.access(fsPath); - return true; - } catch { - return false; - } -} - async function parseArguments() { const pkg = JSON.parse(await fs.readFile(resolveRoot('package.json'), 'utf8')); diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1270be7..25c60bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,7 +63,7 @@ jobs: collect: needs: [host_builds, container_builds] - runs-on: ubunutu-latest + runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v4 From bd3ce63b724fb7467f0edc5e5977db204224a3d6 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 10 Jun 2024 15:25:21 +0200 Subject: [PATCH 03/11] chore: bootstrap releases for path: . (#11) --- .release-please-manifest.json | 3 +++ release-please-config.json | 15 +++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 .release-please-manifest.json create mode 100644 release-please-config.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..601e9be --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "6.0.1" +} diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..505e279 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,15 @@ +{ + "pull-request-header": "Please run the release_notes action before releasing to generate release highlights", + "packages": { + ".": { + "include-component-in-tag": false, + "changelog-path": "HISTORY.md", + "release-type": "node", + "bump-minor-pre-major": false, + "bump-patch-for-minor-pre-major": false, + "draft": false, + "prerelease": false + } + }, + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" +} From 39d898a98dd7e1b9229520deb3ad76f84c173807 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 11 Jun 2024 10:04:57 -0600 Subject: [PATCH 04/11] ci(NODE-6185): add test and lint tasks to CI (#4) --- .github/docker/Dockerfile.glibc | 3 ++- .github/workflows/build.yml | 6 +++++- .github/workflows/lint.yml | 38 +++++++++++++++++++++++++++++++++ package.json | 1 + test/release.test.ts | 14 +++++++++--- 5 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/lint.yml diff --git a/.github/docker/Dockerfile.glibc b/.github/docker/Dockerfile.glibc index c114db0..d15188a 100644 --- a/.github/docker/Dockerfile.glibc +++ b/.github/docker/Dockerfile.glibc @@ -4,7 +4,8 @@ FROM $NODE_BUILD_IMAGE AS build WORKDIR /mongodb-client-encryption COPY . . -RUN node /mongodb-client-encryption/.github/scripts/libmongocrypt.mjs +RUN npm run install:libmongocrypt +RUN npm run test FROM scratch diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 25c60bd..a96ea03 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: branches: [main] workflow_dispatch: {} -name: build +name: Build and Test jobs: host_builds: @@ -20,6 +20,10 @@ jobs: run: node .github/scripts/libmongocrypt.mjs ${{ runner.os == 'Windows' && '--build' || '' }} shell: bash + - name: Test ${{ matrix.os }} + shell: bash + run: npm run test + - id: upload name: Upload prebuild uses: actions/upload-artifact@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..5b71978 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,38 @@ +name: Lint + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + name: ${{ matrix.lint-target }} + strategy: + matrix: + lint-target: ["c++", "typescript"] + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - name: "Build libmongocrypt" + shell: bash + run: | + npm run install:libmongocrypt + + - if: matrix.lint-target == 'c++' + shell: bash + run: | + npm run check:clang-format + + - if: matrix.lint-target == 'typescript' + shell: bash + run: | + npm run check:eslint diff --git a/package.json b/package.json index 090045a..b7dc921 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "lib": "lib" }, "scripts": { + "install:libmongocrypt": "node .github/scripts/libmongocrypt.mjs", "install": "prebuild-install --runtime napi || node-gyp rebuild", "clang-format": "clang-format --style=file:.clang-format --Werror -i addon/*", "check:eslint": "eslint src test", diff --git a/test/release.test.ts b/test/release.test.ts index 11e1250..c503c22 100644 --- a/test/release.test.ts +++ b/test/release.test.ts @@ -24,8 +24,14 @@ const REQUIRED_FILES = [ describe(`Release ${packFile}`, function () { this.timeout(10000); + beforeEach(function () { + if (process.arch !== 'x64') { + this.skip(); + } + }); + let tarFileList; - before(() => { + beforeEach(() => { expect(fs.existsSync(packFile)).to.equal(false); cp.execSync('npm pack', { stdio: 'ignore' }); tarFileList = []; @@ -38,8 +44,10 @@ describe(`Release ${packFile}`, function () { }); }); - after(() => { - fs.unlinkSync(packFile); + afterEach(() => { + if (process.arch === 'x64') { + fs.unlinkSync(packFile); + } }); for (const requiredFile of REQUIRED_FILES) { From 4650f7c29caed7e8889d0fe9e05c169484b819a2 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 12 Jun 2024 10:00:18 -0600 Subject: [PATCH 05/11] feat(NODE-5908): support range v2 protocol (#13) --- addon/mongocrypt.cc | 8 +++----- src/index.ts | 2 -- test/bindings.test.ts | 14 +++++++------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/addon/mongocrypt.cc b/addon/mongocrypt.cc index 1688b11..4ed2a8a 100644 --- a/addon/mongocrypt.cc +++ b/addon/mongocrypt.cc @@ -514,9 +514,7 @@ MongoCrypt::MongoCrypt(const CallbackInfo& info) mongocrypt_setopt_bypass_query_analysis(_mongo_crypt.get()); } - if (options.Get("rangeV2").ToBoolean()) { - mongocrypt_setopt_use_range_v2(_mongo_crypt.get()); - } + mongocrypt_setopt_use_range_v2(_mongo_crypt.get()); mongocrypt_setopt_use_need_kms_credentials_state(_mongo_crypt.get()); @@ -596,10 +594,10 @@ Value MongoCrypt::MakeExplicitEncryptionContext(const CallbackInfo& info) { throw TypeError::New(Env(), errorStringFromStatus(context.get())); } - if (strcasecmp(algorithm.c_str(), "rangepreview") == 0) { + if (strcasecmp(algorithm.c_str(), "range") == 0) { if (!options.Has("rangeOptions")) { throw TypeError::New( - Env(), "`rangeOptions` must be provided if `algorithm` is set to RangePreview"); + Env(), "`rangeOptions` must be provided if `algorithm` is set to Range"); } Uint8Array rangeOptions = Uint8ArrayFromValue(options["rangeOptions"], "rangeOptions"); diff --git a/src/index.ts b/src/index.ts index b47218b..7355840 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,8 +40,6 @@ export interface MongoCryptConstructor { cryptSharedLibSearchPaths?: string[]; cryptSharedLibPath?: string; bypassQueryAnalysis?: boolean; - /** @experimental */ - rangeV2?: boolean; }): MongoCrypt; libmongocryptVersion: string; } diff --git a/test/bindings.test.ts b/test/bindings.test.ts index c91e86b..186bc01 100644 --- a/test/bindings.test.ts +++ b/test/bindings.test.ts @@ -341,17 +341,17 @@ describe('MongoCryptConstructor', () => { }); }); - context('when algorithm is `rangePreview', () => { + context('when algorithm is `Range', () => { it('throws a TypeError if rangeOptions is not provided', () => { expect(() => mc.makeExplicitEncryptionContext(value, { // minimum required arguments from libmongocrypt keyId: keyId.buffer, expressionMode: false, - algorithm: 'rangePreview' + algorithm: 'Range' }) ) - .to.throw(/`rangeOptions` must be provided if `algorithm` is set to RangePreview/) + .to.throw(/`rangeOptions` must be provided if `algorithm` is set to Range/) .to.be.instanceOf(TypeError); }); @@ -361,7 +361,7 @@ describe('MongoCryptConstructor', () => { // minimum required arguments from libmongocrypt keyId: keyId.buffer, expressionMode: false, - algorithm: 'rangePreview', + algorithm: 'Range', rangeOptions: 'non-buffer' }) ) @@ -369,18 +369,18 @@ describe('MongoCryptConstructor', () => { .to.be.instanceOf(TypeError); }); - it('checks if `rangePreview` is set case-insensitive', () => { + it('checks if `Range` is set case-insensitive', () => { expect( mc.makeExplicitEncryptionContext(value, { // minimum required arguments from libmongocrypt keyId: keyId.buffer, expressionMode: false, - algorithm: 'RANGEPREVIEW', + algorithm: 'RANGE', rangeOptions: serialize({ sparsity: new Long(42) }), - // contention factor is required for `rangePreview` but + // contention factor is required for `Range` but // is enforced in libmongocrypt, not our bindings contentionFactor: 2 }) From 51244df6e1ccb3496d498ae9a4a34bcf7b4a6a2e Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 13 Jun 2024 19:21:28 +0200 Subject: [PATCH 06/11] chore(NODE-6160): sign and upload to releases (#9) Co-authored-by: Aditi Khare <106987683+aditi-khare-mongoDB@users.noreply.github.com> --- .github/actions/setup/action.yml | 15 ++++ .../sign_and_upload_package/action.yml | 71 +++++++++++++++++++ .github/workflows/build.yml | 43 +++++++---- 3 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 .github/actions/setup/action.yml create mode 100644 .github/actions/sign_and_upload_package/action.yml diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000..a045df1 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,15 @@ +name: Setup +description: 'Installs node, driver dependencies, and builds source' + +runs: + using: composite + steps: + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + cache: 'npm' + registry-url: 'https://registry.npmjs.org' + - run: npm install -g npm@latest + shell: bash + - run: npm clean-install --ignore-scripts + shell: bash diff --git a/.github/actions/sign_and_upload_package/action.yml b/.github/actions/sign_and_upload_package/action.yml new file mode 100644 index 0000000..8d7c948 --- /dev/null +++ b/.github/actions/sign_and_upload_package/action.yml @@ -0,0 +1,71 @@ +name: Sign and Upload Package +description: 'Signs native modules with garasign' + +inputs: + aws_role_arn: + description: 'AWS role input for drivers-github-tools/gpg-sign@v2' + required: true + aws_region_name: + description: 'AWS region name input for drivers-github-tools/gpg-sign@v2' + required: true + aws_secret_id: + description: 'AWS secret id input for drivers-github-tools/gpg-sign@v2' + required: true + npm_package_name: + description: 'The name for the npm package this repository represents' + required: true + +runs: + using: composite + steps: + - uses: actions/download-artifact@v4 + + - name: Make signatures directory + shell: bash + run: mkdir artifacts + + - name: Set up drivers-github-tools + uses: mongodb-labs/drivers-github-tools/setup@v2 + with: + aws_region_name: ${{ inputs.aws_region_name }} + aws_role_arn: ${{ inputs.aws_role_arn }} + aws_secret_id: ${{ inputs.aws_secret_id }} + + - name: Create detached signature + uses: mongodb-labs/drivers-github-tools/gpg-sign@v2 + with: + filenames: 'build-*/*.tar.gz' + env: + RELEASE_ASSETS: artifacts/ + + - name: Copy the tarballs to the artifacts directory + shell: bash + run: for filename in build-*/*.tar.gz; do cp ${filename} artifacts/; done + + - run: npm pack + shell: bash + + - name: Get release version and release package file name + id: get_vars + shell: bash + run: | + package_version=$(jq --raw-output '.version' package.json) + echo "package_version=${package_version}" >> "$GITHUB_OUTPUT" + echo "package_file=${{ inputs.npm_package_name }}-${package_version}.tgz" >> "$GITHUB_OUTPUT" + + - name: Create detached signature for module + uses: mongodb-labs/drivers-github-tools/gpg-sign@v2 + with: + filenames: ${{ steps.get_vars.outputs.package_file }} + env: + RELEASE_ASSETS: artifacts/ + + - name: Display structure of downloaded files + shell: bash + run: ls -la artifacts/ + + - name: "Upload release artifacts" + run: gh release upload v${{ steps.get_vars.outputs.package_version }} artifacts/*.* + shell: bash + env: + GH_TOKEN: ${{ github.token }} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a96ea03..eda08be 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,11 @@ on: name: Build and Test +permissions: + contents: write + pull-requests: write + id-token: write + jobs: host_builds: strategy: @@ -65,21 +70,31 @@ jobs: retention-days: 1 compression-level: 0 - collect: + release_please: needs: [host_builds, container_builds] runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} steps: - - uses: actions/download-artifact@v4 - - - name: Display structure of downloaded files - run: ls -R + - id: release + uses: googleapis/release-please-action@v4 - - id: upload - name: Upload all prebuilds - uses: actions/upload-artifact@v4 - with: - name: all-build - path: '*.tar.gz' - if-no-files-found: 'error' - retention-days: 1 - compression-level: 0 + sign_and_upload: + needs: [release_please] + if: ${{ needs.release_please.outputs.release_created }} + runs-on: ubuntu-latest + environment: release + steps: + - uses: actions/checkout@v4 + - name: actions/setup + uses: ./.github/actions/setup + - name: actions/sign_and_upload_package + uses: ./.github/actions/sign_and_upload_package + with: + aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} + aws_region_name: 'us-east-1' + aws_secret_id: ${{ secrets.AWS_SECRET_ID }} + npm_package_name: 'mongodb-client-encryption' + - run: npm publish --provenance + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file From ac86fcfb42110e190a37a682fbb8d98c3d5988bf Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 13 Jun 2024 12:47:09 -0600 Subject: [PATCH 07/11] chore: adjust package version (#17) --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ba4f99a..fadf63e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mongodb-client-encryption", - "version": "6.0.0", + "version": "6.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mongodb-client-encryption", - "version": "6.0.0", + "version": "6.0.1", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index b7dc921..590c879 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mongodb-client-encryption", - "version": "6.0.0", + "version": "6.0.1", "description": "Official client encryption module for the MongoDB Node.js driver", "main": "lib/index.js", "types": "lib/index.d.ts", From 563154c970a0c89a61f15ac39dc82bb814bab37f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 17 Jun 2024 11:16:03 -0400 Subject: [PATCH 08/11] chore: prepare for alpha release (#20) --- .github/workflows/build.yml | 6 +++--- release-please-config.json | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eda08be..93b8df9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,11 +90,11 @@ jobs: uses: ./.github/actions/setup - name: actions/sign_and_upload_package uses: ./.github/actions/sign_and_upload_package - with: + with: aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} aws_region_name: 'us-east-1' aws_secret_id: ${{ secrets.AWS_SECRET_ID }} npm_package_name: 'mongodb-client-encryption' - - run: npm publish --provenance + - run: npm publish --provenance --tag=alpha env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/release-please-config.json b/release-please-config.json index 505e279..91d9387 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -8,7 +8,9 @@ "bump-minor-pre-major": false, "bump-patch-for-minor-pre-major": false, "draft": false, - "prerelease": false + "prerelease-type": "alpha", + "prerelease": true, + "versioning": "prerelease" } }, "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" From a044ccd956b2e666d4ddeadc6b98d9b5291d32eb Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 17 Jun 2024 16:37:41 -0400 Subject: [PATCH 09/11] feat(NODE-6226): build macos universal binary (#21) --- .github/scripts/libmongocrypt.mjs | 13 +++++++++++-- .github/workflows/build.yml | 2 +- README.md | 15 +++++++++++++++ binding.gyp | 10 +++++++++- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/.github/scripts/libmongocrypt.mjs b/.github/scripts/libmongocrypt.mjs index f07dea3..7299ac1 100644 --- a/.github/scripts/libmongocrypt.mjs +++ b/.github/scripts/libmongocrypt.mjs @@ -41,7 +41,8 @@ async function parseArguments() { return { libmongocrypt: { url: args.values.gitURL, ref: args.values.libVersion }, clean: args.values.clean, - build: args.values.build + build: args.values.build, + pkg }; } @@ -189,7 +190,7 @@ export async function downloadLibMongoCrypt(nodeDepsRoot, { ref }) { } async function main() { - const { libmongocrypt, build, clean } = await parseArguments(); + const { libmongocrypt, build, clean, pkg } = await parseArguments(); const nodeDepsDir = resolveRoot('deps'); @@ -228,6 +229,14 @@ async function main() { // The prebuild command will make both a .node file in `./build` (local and CI testing will run on current code) // it will also produce `./prebuilds/mongodb-client-encryption-vVERSION-napi-vNAPI_VERSION-OS-ARCH.tar.gz`. await run('npm', ['run', 'prebuild']); + + if (process.platform === 'darwin') { + // The "arm64" build is actually a universal binary + await fs.copyFile( + resolveRoot('prebuilds', `mongodb-client-encryption-v${pkg.version}-napi-v4-darwin-arm64.tar.gz`), + resolveRoot('prebuilds', `mongodb-client-encryption-v${pkg.version}-napi-v4-darwin-x64.tar.gz`) + ); + } } await main(); diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93b8df9..a4c3421 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: host_builds: strategy: matrix: - os: [macos-11, macos-latest, windows-2019] + os: [macos-latest, windows-2019] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index 61844ac..50f51ca 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,21 @@ Run the following command to build libmongocrypt and setup the node bindings for bash ./etc/build-static.sh ``` +#### Prebuild Platforms + +Below are the platforms that are available as prebuilds on each github release. +`prebuild-install` downloads these automatically depending on the platform you are running npm install on. + +- Linux GLIBC 2.23 or later + - s390x + - arm64 + - x64 +- MacOS universal binary + - x64 + - arm64 +- Windows + - x64 + #### Linting We lint both the c++ bindings and the Typescript. diff --git a/binding.gyp b/binding.gyp index d789780..aeb631f 100644 --- a/binding.gyp +++ b/binding.gyp @@ -23,7 +23,15 @@ 'xcode_settings': { 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 'CLANG_CXX_LIBRARY': 'libc++', - 'MACOSX_DEPLOYMENT_TARGET': '10.12' + 'MACOSX_DEPLOYMENT_TARGET': '10.12', + "OTHER_CFLAGS": [ + "-arch x86_64", + "-arch arm64" + ], + "OTHER_LDFLAGS": [ + "-arch x86_64", + "-arch arm64" + ] }, 'cflags!': [ '-fno-exceptions' ], 'cflags_cc!': [ '-fno-exceptions' ], From 29dd17511dc1a0151d5d6274a69e7201e98485f6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:14:13 -0400 Subject: [PATCH 10/11] chore(main): release 6.1.0-alpha (#15) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- HISTORY.md | 8 ++++++++ package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 601e9be..0eb5a54 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "6.0.1" + ".": "6.1.0-alpha" } diff --git a/HISTORY.md b/HISTORY.md index 0c23a3f..53e2c65 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [6.1.0-alpha](https://github.com/mongodb-js/mongodb-client-encryption/compare/v6.0.1...v6.1.0-alpha) (2024-06-17) + + +### Features + +* **NODE-5908:** support range v2 protocol ([#13](https://github.com/mongodb-js/mongodb-client-encryption/issues/13)) ([4650f7c](https://github.com/mongodb-js/mongodb-client-encryption/commit/4650f7c29caed7e8889d0fe9e05c169484b819a2)) +* **NODE-6226:** build macos universal binary ([#21](https://github.com/mongodb-js/mongodb-client-encryption/issues/21)) ([a044ccd](https://github.com/mongodb-js/mongodb-client-encryption/commit/a044ccd956b2e666d4ddeadc6b98d9b5291d32eb)) + ## [6.0.0](https://github.com/mongodb-js/mongodb-client-encryption/compare/v6.0.0-alpha.3...v6.0.0) (2023-08-28) * 86bd61e docs: Update Node bindings README.md diff --git a/package-lock.json b/package-lock.json index fadf63e..f0c536b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mongodb-client-encryption", - "version": "6.0.1", + "version": "6.1.0-alpha", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mongodb-client-encryption", - "version": "6.0.1", + "version": "6.1.0-alpha", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 590c879..98dcbaa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mongodb-client-encryption", - "version": "6.0.1", + "version": "6.1.0-alpha", "description": "Official client encryption module for the MongoDB Node.js driver", "main": "lib/index.js", "types": "lib/index.d.ts", From 270ecc69e9b7244278bafee042b351773f68b27c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:51:40 -0400 Subject: [PATCH 11/11] chore(main): release 6.1.0-alpha (#22) Release-As: 6.1.0-alpha --- release-please-config.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/release-please-config.json b/release-please-config.json index 91d9387..2bb2f6b 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -8,9 +8,7 @@ "bump-minor-pre-major": false, "bump-patch-for-minor-pre-major": false, "draft": false, - "prerelease-type": "alpha", - "prerelease": true, - "versioning": "prerelease" + "prerelease": true } }, "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"