From 75a8aedc32a7521b14580c2ee93f49b6e9cf3c5f Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 10 Mar 2022 12:31:50 -0330 Subject: [PATCH] Derive version suffix from build type and version (#13895) The version of a build is now derived from both the `version` field in `package.json` and the requested build type and version. The build type and version are added onto the manifest version as a suffix, according to the SemVer prerelease format. We already have support in the extension for versions of this format, but to apply a Flask or Beta version required manual updates to `package.json`. Now it can be done just with build arguments. A `get-version` module was created to make it easier to generate the version in the various places we do that during the build. It was created in the `development/lib` directory because it will be used by other non-build development scripts in a future PR. The `BuildType` constant was extracted to its own module as well, and moved to the `development/lib` directory. This was to make it clear that it's used by various different development scripts, not just the build. --- development/build/etc.js | 12 ++++++---- development/build/index.js | 24 ++++++++++++++++--- development/build/manifest.js | 2 +- development/build/scripts.js | 15 ++++++++---- development/build/static.js | 2 +- .../build/transforms/remove-fenced-code.js | 2 +- .../transforms/remove-fenced-code.test.js | 2 +- development/build/utils.js | 17 +++---------- development/lib/build-type.js | 12 ++++++++++ development/lib/get-version.js | 21 ++++++++++++++++ 10 files changed, 79 insertions(+), 30 deletions(-) create mode 100644 development/lib/build-type.js create mode 100644 development/lib/get-version.js diff --git a/development/build/etc.js b/development/build/etc.js index fe30002d8..7b76e0b9c 100644 --- a/development/build/etc.js +++ b/development/build/etc.js @@ -4,13 +4,13 @@ const gulpZip = require('gulp-zip'); const del = require('del'); const pify = require('pify'); const pump = pify(require('pump')); -const { version } = require('../../package.json'); + +const { BuildType } = require('../lib/build-type'); const { createTask, composeParallel } = require('./task'); -const { BuildType } = require('./utils'); module.exports = createEtcTasks; -function createEtcTasks({ browserPlatforms, buildType, livereload }) { +function createEtcTasks({ browserPlatforms, buildType, livereload, version }) { const clean = createTask('clean', async function clean() { await del(['./dist/*']); await Promise.all( @@ -28,14 +28,16 @@ function createEtcTasks({ browserPlatforms, buildType, livereload }) { const zip = createTask( 'zip', composeParallel( - ...browserPlatforms.map((platform) => createZipTask(platform, buildType)), + ...browserPlatforms.map((platform) => + createZipTask(platform, buildType, version), + ), ), ); return { clean, reload, zip }; } -function createZipTask(platform, buildType) { +function createZipTask(platform, buildType, version) { return async () => { const path = buildType === BuildType.main diff --git a/development/build/index.js b/development/build/index.js index 987d99854..dd0e7b4c9 100755 --- a/development/build/index.js +++ b/development/build/index.js @@ -7,6 +7,8 @@ const path = require('path'); const livereload = require('gulp-livereload'); const minimist = require('minimist'); const { sync: globby } = require('globby'); +const { getVersion } = require('../lib/get-version'); +const { BuildType } = require('../lib/build-type'); const { createTask, composeSeries, @@ -18,7 +20,7 @@ const createScriptTasks = require('./scripts'); const createStyleTasks = require('./styles'); const createStaticAssetTasks = require('./static'); const createEtcTasks = require('./etc'); -const { BuildType, getBrowserVersionMap } = require('./utils'); +const { getBrowserVersionMap } = require('./utils'); // Packages required dynamically via browserify configuration in dependencies // Required for LavaMoat policy generation @@ -58,11 +60,12 @@ function defineAndRunBuildTasks() { shouldIncludeLockdown, shouldLintFenceFiles, skipStats, + version, } = parseArgv(); const browserPlatforms = ['firefox', 'chrome', 'brave', 'opera']; - const browserVersionMap = getBrowserVersionMap(browserPlatforms); + const browserVersionMap = getBrowserVersionMap(browserPlatforms, version); const ignoredFiles = getIgnoredFiles(buildType); @@ -89,12 +92,14 @@ function defineAndRunBuildTasks() { livereload, policyOnly, shouldLintFenceFiles, + version, }); const { clean, reload, zip } = createEtcTasks({ livereload, browserPlatforms, buildType, + version, }); // build for development (livereload) @@ -162,6 +167,7 @@ function defineAndRunBuildTasks() { function parseArgv() { const NamedArgs = { BuildType: 'build-type', + BuildVersion: 'build-version', LintFenceFiles: 'lint-fence-files', Lockdown: 'lockdown', PolicyOnly: 'policy-only', @@ -175,9 +181,10 @@ function parseArgv() { NamedArgs.PolicyOnly, NamedArgs.SkipStats, ], - string: [NamedArgs.BuildType], + string: [NamedArgs.BuildType, NamedArgs.BuildVersion], default: { [NamedArgs.BuildType]: BuildType.main, + [NamedArgs.BuildVersion]: '0', [NamedArgs.LintFenceFiles]: true, [NamedArgs.Lockdown]: true, [NamedArgs.PolicyOnly]: false, @@ -201,6 +208,14 @@ function parseArgv() { throw new Error(`MetaMask build: Invalid build type: "${buildType}"`); } + const rawBuildVersion = argv[NamedArgs.BuildVersion]; + const buildVersion = Number.parseInt(rawBuildVersion, 10); + if (rawBuildVersion.match(/^\d+$/u) === null || Number.isNaN(buildVersion)) { + throw new Error( + `MetaMask build: Invalid build version: "${rawBuildVersion}"`, + ); + } + // Manually default this to `false` for dev builds only. const shouldLintFenceFiles = process.argv.includes( `--${NamedArgs.LintFenceFiles}`, @@ -210,6 +225,8 @@ function parseArgv() { const policyOnly = argv[NamedArgs.PolicyOnly]; + const version = getVersion(buildType, buildVersion); + return { buildType, entryTask, @@ -218,6 +235,7 @@ function parseArgv() { shouldIncludeLockdown: argv[NamedArgs.Lockdown], shouldLintFenceFiles, skipStats: argv[NamedArgs.SkipStats], + version, }; } diff --git a/development/build/manifest.js b/development/build/manifest.js index 524f2ef3b..498ab2785 100644 --- a/development/build/manifest.js +++ b/development/build/manifest.js @@ -3,9 +3,9 @@ const path = require('path'); const { mergeWith, cloneDeep } = require('lodash'); const baseManifest = require('../../app/manifest/_base.json'); +const { BuildType } = require('../lib/build-type'); const { createTask, composeSeries } = require('./task'); -const { BuildType } = require('./utils'); module.exports = createManifestTasks; diff --git a/development/build/scripts.js b/development/build/scripts.js index e5e36a863..063fc5a7e 100644 --- a/development/build/scripts.js +++ b/development/build/scripts.js @@ -46,7 +46,7 @@ const metamaskrc = require('rc')('metamask', { }); const { streamFlatMap } = require('../stream-flat-map.js'); -const { version } = require('../../package.json'); +const { BuildType } = require('../lib/build-type'); const { createTask, @@ -57,7 +57,6 @@ const { const { createRemoveFencedCodeTransform, } = require('./transforms/remove-fenced-code'); -const { BuildType } = require('./utils'); /** * The build environment. This describes the environment this build was produced in. @@ -148,6 +147,7 @@ function createScriptTasks({ livereload, shouldLintFenceFiles, policyOnly, + version, }) { // internal tasks const core = { @@ -193,6 +193,7 @@ function createScriptTasks({ policyOnly, shouldLintFenceFiles, testing, + version, }), ); @@ -345,6 +346,7 @@ function createFactoredBuild({ policyOnly, shouldLintFenceFiles, testing, + version, }) { return async function () { // create bundler setup and apply defaults @@ -356,7 +358,12 @@ function createFactoredBuild({ const reloadOnChange = Boolean(devMode); const minify = Boolean(devMode) === false; - const envVars = getEnvironmentVariables({ buildType, devMode, testing }); + const envVars = getEnvironmentVariables({ + buildType, + devMode, + testing, + version, + }); setupBundlerDefaults(buildConfiguration, { buildType, devMode, @@ -781,7 +788,7 @@ async function bundleIt(buildConfiguration, { reloadOnChange }) { } } -function getEnvironmentVariables({ buildType, devMode, testing }) { +function getEnvironmentVariables({ buildType, devMode, testing, version }) { const environment = getEnvironment({ devMode, testing }); if (environment === ENVIRONMENT.PRODUCTION && !process.env.SENTRY_DSN) { throw new Error('Missing SENTRY_DSN environment variable'); diff --git a/development/build/static.js b/development/build/static.js index 1ec935502..7efde5a72 100644 --- a/development/build/static.js +++ b/development/build/static.js @@ -4,9 +4,9 @@ const watch = require('gulp-watch'); const glob = require('fast-glob'); const locales = require('../../app/_locales/index.json'); +const { BuildType } = require('../lib/build-type'); const { createTask, composeSeries } = require('./task'); -const { BuildType } = require('./utils'); const EMPTY_JS_FILE = './development/empty.js'; diff --git a/development/build/transforms/remove-fenced-code.js b/development/build/transforms/remove-fenced-code.js index 71236e179..90d49972e 100644 --- a/development/build/transforms/remove-fenced-code.js +++ b/development/build/transforms/remove-fenced-code.js @@ -1,6 +1,6 @@ const path = require('path'); const { PassThrough, Transform } = require('stream'); -const { BuildType } = require('../utils'); +const { BuildType } = require('../../lib/build-type'); const { lintTransformedFile } = require('./utils'); const hasKey = (obj, key) => Reflect.hasOwnProperty.call(obj, key); diff --git a/development/build/transforms/remove-fenced-code.test.js b/development/build/transforms/remove-fenced-code.test.js index b3ff511e3..3be3e401a 100644 --- a/development/build/transforms/remove-fenced-code.test.js +++ b/development/build/transforms/remove-fenced-code.test.js @@ -1,5 +1,5 @@ const deepFreeze = require('deep-freeze-strict'); -const { BuildType } = require('../utils'); +const { BuildType } = require('../../lib/build-type'); const { createRemoveFencedCodeTransform, removeFencedCode, diff --git a/development/build/utils.js b/development/build/utils.js index be33bf374..bd427d6bb 100644 --- a/development/build/utils.js +++ b/development/build/utils.js @@ -1,16 +1,5 @@ const semver = require('semver'); -const { version } = require('../../package.json'); - -/** - * The distribution this build is intended for. - * - * This should be kept in-sync with the `BuildType` map in `shared/constants/app.js`. - */ -const BuildType = { - beta: 'beta', - flask: 'flask', - main: 'main', -}; +const { BuildType } = require('../lib/build-type'); /** * Map the current version to a format that is compatible with each browser. @@ -20,11 +9,12 @@ const BuildType = { * where the build version is a positive integer. * * @param {string[]} platforms - A list of browsers to generate versions for. + * @param {string} version - The current version. * @returns {Object} An object with the browser as the key and the browser-specific version object * as the value. For example, the version `9.6.0-beta.1` would return the object * `{ firefox: { version: '9.6.0.beta1' }, chrome: { version: '9.6.0.1', version_name: '9.6.0-beta.1' } }`. */ -function getBrowserVersionMap(platforms) { +function getBrowserVersionMap(platforms, version) { const major = semver.major(version); const minor = semver.minor(version); const patch = semver.patch(version); @@ -62,6 +52,5 @@ function getBrowserVersionMap(platforms) { } module.exports = { - BuildType, getBrowserVersionMap, }; diff --git a/development/lib/build-type.js b/development/lib/build-type.js new file mode 100644 index 000000000..531ed18e9 --- /dev/null +++ b/development/lib/build-type.js @@ -0,0 +1,12 @@ +/** + * The distribution this build is intended for. + * + * This should be kept in-sync with the `BuildType` map in `shared/constants/app.js`. + */ +const BuildType = { + beta: 'beta', + flask: 'flask', + main: 'main', +}; + +module.exports = { BuildType }; diff --git a/development/lib/get-version.js b/development/lib/get-version.js new file mode 100644 index 000000000..92119c352 --- /dev/null +++ b/development/lib/get-version.js @@ -0,0 +1,21 @@ +const { version: manifestVersion } = require('../../package.json'); +const { BuildType } = require('./build-type'); + +/** + * Get the current version of the MetaMask extension. The base manifest version + * is modified according to the build type and version. + * + * The build version is needed because certain build types (such as beta) may + * be released multiple times during the release process. + * + * @param {BuildType} buildType - The build type. + * @param {number} buildVersion - The build version. + * @returns {string} The MetaMask extension version. + */ +function getVersion(buildType, buildVersion) { + return buildType === BuildType.main + ? manifestVersion + : `${manifestVersion}-${buildType}.${buildVersion}`; +} + +module.exports = { getVersion };