scripts/package.ts (119 lines of code) (raw):

/*! * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ // // Creates an artifact that can be given to users for testing alpha/beta builds: // // aws-toolkit-vscode-99.0.0-gxxxxxxx.vsix // or amazon-q-vscode-99.0.0-gxxxxxxx.vsix // // Where `xxxxxxx` is the first 7 characters of the commit hash that produced the artifact. // // The script works like this: // 1. temporarily change `version` in package.json // 2. invoke `vsce package` // 3. restore the original package.json // import * as child_process from 'child_process' // eslint-disable-line no-restricted-imports import * as nodefs from 'fs' // eslint-disable-line no-restricted-imports import * as path from 'path' function parseArgs() { // Invoking this script with argument "foo": // $ npm run package -- foo // yields this argv: // 0: /…/node_modules/.bin/ts-node // 1: /…/src/scripts/build/package.ts // 2: foo const args: { [key: string]: any } = { /** Produce an unoptimized VSIX. Include git SHA in version string. */ debug: false, /** Skips `npm run clean` when building the VSIX. This prevents file watching from breaking. */ skipClean: false, feature: '', } const givenArgs = process.argv.slice(2) const validOptions = ['--debug', '--no-clean', '--feature'] const expectValue = ['--feature'] for (let i = 0; i < givenArgs.length; i++) { const a = givenArgs[i] const argName = a.replace(/^-+/, '') // "--foo" => "foo" if (!validOptions.includes(a)) { throw Error(`invalid argument: ${a}`) } if (expectValue.includes(a)) { i++ const val = givenArgs[i] if (val === undefined) { throw Error(`missing value for arg: ${a}`) } args[argName] = val } else { args[argName] = true } } return args } /** * If the _current_ commit is tagged as a release ("v1.26.0") then it is a "release build", else it * is a prerelease/nightly/edge/preview build. */ function isRelease(): boolean { const tag = child_process.execFileSync('git', ['tag', '-l', '--contains', 'HEAD']).toString() return !!tag?.match(/v\d+\.\d+\.\d+/) } /** * Whether or not this a private beta build */ function isBeta(): boolean { try { // This path only exists for packages/toolkit. // As noted before: "Importing from `src` isn't great but it does make things simple" // TODO: Generalize betaUrl for all packages. const betaUrl = require(path.resolve('./src/dev/config')).betaUrl return !!betaUrl } catch { return false } } /** * Gets a suffix to append to the version-string, or empty for release builds. * * TODO: use `git describe` instead. * * @returns version-string suffix, for example: "-e6ecd84685a9" */ function getVersionSuffix(feature: string, debug: boolean): string { if (isRelease()) { return '' } const debugSuffix = debug ? '-debug' : '' const featureSuffix = feature === '' ? '' : `-${feature}` const commitId = child_process.execFileSync('git', ['rev-parse', '--short=7', 'HEAD']).toString().trim() // Commit id is prefixed with "g" because "-0abc123" is not a valid semver prerelease, and will cause vsce to fail. const commitSuffix = commitId ? `-g${commitId}` : '' return `${debugSuffix}${featureSuffix}${commitSuffix}` } function main() { const args = parseArgs() // It is expected that this will package from a packages/{subproject} folder. // There is a base config in packages/ const packageJsonFile = './package.json' const backupJsonFile = `${packageJsonFile}.package.bk` const webpackConfigJsFile = '../webpack.base.config.js' const backupWebpackConfigFile = `${webpackConfigJsFile}.package.bk` if (!nodefs.existsSync(packageJsonFile)) { throw new Error(`package.json not found, cannot package this directory: ${process.cwd()}`) } let release = true try { release = isRelease() if (release && isBeta()) { throw new Error('Cannot package VSIX as both a release and a beta simultaneously') } // Create backup file so we can restore the originals later. nodefs.copyFileSync(packageJsonFile, backupJsonFile) const packageJson = JSON.parse(nodefs.readFileSync(packageJsonFile, { encoding: 'utf-8' })) if (!release || args.debug) { const versionSuffix = getVersionSuffix(args.feature, args.debug) const version = packageJson.version if (isBeta()) { // Declare an arbitrarily high version number, to stop VSC from auto-updating "beta" builds. packageJson.version = `99.0.0${versionSuffix}` } else { packageJson.version = version.replace('-SNAPSHOT', versionSuffix) } if (args.skipClean) { // Clearly we need `prepublish` to be a standalone script and not a bunch of `npm` commands const prepublish = packageJson.scripts['vscode:prepublish'] const replaced = prepublish.replace('npm run clean', 'echo "Skipped clean"') packageJson.scripts['vscode:prepublish'] = replaced } if (args.debug) { nodefs.copyFileSync(webpackConfigJsFile, backupWebpackConfigFile) const webpackConfigJs = nodefs.readFileSync(webpackConfigJsFile, { encoding: 'utf-8' }) nodefs.writeFileSync(webpackConfigJsFile, webpackConfigJs.replace(/minimize: true/, 'minimize: false')) } } nodefs.writeFileSync(packageJsonFile, JSON.stringify(packageJson, undefined, ' ')) child_process.execFileSync( 'vsce', [ 'package', '--ignoreFile', '../.vscodeignore.packages', /** * Depdendency gathering not required because we bundle with webpack: https://github.com/microsoft/vscode-vsce/issues/439 * * Removing this arg will cause packaging to break due to issues in src.gen/.../node_modules, * since those dependencies are disjoint (i.e. not a workspace in the root package.json) */ '--no-dependencies', ], { stdio: 'inherit', shell: process.platform === 'win32', // For vsce.cmd on Windows. } ) console.log(`VSIX Version: ${packageJson.version}`) // Hoist .vsix to root folder, because the release infra expects it to be there. // TODO: Once we can support releasing multiple artifacts, // let's just keep the .vsix in its respective project folder in packages/ const vsixName = `${packageJson.name}-${packageJson.version}.vsix` nodefs.renameSync(vsixName, `../../${vsixName}`) } catch (e) { console.log(e) throw Error('package.ts: failed') } finally { // Restore the original files. nodefs.copyFileSync(backupJsonFile, packageJsonFile) nodefs.unlinkSync(backupJsonFile) if (args.debug) { nodefs.copyFileSync(backupWebpackConfigFile, webpackConfigJsFile) nodefs.unlinkSync(backupWebpackConfigFile) } } } main()