From dec0a37f251d92324c467ae70b8a60590e9533a9 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 15 Jul 2021 17:19:59 +0200 Subject: [PATCH 1/4] feat: add --build-info cli flag MONGOSH-896 As part of this: - Rename the analytics config writer part of the build package and add information other than the Segment API key to it - Clean up some functions in the build package to just take a config argument instead of a list of arguments with names that differ between call site and function definition (this was super confusing before, and still is in a lot of places) - Make sure that the compiled binary identifies itself as such in the evergreen script --- .evergreen/compile-artifact.sh | 2 + config/build.conf.js | 12 ++-- packages/build/src/analytics.spec.ts | 65 ------------------- packages/build/src/analytics.ts | 37 ----------- packages/build/src/barque.spec.ts | 2 +- packages/build/src/build-info.ts | 29 +++++++++ .../build/src/compile/generate-bundle.spec.ts | 43 +++++++----- packages/build/src/compile/generate-bundle.ts | 23 ++++--- packages/build/src/compile/run-compile.ts | 23 +++---- .../build/src/compile/signable-compiler.ts | 5 +- packages/build/src/config/config.ts | 3 +- packages/build/src/release.ts | 19 ++---- packages/build/src/run-draft.spec.ts | 2 +- packages/build/src/run-publish.spec.ts | 41 ++++++------ packages/build/src/run-publish.ts | 9 +-- packages/build/src/run-upload.spec.ts | 2 +- packages/cli-repl/src/arg-parser.ts | 7 +- packages/cli-repl/src/cli-repl.ts | 2 +- packages/cli-repl/src/run.ts | 20 ++++++ packages/cli-repl/test/e2e.spec.ts | 28 +++++++- 20 files changed, 168 insertions(+), 206 deletions(-) delete mode 100644 packages/build/src/analytics.spec.ts delete mode 100644 packages/build/src/analytics.ts create mode 100644 packages/build/src/build-info.ts diff --git a/.evergreen/compile-artifact.sh b/.evergreen/compile-artifact.sh index 1af34243e6..9e5e411b92 100755 --- a/.evergreen/compile-artifact.sh +++ b/.evergreen/compile-artifact.sh @@ -36,5 +36,7 @@ fi export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD="true" npm run evergreen-release compile dist/mongosh --version +dist/mongosh --build-info +dist/mongosh --build-info | grep -q '"distributionKind": "compiled"' tar cvzf dist.tgz dist diff --git a/config/build.conf.js b/config/build.conf.js index 9142f10985..46a269caf0 100644 --- a/config/build.conf.js +++ b/config/build.conf.js @@ -46,9 +46,9 @@ const EXECUTABLE_PATH = path.join(OUTPUT_DIR, process.platform === 'win32' ? 'mo const MONGOCRYPTD_PATH = path.resolve(__dirname, '..', 'tmp', 'mongocryptd-mongosh' + (process.platform === 'win32' ? '.exe' : '')); /** - * Analytics configuration file. + * Build info JSON data file. */ -const ANALYTICS_CONFIG_FILE_PATH = path.join(CLI_REPL_DIR, 'lib', 'analytics-config.js'); +const BUILD_INFO_FILE_PATH = path.join(CLI_REPL_DIR, 'lib', 'build-info.json'); /** * The bundle id for MacOs. @@ -58,10 +58,7 @@ const APPLE_NOTARIZATION_BUNDLE_ID = 'com.mongodb.mongosh'; /** * The SHA for the current git HEAD. */ -// TODO: replace with "real" SHA after EVG-13919 -const REVISION = process.env.IS_PATCH ? - `pr-${process.env.GITHUB_PR_NUMBER}-${process.env.REVISION_ORDER_ID}` : - process.env.REVISION; +const REVISION = process.env.GITHUB_COMMIT ?? process.env.REVISION; /** * The copyright notice for debian packages and .exe files @@ -78,7 +75,8 @@ module.exports = { execInput: EXEC_INPUT, executablePath: EXECUTABLE_PATH, outputDir: OUTPUT_DIR, - analyticsConfigFilePath: ANALYTICS_CONFIG_FILE_PATH, + buildInfoFilePath: BUILD_INFO_FILE_PATH, + executableOsId: process.env.EXECUTABLE_OS_ID, project: process.env.PROJECT, revision: REVISION, branch: process.env.BRANCH_NAME, diff --git a/packages/build/src/analytics.spec.ts b/packages/build/src/analytics.spec.ts deleted file mode 100644 index 3c9111b4c6..0000000000 --- a/packages/build/src/analytics.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { expect } from 'chai'; -import { promises as fs } from 'fs'; -import * as path from 'path'; -import rimraf from 'rimraf'; -import { promisify } from 'util'; -import { createAnalyticsConfig, writeAnalyticsConfig } from './analytics'; - -describe('analytics', () => { - describe('createAnalyticsConfig', () => { - let config: string; - - before(() => { - config = createAnalyticsConfig('key'); - }); - - it('returns the string with the segment key injected', () => { - expect(config).to.include('SEGMENT_API_KEY: "key"'); - }); - }); - - describe('writeAnalyticsConfig', () => { - const tmpdir = path.resolve( - __dirname, '..', '..', 'tmp', `test-build-${Date.now()}-${Math.random()}` - ); - - before(async() => { - await fs.mkdir(tmpdir, { recursive: true }); - }); - after(async() => { - await promisify(rimraf)(tmpdir); - }); - - it('writes the configuration', async() => { - const file = path.join(tmpdir, 'analytics.js'); - await writeAnalyticsConfig( - file, - 'key' - ); - const content = await fs.readFile(file); - expect(content.toString('utf8')).to.equal('module.exports = { SEGMENT_API_KEY: "key" };'); - }); - - it('fails when file path is missing', async() => { - try { - await writeAnalyticsConfig( - '', 'key' - ); - } catch (e) { - return expect(e.message).to.contain('path is required'); - } - expect.fail('Expected error'); - }); - - it('fails when key is missing', async() => { - try { - await writeAnalyticsConfig( - 'somepath', '' - ); - } catch (e) { - return expect(e.message).to.contain('Segment key is required'); - } - expect.fail('Expected error'); - }); - }); -}); diff --git a/packages/build/src/analytics.ts b/packages/build/src/analytics.ts deleted file mode 100644 index 32287f5e76..0000000000 --- a/packages/build/src/analytics.ts +++ /dev/null @@ -1,37 +0,0 @@ -import util from 'util'; -import fs from 'fs'; - -/** - * Create the analytics config. - * - * @param {string} segmentKey - The segment key. - * - * @returns {string} The compiled template. - */ -export function createAnalyticsConfig(segmentKey: string): string { - return `module.exports = { SEGMENT_API_KEY: ${JSON.stringify(segmentKey)} };`; -} - -/** - * Write the analytics config. - * - * @param analyticsConfigFilePath - The filename. - * @param segmentKey - The segment key. - */ -export function writeAnalyticsConfig( - analyticsConfigFilePath?: string, - segmentKey?: string -): Promise { - if (!analyticsConfigFilePath) { - throw new Error('Analytics config file path is required'); - } - - if (!segmentKey) { - throw new Error('Segment key is required'); - } - - const template = createAnalyticsConfig(segmentKey); - console.info('mongosh: writing analytics template:', analyticsConfigFilePath); - // Cannot use fs/promises on Cygwin. - return util.promisify(fs.writeFile)(analyticsConfigFilePath, template); -} diff --git a/packages/build/src/barque.spec.ts b/packages/build/src/barque.spec.ts index 552f6ed0eb..c27062a9dd 100644 --- a/packages/build/src/barque.spec.ts +++ b/packages/build/src/barque.spec.ts @@ -21,7 +21,7 @@ describe('Barque', () => { executablePath: 'executablePath', mongocryptdPath: 'mongocryptdPath', outputDir: 'outputDir', - analyticsConfigFilePath: 'analyticsConfigFilePath', + buildInfoFilePath: 'buildInfoFilePath', project: 'project', revision: 'revision', branch: 'branch', diff --git a/packages/build/src/build-info.ts b/packages/build/src/build-info.ts new file mode 100644 index 0000000000..e90c019d26 --- /dev/null +++ b/packages/build/src/build-info.ts @@ -0,0 +1,29 @@ +import type { Config } from './config'; +import { promises as fs } from 'fs'; +import os from 'os'; + +/** + * Write data into a build config that is included in the executable but + * comes from the build environment rather than the source tree. + */ +export async function writeBuildInfo(config: Config, distributionKind: 'compiled' | 'packaged'): Promise { + if (!config.buildInfoFilePath) { + throw new Error('Build info file path is required'); + } + + if (!config.segmentKey) { + throw new Error('Segment key is required'); + } + + const info = { + segmentApiKey: config.segmentKey, + distributionKind, + buildArch: os.arch(), + buildPlatform: os.platform(), + buildTarget: config.executableOsId ?? 'unknown', + buildTime: new Date().toISOString(), + gitVersion: config.revision ?? null + }; + console.info('mongosh: writing build info data:', config.buildInfoFilePath); + await fs.writeFile(config.buildInfoFilePath, JSON.stringify(info)); +} diff --git a/packages/build/src/compile/generate-bundle.spec.ts b/packages/build/src/compile/generate-bundle.spec.ts index a92990debb..ad89dd1648 100644 --- a/packages/build/src/compile/generate-bundle.spec.ts +++ b/packages/build/src/compile/generate-bundle.spec.ts @@ -5,6 +5,7 @@ import path from 'path'; import rimraf from 'rimraf'; import { promisify } from 'util'; import { generateBundle } from './generate-bundle'; +import type { Config } from '../config'; const execFile = promisify(childProcess.execFile); @@ -28,21 +29,25 @@ describe('compile generateBundle', function() { await fs.writeFile(path.join(tmpdir, 'b.js'), ` console.log(JSON.stringify({ main: 'it ' + require("./a") + '!', - analytics: require("./analytics") + buildInfo: require("./buildInfo.json") })); `); - await generateBundle( - path.join(tmpdir, 'b.js'), - path.join(tmpdir, 'compiled.js'), - path.join(tmpdir, 'analytics.js'), - '...segment-key...'); + await generateBundle({ + input: path.join(tmpdir, 'b.js'), + execInput: path.join(tmpdir, 'compiled.js'), + buildInfoFilePath: path.join(tmpdir, 'buildInfo.json'), + segmentKey: '...segment-key...' + } as Partial as any); await fs.unlink(path.join(tmpdir, 'a.js')); await fs.unlink(path.join(tmpdir, 'b.js')); - await fs.unlink(path.join(tmpdir, 'analytics.js')); + await fs.unlink(path.join(tmpdir, 'buildInfo.json')); const { stdout } = await execFile(process.execPath, [path.join(tmpdir, 'compiled.js')]); const parsed = JSON.parse(stdout); expect(parsed.main).to.equal('it works!'); - expect(parsed.analytics.SEGMENT_API_KEY).to.equal('...segment-key...'); + expect(parsed.buildInfo.segmentApiKey).to.equal('...segment-key...'); + expect(parsed.buildInfo.buildArch).to.equal(os.arch()); + expect(parsed.buildInfo.buildPlatform).to.equal(os.platform()); + expect(parsed.buildInfo.buildTime).to.be.a('string'); }); it('does not attempt to load ES6 modules because parcel cannot handle them properly', async() => { @@ -54,11 +59,12 @@ describe('compile generateBundle', function() { await fs.writeFile(path.join(tmpdir, 'b.js'), ` console.log(require("some-fake-module")); `); - await generateBundle( - path.join(tmpdir, 'b.js'), - path.join(tmpdir, 'compiled.js'), - path.join(tmpdir, 'analytics.js'), - '...segment-key...'); + await generateBundle({ + input: path.join(tmpdir, 'b.js'), + execInput: path.join(tmpdir, 'compiled.js'), + buildInfoFilePath: path.join(tmpdir, 'buildInfo.json'), + segmentKey: '...segment-key...' + } as Partial as any); const { stdout } = await execFile(process.execPath, [path.join(tmpdir, 'compiled.js')]); expect(stdout.trim()).to.equal('cjs'); }); @@ -72,11 +78,12 @@ describe('compile generateBundle', function() { await fs.writeFile(path.join(tmpdir, 'b.js'), ` console.log(require("some-fake-module")); `); - await generateBundle( - path.join(tmpdir, 'b.js'), - path.join(tmpdir, 'compiled.js'), - path.join(tmpdir, 'analytics.js'), - '...segment-key...'); + await generateBundle({ + input: path.join(tmpdir, 'b.js'), + execInput: path.join(tmpdir, 'compiled.js'), + buildInfoFilePath: path.join(tmpdir, 'buildInfo.json'), + segmentKey: '...segment-key...' + } as Partial as any); const { stdout } = await execFile(process.execPath, [path.join(tmpdir, 'compiled.js')]); expect(stdout.trim()).to.equal('plain'); }); diff --git a/packages/build/src/compile/generate-bundle.ts b/packages/build/src/compile/generate-bundle.ts index 08708e6dc6..613ce79774 100644 --- a/packages/build/src/compile/generate-bundle.ts +++ b/packages/build/src/compile/generate-bundle.ts @@ -1,29 +1,28 @@ import path from 'path'; import Bundler from 'parcel-bundler'; -import { writeAnalyticsConfig } from '../analytics'; +import { writeBuildInfo } from '../build-info'; +import type { Config } from '../config'; /** * Generate the bundled up JS entryFile that will be compiled * into the executable. * - * @param {string} entryFile - The entry file for generating the bundle. - * @param {string} bundleOutputFile - The output file the bundle will be written to. - * @param {string} analyticsConfigFilePath - The path to the analytics config file. - * @param {string} segmentKey - The segment API key. + * @param {object} config - The current build config. */ -export async function generateBundle(entryFile: string, bundleOutputFile: string, analyticsConfigFilePath: string, segmentKey: string): Promise { +export async function generateBundle(config: Config): Promise { // This takes the segment api key and writes it to the - // cli-repl's analytics-config file. - await writeAnalyticsConfig(analyticsConfigFilePath, segmentKey); + // cli-repl's analytics-config file, as well as information about the + // current build environment. + await writeBuildInfo(config, 'compiled'); - console.info('mongosh: creating bundle:', bundleOutputFile); + console.info('mongosh: creating bundle:', config.execInput); // Parcel is the saviour here since it was the only bundling // tool that could figure out how to handle everything in a // complex lerna project with cyclic dependencies everywhere. - const bundler = new Bundler(entryFile, { - outDir: path.dirname(bundleOutputFile), - outFile: path.basename(bundleOutputFile), + const bundler = new Bundler(config.input, { + outDir: path.dirname(config.execInput), + outFile: path.basename(config.execInput), contentHash: false, target: 'node', bundleNodeModules: true, diff --git a/packages/build/src/compile/run-compile.ts b/packages/build/src/compile/run-compile.ts index ce36790ac8..1ac085c06d 100644 --- a/packages/build/src/compile/run-compile.ts +++ b/packages/build/src/compile/run-compile.ts @@ -1,3 +1,4 @@ +import type { Config } from '../config'; import type { PackageInformation } from '../packaging/package'; import { generateBundle } from './generate-bundle'; import { SignableCompiler } from './signable-compiler'; @@ -6,24 +7,20 @@ import { SignableCompiler } from './signable-compiler'; * Compile the executable. This builds the thing that ends up in `dist/` * that we will zip up and send off to userland. */ -export async function runCompile( - input: string, - execInput: string, - executablePath: string, - execNodeVersion: string, - analyticsConfigFilePath: string, - segmentKey: string, - executableMetadata: PackageInformation['metadata'] -): Promise { +export async function runCompile(config: Config): Promise { // We use Parcel to bundle up everything into a single JS under // cli-repl/dist/mongosh.js that the executable generator can use as input. // This JS also takes care of the analytics config file being written. - await generateBundle(input, execInput, analyticsConfigFilePath, segmentKey); + await generateBundle(config); - console.info('mongosh: creating binary:', executablePath); + console.info('mongosh: creating binary:', config.executablePath); - await new SignableCompiler(execInput, executablePath, execNodeVersion, executableMetadata) + await new SignableCompiler( + config.execInput, + config.executablePath, + config.execNodeVersion, + (config.packageInformation?.metadata ?? {}) as PackageInformation['metadata']) .compile(); - return executablePath; + return config.executablePath; } diff --git a/packages/build/src/compile/signable-compiler.ts b/packages/build/src/compile/signable-compiler.ts index ceaadcb00c..1b250651e2 100644 --- a/packages/build/src/compile/signable-compiler.ts +++ b/packages/build/src/compile/signable-compiler.ts @@ -5,7 +5,6 @@ import pkgUp from 'pkg-up'; import path from 'path'; import childProcess from 'child_process'; import { once } from 'events'; -import { Platform } from '../config'; import type { PackageInformation } from '../packaging/package'; import { compileJSFileAsBinary } from 'boxednode'; @@ -88,8 +87,8 @@ export class SignableCompiler { // open ssl with asm so we revert back to the slower version. await compileJSFileAsBinary({ configureArgs: - os.platform() === Platform.Windows ? ['openssl-no-asm'] : - os.platform() === Platform.MacOs ? ['--openssl-no-asm'] : [], + os.platform() === 'win32' ? ['openssl-no-asm'] : + os.platform() === 'darwin' ? ['--openssl-no-asm'] : [], sourceFile: this.sourceFile, targetFile: this.targetFile, nodeVersionRange: this.nodeVersionRange, diff --git a/packages/build/src/config/config.ts b/packages/build/src/config/config.ts index 372b1d5d90..631d6a5b65 100644 --- a/packages/build/src/config/config.ts +++ b/packages/build/src/config/config.ts @@ -11,7 +11,8 @@ export interface Config { execInput: string; executablePath: string; outputDir: string; - analyticsConfigFilePath?: string; + buildInfoFilePath?: string; + executableOsId?: string; rootDir: string; project?: string; revision?: string; diff --git a/packages/build/src/release.ts b/packages/build/src/release.ts index a2d3165a12..602fadd384 100644 --- a/packages/build/src/release.ts +++ b/packages/build/src/release.ts @@ -1,7 +1,7 @@ import { Octokit } from '@octokit/rest'; import { promises as fs } from 'fs'; import path from 'path'; -import { writeAnalyticsConfig } from './analytics'; +import { writeBuildInfo } from './build-info'; import { Barque } from './barque'; import { runCompile } from './compile'; import { Config, getReleaseVersionFromTag, redactConfig } from './config'; @@ -10,7 +10,6 @@ import { downloadArtifactFromEvergreen, uploadArtifactToEvergreen } from './ever import { GithubRepo } from './github-repo'; import { publishToHomebrew } from './homebrew'; import { bumpNpmPackages, publishNpmPackages } from './npm-packages'; -import type { PackageInformation } from './packaging'; import { runPackage } from './packaging'; import { runDraft } from './run-draft'; import { runPublish } from './run-publish'; @@ -52,19 +51,9 @@ export async function release( const mongoHomebrewForkRepo = new GithubRepo({ owner: 'mongodb-js', repo: 'homebrew-core' }, octokit); if (command === 'compile') { - await runCompile( - config.input, - config.execInput, - config.executablePath, - config.execNodeVersion, - config.analyticsConfigFilePath ?? '', - config.segmentKey ?? '', - (config.packageInformation?.metadata ?? {}) as PackageInformation['metadata'] - ); + await runCompile(config); } else if (command === 'package') { - const tarballFile = await runPackage( - config - ); + const tarballFile = await runPackage(config); await fs.writeFile(path.join(config.outputDir, '.artifact_metadata'), JSON.stringify(tarballFile)); } else if (command === 'upload') { const tarballFile = JSON.parse(await fs.readFile(path.join(config.outputDir, '.artifact_metadata'), 'utf8')); @@ -90,7 +79,7 @@ export async function release( barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew ); } else { diff --git a/packages/build/src/run-draft.spec.ts b/packages/build/src/run-draft.spec.ts index 060787f781..f427b42ff7 100644 --- a/packages/build/src/run-draft.spec.ts +++ b/packages/build/src/run-draft.spec.ts @@ -30,7 +30,7 @@ describe('draft', () => { executablePath: 'executablePath', mongocryptdPath: 'mongocryptdPath', outputDir: 'outputDir', - analyticsConfigFilePath: 'analyticsConfigFilePath', + buildInfoFilePath: 'buildInfoFilePath', project: 'project', revision: 'revision', branch: 'branch', diff --git a/packages/build/src/run-publish.spec.ts b/packages/build/src/run-publish.spec.ts index 5dfe405cbb..9d9080710f 100644 --- a/packages/build/src/run-publish.spec.ts +++ b/packages/build/src/run-publish.spec.ts @@ -1,7 +1,7 @@ import chai, { expect } from 'chai'; import path from 'path'; import sinon from 'ts-sinon'; -import type { writeAnalyticsConfig as writeAnalyticsConfigType } from './analytics'; +import type { writeBuildInfo as writeBuildInfoType } from './build-info'; import { Barque } from './barque'; import { Config, shouldDoPublicRelease as shouldDoPublicReleaseFn } from './config'; import { createAndPublishDownloadCenterConfig as createAndPublishDownloadCenterConfigFn } from './download-center'; @@ -25,7 +25,7 @@ describe('publish', () => { let config: Config; let createAndPublishDownloadCenterConfig: typeof createAndPublishDownloadCenterConfigFn; let publishNpmPackages: typeof publishNpmPackagesType; - let writeAnalyticsConfig: typeof writeAnalyticsConfigType; + let writeBuildInfo: typeof writeBuildInfoType; let publishToHomebrew: typeof publishToHomebrewType; let shouldDoPublicRelease: typeof shouldDoPublicReleaseFn; let githubRepo: GithubRepo; @@ -42,7 +42,7 @@ describe('publish', () => { executablePath: 'executablePath', mongocryptdPath: 'mongocryptdPath', outputDir: 'outputDir', - analyticsConfigFilePath: 'analyticsConfigFilePath', + buildInfoFilePath: 'buildInfoFilePath', project: 'project', revision: 'revision', branch: 'branch', @@ -78,7 +78,7 @@ describe('publish', () => { createAndPublishDownloadCenterConfig = sinon.spy(); publishNpmPackages = sinon.spy(); - writeAnalyticsConfig = sinon.spy(); + writeBuildInfo = sinon.spy(); publishToHomebrew = sinon.spy(); shouldDoPublicRelease = sinon.spy(); githubRepo = createStubRepo(); @@ -119,7 +119,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -142,7 +142,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -165,7 +165,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -185,7 +185,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -215,7 +215,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -236,7 +236,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -253,17 +253,14 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); - expect(writeAnalyticsConfig).to.have.been.calledOnceWith( - config.analyticsConfigFilePath, - config.segmentKey - ); + expect(writeBuildInfo).to.have.been.calledOnceWith(config); expect(publishNpmPackages).to.have.been.calledWith(); - expect(publishNpmPackages).to.have.been.calledAfter(writeAnalyticsConfig as any); + expect(publishNpmPackages).to.have.been.calledAfter(writeBuildInfo as any); }); it('publishes to homebrew', async() => { await runPublish( @@ -274,7 +271,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -302,7 +299,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -319,7 +316,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -336,7 +333,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -353,7 +350,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); @@ -370,7 +367,7 @@ describe('publish', () => { barque, createAndPublishDownloadCenterConfig, publishNpmPackages, - writeAnalyticsConfig, + writeBuildInfo, publishToHomebrew, shouldDoPublicRelease ); diff --git a/packages/build/src/run-publish.ts b/packages/build/src/run-publish.ts index c8566eff15..9833fcf675 100644 --- a/packages/build/src/run-publish.ts +++ b/packages/build/src/run-publish.ts @@ -1,4 +1,4 @@ -import type { writeAnalyticsConfig as writeAnalyticsConfigType } from './analytics'; +import type { writeBuildInfo as writeBuildInfoType } from './build-info'; import { Barque } from './barque'; import { ALL_BUILD_VARIANTS, @@ -21,7 +21,7 @@ export async function runPublish( barque: Barque, createAndPublishDownloadCenterConfig: typeof createAndPublishDownloadCenterConfigFn, publishNpmPackages: typeof publishNpmPackagesType, - writeAnalyticsConfig: typeof writeAnalyticsConfigType, + writeBuildInfo: typeof writeBuildInfoType, publishToHomebrew: typeof publishToHomebrewType, shouldDoPublicRelease: typeof shouldDoPublicReleaseFn = shouldDoPublicReleaseFn, getEvergreenArtifactUrl: typeof getArtifactUrlFn = getArtifactUrlFn @@ -65,10 +65,7 @@ export async function runPublish( await mongoshGithubRepo.promoteRelease(config); // ensures the segment api key to be present in the published packages - await writeAnalyticsConfig( - config.analyticsConfigFilePath, - config.segmentKey - ); + await writeBuildInfo(config, 'packaged'); publishNpmPackages(); diff --git a/packages/build/src/run-upload.spec.ts b/packages/build/src/run-upload.spec.ts index b3b0867866..c30cb01656 100644 --- a/packages/build/src/run-upload.spec.ts +++ b/packages/build/src/run-upload.spec.ts @@ -22,7 +22,7 @@ describe('do-upload', () => { executablePath: 'executablePath', mongocryptdPath: 'mongocryptdPath', outputDir: 'outputDir', - analyticsConfigFilePath: 'analyticsConfigFilePath', + buildInfoFilePath: 'buildInfoFilePath', project: 'project', revision: 'revision', branch: 'branch', diff --git a/packages/cli-repl/src/arg-parser.ts b/packages/cli-repl/src/arg-parser.ts index 081ad8f148..09ffe57132 100644 --- a/packages/cli-repl/src/arg-parser.ts +++ b/packages/cli-repl/src/arg-parser.ts @@ -52,6 +52,7 @@ const OPTIONS = { boolean: [ 'apiDeprecationErrors', 'apiStrict', + 'buildInfo', 'help', 'ipv6', 'nodb', @@ -78,7 +79,8 @@ const OPTIONS = { h: 'help', p: 'password', u: 'username', - f: 'file' + f: 'file', + 'build-info': 'buildInfo' }, configuration: { 'camel-case-expansion': false, @@ -144,12 +146,13 @@ function isConnectionSpecifier(arg?: string): boolean { * * @returns The arguments as cli options. */ -export function parseCliArgs(args: string[]): (CliOptions & { smokeTests: boolean }) { +export function parseCliArgs(args: string[]): (CliOptions & { smokeTests: boolean, buildInfo: boolean }) { const programArgs = args.slice(2); i18n.setLocale(getLocale(programArgs, process.env)); const parsed = parser(programArgs, OPTIONS) as unknown as CliOptions & { smokeTests: boolean; + buildInfo: boolean; _?: string[]; file?: string[]; }; diff --git a/packages/cli-repl/src/cli-repl.ts b/packages/cli-repl/src/cli-repl.ts index 7572253334..606603841e 100644 --- a/packages/cli-repl/src/cli-repl.ts +++ b/packages/cli-repl/src/cli-repl.ts @@ -150,7 +150,7 @@ class CliRepl { } this.analytics = new Analytics( // analytics-config.js gets written as a part of a release - this.analyticsOptions?.apiKey ?? require('./analytics-config.js').SEGMENT_API_KEY, + this.analyticsOptions?.apiKey ?? require('./buildinfo.json').segmentApiKey, this.analyticsOptions); return this.analytics; }); diff --git a/packages/cli-repl/src/run.ts b/packages/cli-repl/src/run.ts index 70d46d850b..2296455962 100644 --- a/packages/cli-repl/src/run.ts +++ b/packages/cli-repl/src/run.ts @@ -2,7 +2,9 @@ import { CliRepl, parseCliArgs, mapCliToDriver, getStoragePaths, getMongocryptdP import { generateUri } from '@mongosh/service-provider-server'; import { redactCredentials } from '@mongosh/history'; import { runMain } from 'module'; +import os from 'os'; +// eslint-disable-next-line complexity (async() => { if (process.env.MONGOSH_RUN_NODE_SCRIPT) { if (process.execPath !== process.argv[1]) { @@ -24,6 +26,24 @@ import { runMain } from 'module'; } else if (options.version) { // eslint-disable-next-line no-console console.log(version); + } else if (options.buildInfo) { + if (process.execPath === process.argv[1]) { + const buildInfo = require('./buildinfo.json'); + delete buildInfo.segmentApiKey; + // eslint-disable-next-line no-console + console.log(JSON.stringify(buildInfo, null, ' ')); + } else { + // eslint-disable-next-line no-console + console.log(JSON.stringify({ + version, + distributionKind: 'unpackaged', + buildArch: os.arch(), + buildPlatform: os.platform(), + buildTarget: 'unknown', + buildTime: null, + gitVersion: null + }, null, ' ')); + } } else if (options.smokeTests) { const smokeTestServer = process.env.MONGOSH_SMOKE_TEST_SERVER; if (process.execPath === process.argv[1]) { diff --git a/packages/cli-repl/test/e2e.spec.ts b/packages/cli-repl/test/e2e.spec.ts index 9799b8e2fa..b85952e40d 100644 --- a/packages/cli-repl/test/e2e.spec.ts +++ b/packages/cli-repl/test/e2e.spec.ts @@ -20,7 +20,6 @@ describe('e2e', function() { describe('--version', () => { it('shows version', async() => { const shell = TestShell.start({ args: [ '--version' ] }); - await shell.waitForExit(); shell.assertNoErrors(); @@ -30,6 +29,33 @@ describe('e2e', function() { }); }); + describe('--build-info', () => { + it('shows build info in JSON format', async() => { + const shell = TestShell.start({ args: [ '--build-info' ] }); + await shell.waitForExit(); + + shell.assertNoErrors(); + const data = JSON.parse(shell.output); + expect(Object.keys(data)).to.deep.equal([ + 'version', 'distributionKind', 'buildArch', 'buildPlatform', + 'buildTarget', 'buildTime', 'gitVersion' + ]); + expect(data.version).to.be.a('string'); + expect(data.distributionKind).to.be.a('string'); + expect(['unpackaged', 'packaged', 'compiled'].includes(data.distributionKind)).to.be.true; + expect(data.buildArch).to.be.a('string'); + expect(data.buildPlatform).to.be.a('string'); + expect(data.buildTarget).to.be.a('string'); + if (data.distributionKind !== 'unpackaged') { + expect(data.buildTime).to.be.a('string'); + expect(data.gitVersion).to.be.a('string'); + } else { + expect(data.buildTime).to.equal(null); + expect(data.gitVersion).to.equal(null); + } + }); + }); + describe('--nodb', () => { let shell: TestShell; beforeEach(async() => { From f4259d541a8976d3cc964644b8db99530cf831a8 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 15 Jul 2021 17:43:49 +0200 Subject: [PATCH 2/4] fixup --- packages/cli-repl/src/cli-repl.ts | 2 +- packages/cli-repl/src/run.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli-repl/src/cli-repl.ts b/packages/cli-repl/src/cli-repl.ts index 606603841e..e297b9c6c1 100644 --- a/packages/cli-repl/src/cli-repl.ts +++ b/packages/cli-repl/src/cli-repl.ts @@ -150,7 +150,7 @@ class CliRepl { } this.analytics = new Analytics( // analytics-config.js gets written as a part of a release - this.analyticsOptions?.apiKey ?? require('./buildinfo.json').segmentApiKey, + this.analyticsOptions?.apiKey ?? require('./build-info.json').segmentApiKey, this.analyticsOptions); return this.analytics; }); diff --git a/packages/cli-repl/src/run.ts b/packages/cli-repl/src/run.ts index 2296455962..4d22a79b44 100644 --- a/packages/cli-repl/src/run.ts +++ b/packages/cli-repl/src/run.ts @@ -28,7 +28,7 @@ import os from 'os'; console.log(version); } else if (options.buildInfo) { if (process.execPath === process.argv[1]) { - const buildInfo = require('./buildinfo.json'); + const buildInfo = require('./build-info.json'); delete buildInfo.segmentApiKey; // eslint-disable-next-line no-console console.log(JSON.stringify(buildInfo, null, ' ')); From d33fc0f5742d19d82d8a25c3f4f6d7cf1e64bfd3 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 15 Jul 2021 17:57:08 +0200 Subject: [PATCH 3/4] fixup --- packages/build/src/build-info.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/build/src/build-info.ts b/packages/build/src/build-info.ts index e90c019d26..b961068026 100644 --- a/packages/build/src/build-info.ts +++ b/packages/build/src/build-info.ts @@ -17,6 +17,7 @@ export async function writeBuildInfo(config: Config, distributionKind: 'compiled const info = { segmentApiKey: config.segmentKey, + version: config.version, distributionKind, buildArch: os.arch(), buildPlatform: os.platform(), From dc3b5bd439e9789cfc04178f4ad2613306c99f42 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 15 Jul 2021 18:36:31 +0200 Subject: [PATCH 4/4] fixup --- packages/build/src/compile/generate-bundle.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/build/src/compile/generate-bundle.spec.ts b/packages/build/src/compile/generate-bundle.spec.ts index ad89dd1648..5a2ddfa293 100644 --- a/packages/build/src/compile/generate-bundle.spec.ts +++ b/packages/build/src/compile/generate-bundle.spec.ts @@ -1,6 +1,7 @@ import { expect } from 'chai'; import childProcess from 'child_process'; import { promises as fs } from 'fs'; +import os from 'os'; import path from 'path'; import rimraf from 'rimraf'; import { promisify } from 'util';