Skip to content

Commit

Permalink
Break up release.js (elastic#3723)
Browse files Browse the repository at this point in the history
* Switch release.js to use arguments instead of env vars

* Switch MFA code back to env var so it doesn't leak in CI logs

* Update job definition to use --type arg

* Support breaking up release steps with args

* Break release up to fetch time-sensitive MFA token right before publish

* Strip whitespace from each step

Co-authored-by: Chandler Prall <chandler.prall@gmail.com>

Co-authored-by: Chandler Prall <chandler.prall@gmail.com>
  • Loading branch information
2 people authored and anishagg17 committed Jul 20, 2020
1 parent 6b180ec commit 936a8a7
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 35 deletions.
5 changes: 3 additions & 2 deletions .ci/jobs/elastic+eui+npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
npm install -g yarn
yarn
npm run release -- --type=${version_type} --steps=test,build,version,tag
set +x
export VAULT_TOKEN=$(vault write -field=token auth/approle/login role_id="$VAULT_ROLE_ID" secret_id="$VAULT_SECRET_ID")
Expand All @@ -49,5 +51,4 @@
set -x
export VERSION_TYPE=${version_type}
npm run release
npm run release -- --type=${version_type} --steps=publish,docs
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"@types/uuid": "^3.4.4",
"@typescript-eslint/eslint-plugin": "^3.2.0",
"@typescript-eslint/parser": "^3.2.0",
"argparse": "^1.0.10",
"autoprefixer": "^7.1.5",
"axe-core": "^3.5.4",
"axe-puppeteer": "^1.1.0",
Expand Down
121 changes: 88 additions & 33 deletions scripts/release.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const argparse = require('argparse');
const chalk = require('chalk');
const fs = require('fs');
const git = require('nodegit');
const path = require('path');
const prompt = require('prompt');
const { execSync } = require('child_process');
let { execSync } = require('child_process');

const cwd = path.resolve(__dirname, '..');
const stdio = 'inherit';
Expand All @@ -18,6 +19,15 @@ const humanReadableTypes = {
[TYPE_PATCH]: 'patch'
};

const args = parseArguments();

if (args.dry_run) {
console.warn(chalk.yellow('Dry run mode: no changes will be pushed to npm or Github'));
execSync = function() {
console.log.apply(null, arguments);
};
}

(async function () {
// make sure the release script is being run by npm (required for `npm publish` step)
// https://github.com/yarnpkg/yarn/issues/5063
Expand All @@ -31,35 +41,87 @@ const humanReadableTypes = {
await ensureMasterBranch();

// run linting and unit tests
execSync('npm test', execOptions);
if (args.steps.indexOf('test') > -1) {
execSync('npm test', execOptions);
}

// (trans|com)pile `src` into `lib` and `dist`
execSync('npm run build', execOptions);
if (args.steps.indexOf('build') > -1) {
execSync('npm run build', execOptions);
}

// prompt user for what type of version bump to make (major|minor|patch)
const versionTarget = await getVersionTypeFromChangelog();

if (args.steps.indexOf('version') > -1) {
// prompt user for what type of version bump to make (major|minor|patch)
const versionTarget = await getVersionTypeFromChangelog();

// build may have generated a new src-docs/src/i18ntokens.json file, dirtying the git workspace
// it's important to track those changes with this release, so determine the changes and write them
// to src-docs/src/i18ntokens_changelog.json, comitting both to the workspace before running `npm version`
execSync(`npm run update-token-changelog -- ${versionTarget}`, execOptions);
// build may have generated a new src-docs/src/i18ntokens.json file, dirtying the git workspace
// it's important to track those changes with this release, so determine the changes and write them
// to src-docs/src/i18ntokens_changelog.json, comitting both to the workspace before running `npm version`
execSync(`npm run update-token-changelog -- ${versionTarget}`, execOptions);

// update package.json & package-lock.json version, git commit, git tag
execSync(`npm version ${versionTarget}`, execOptions);
// update package.json & package-lock.json version, git commit, git tag
execSync(`npm version ${versionTarget}`, execOptions);
}

// push the version commit & tag to upstream
execSync('git push upstream --tags', execOptions);
if (args.steps.indexOf('tag') > -1) {
// push the version commit & tag to upstream
execSync('git push upstream --tags', execOptions);
}

// prompt user for npm 2FA
const otp = await getOneTimePassword();
if (args.steps.indexOf('publish') > -1) {
// prompt user for npm 2FA
const otp = await getOneTimePassword();

// publish new version to npm
execSync(`npm publish --otp=${otp}`, execOptions);
// publish new version to npm
execSync(`npm publish --otp=${otp}`, execOptions);
}

// update docs, git commit, git push
execSync('npm run sync-docs', execOptions);
if (args.steps.indexOf('docs') > -1) {
// update docs, git commit, git push
execSync('npm run sync-docs', execOptions);
}
}()).catch(e => console.error(e));

function parseArguments() {
const parser = new argparse.ArgumentParser({
addHelp: true,
description: 'Tag and publish a new version of EUI',
});

parser.addArgument('--type', {
help: 'Version type; can be "major", "minor" or "patch"',
choices: Object.values(humanReadableTypes),
});

parser.addArgument('--dry-run', {
action: 'storeTrue',
defaultValue: false,
help: 'Dry run mode; no changes are made',
});

const allSteps = ['test', 'build', 'version', 'tag', 'publish', 'docs'];
parser.addArgument('--steps', {
help: 'Which release steps to run; a comma-separated list of values that can include "test", "build", "version", "tag", "publish" and "docs". If no value is given, all steps are run. Example: --steps=test,build,version,tag',
defaultValue: allSteps.join(','),
});

const args = parser.parseArgs();

// validate --steps argument
const steps = args.steps.split(',').map(step => step.trim());
const diff = steps.filter(x => allSteps.indexOf(x) === -1);
if (diff.length > 0) {
console.error(`Invalid --step value(s): ${diff.join(', ')}`);
process.exit(1);
}

return {
...args,
steps,
};
}

async function ensureMasterBranch() {
// ignore master check in CI since it's checking out the HEAD commit instead
if (process.env.CI === 'true') {
Expand Down Expand Up @@ -131,23 +193,17 @@ async function getVersionTypeFromChangelog() {
console.log('');
console.log(`${chalk.magenta('The recommended version update for these changes is')} ${chalk.blue(humanReadableRecommendation)}`);

// checking for VERSION_TYPE environment variable, which overrides prompts to
// the user to choose a version type; this is used by CI to automate releases
const envVersion = process.env.VERSION_TYPE;
if (envVersion) {
// checking for --type argument value; used by CI to automate releases
const versionType = args.type;
if (versionType) {
// detected version type preference set
console.log(`${chalk.magenta('VERSION_TYPE environment variable identifed, set to')} ${chalk.blue(envVersion)}`);

if (['major', 'minor', 'patch'].indexOf(envVersion) === -1) {
console.error(`${chalk.magenta('VERSION_TYPE environment variable is not "major", "minor" or "patch"')}`);
process.exit(1);
}
console.log(`${chalk.magenta('--type argument identifed, set to')} ${chalk.blue(versionType)}`);

if (envVersion !== recommendedType) {
console.warn(`${chalk.yellow('WARNING: VERSION_TYPE does not match recommended version update')}`);
if (versionType !== humanReadableRecommendation) {
console.warn(`${chalk.yellow('WARNING: --type argument does not match recommended version update')}`);
}

return envVersion;
return versionType;
} else {
console.log(`${chalk.magenta('What part of the package version do you want to bump?')} ${chalk.gray('(major, minor, patch)')}`);

Expand Down Expand Up @@ -189,7 +245,6 @@ async function getOneTimePassword() {
console.log(chalk.magenta('The @elastic organization requires membership and 2FA to publish'));

if (process.env.NPM_OTP) {
// skip prompting user for manual input if NPM_OTP env var is present
console.log(chalk.magenta('2FA code provided by NPM_OTP environment variable'));
return process.env.NPM_OTP;
}
Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2300,6 +2300,13 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"

argparse@^1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"

argparse@^1.0.7:
version "1.0.9"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
Expand Down

0 comments on commit 936a8a7

Please sign in to comment.