From 6aaeab2f242b0c28aa0663a5a17cf7a1b55703fb Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Tue, 15 Mar 2022 08:54:37 -0230 Subject: [PATCH] Automate the Flask release process (#13898) * Automate the Flask release A Flask release will now be published alongside each main extension release. The version of each Flask release will be the same as the extension version except it will have the suffix `-flask.0`. * Programmatically remove build prefix The create GH release Bash script derives the Flask version from the Flask build filename by removing the build prefix, leaving just the version. Rather than hard-coding the prefix size to remove, it is now calculated programmatically so that it is easier to read and update. * Fix tag publishing The tab publishing step used the wrong credentials, and didn't properly identify the commit author. This has now been fixed. --- .circleci/config.yml | 10 ++- .../scripts/release-create-gh-release.sh | 46 ++++++++--- development/metamaskbot-build-announce.js | 2 +- development/sentry-publish.js | 77 ++++++++++++++----- development/sentry-upload-artifacts.sh | 17 +++- 5 files changed, 118 insertions(+), 34 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b8666d879..76992feca 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -160,6 +160,7 @@ workflows: requires: - prep-deps - prep-build + - prep-build-flask - all-tests-pass - job-publish-storybook: filters: @@ -371,7 +372,7 @@ jobs: root: . paths: - storybook-build - + test-storybook: executor: node-browsers steps: @@ -718,8 +719,11 @@ jobs: - attach_workspace: at: . - run: - name: sentry sourcemaps upload - command: SENTRY_ORG=metamask SENTRY_PROJECT=metamask yarn sentry:publish + name: Publish main release to Sentry + command: yarn sentry:publish + - run: + name: Publish Flask release to Sentry + command: yarn sentry:publish --build-type flask - run: name: Create GitHub release command: | diff --git a/.circleci/scripts/release-create-gh-release.sh b/.circleci/scripts/release-create-gh-release.sh index 93303f576..54d3946a9 100755 --- a/.circleci/scripts/release-create-gh-release.sh +++ b/.circleci/scripts/release-create-gh-release.sh @@ -26,25 +26,53 @@ function install_github_cli () popd } +function print_flask_version () +{ + local flask_filename + flask_filename="$(find ./builds-flask -type f -name 'metamask-flask-chrome-*.zip' -exec basename {} .zip \;)" + + local flask_build_filename_prefix + flask_build_filename_prefix='metamask-flask-chrome-' + local flask_build_filename_prefix_size + flask_build_filename_prefix_size="${#flask_build_filename_prefix}" + + # Use substring parameter expansion to remove the filename prefix, leaving just the version + echo "${flask_filename:$flask_build_filename_prefix_size}" +} + +function publish_flask_tag () +{ + local flask_version="${1}"; shift + + git config user.email "metamaskbot@users.noreply.github.com" + git config user.name "MetaMask Bot" + git tag -a "v${flask_version}" -m "Flask version ${flask_version}" + repo_slug="$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME" + git push "https://$GITHUB_TOKEN@github.com/$repo_slug" "v${flask_version}" +} + current_commit_msg=$(git show -s --format='%s' HEAD) if [[ $current_commit_msg =~ Version[-[:space:]](v[[:digit:]]+.[[:digit:]]+.[[:digit:]]+) ]] then tag="${BASH_REMATCH[1]}" + flask_version="$(print_flask_version)" install_github_cli printf '%s\n' 'Creating GitHub Release' release_body="$(awk -v version="${tag##v}" -f .circleci/scripts/show-changelog.awk CHANGELOG.md)" - pushd builds - hub release create \ - --attach metamask-chrome-*.zip \ - --attach metamask-firefox-*.zip \ - --message "Version ${tag##v}" \ - --message "$release_body" \ - --commitish "$CIRCLE_SHA1" \ - "$tag" - popd + hub release create \ + --attach builds/metamask-chrome-*.zip \ + --attach builds/metamask-firefox-*.zip \ + --attach builds-flask/metamask-flask-chrome-*.zip \ + --attach builds-flask/metamask-flask-firefox-*.zip \ + --message "Version ${tag##v}" \ + --message "$release_body" \ + --commitish "$CIRCLE_SHA1" \ + "$tag" + + publish_flask_tag "${flask_version}" else printf '%s\n' 'Version not found in commit message; skipping GitHub Release' exit 0 diff --git a/development/metamaskbot-build-announce.js b/development/metamaskbot-build-announce.js index 2d09a93d1..8f1a0bece 100755 --- a/development/metamaskbot-build-announce.js +++ b/development/metamaskbot-build-announce.js @@ -3,7 +3,7 @@ const { promises: fs } = require('fs'); const path = require('path'); const fetch = require('node-fetch'); const glob = require('fast-glob'); -const VERSION = require('../dist/chrome/manifest.json').version; // eslint-disable-line import/no-unresolved +const VERSION = require('../package.json').version; const { getHighlights } = require('./highlights'); start().catch(console.error); diff --git a/development/sentry-publish.js b/development/sentry-publish.js index 81fa58790..5a988c65b 100644 --- a/development/sentry-publish.js +++ b/development/sentry-publish.js @@ -1,6 +1,11 @@ #!/usr/bin/env node -const VERSION = require('../dist/chrome/manifest.json').version; // eslint-disable-line import/no-unresolved + +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); + const { runCommand, runInShell } = require('./lib/run-command'); +const { getVersion } = require('./lib/get-version'); +const { BuildType } = require('./lib/build-type'); start().catch((error) => { console.error(error); @@ -8,34 +13,63 @@ start().catch((error) => { }); async function start() { - if (!process.env.SENTRY_ORG) { - throw new Error('Missing required "SENTRY_ORG" environment variable'); - } else if (!process.env.SENTRY_PROJECT) { - throw new Error('Missing required "SENTRY_PROJECT" environment variable'); - } + const { argv } = yargs(hideBin(process.argv)).usage( + '$0 [options]', + 'Publish a release to Sentry', + (_yargs) => + _yargs + .option('org', { + default: 'metamask', + description: 'The Sentry organization', + type: 'string', + }) + .option('project', { + default: 'metamask', + description: 'The Sentry project to publish', + type: 'string', + }) + .option('build-type', { + default: BuildType.main, + description: 'The MetaMask extension build type', + choices: Object.values(BuildType), + }) + .option('build-version', { + default: 0, + description: 'The MetaMask extension build version', + type: 'number', + }), + ); + + const { buildType, buildVersion, org, project } = argv; + + process.env.SENTRY_ORG = org; + process.env.SENTRY_PROJECT = project; const authWorked = await checkIfAuthWorks(); if (!authWorked) { throw new Error(`Sentry auth failed`); } + + const version = getVersion(buildType, buildVersion); + // check if version exists or not - const versionAlreadyExists = await checkIfVersionExists(); + const versionAlreadyExists = await checkIfVersionExists(version); // abort if versions exists if (versionAlreadyExists) { console.log( - `Version "${VERSION}" already exists on Sentry, skipping version creation`, + `Version "${version}" already exists on Sentry, skipping version creation`, ); } else { // create sentry release - console.log(`creating Sentry release for "${VERSION}"...`); - await runCommand('sentry-cli', ['releases', 'new', VERSION]); + console.log(`creating Sentry release for "${version}"...`); + await runCommand('sentry-cli', ['releases', 'new', version]); console.log( - `removing any existing files from Sentry release "${VERSION}"...`, + `removing any existing files from Sentry release "${version}"...`, ); await runCommand('sentry-cli', [ 'releases', 'files', - VERSION, + version, 'delete', '--all', ]); @@ -43,18 +77,23 @@ async function start() { // check if version has artifacts or not const versionHasArtifacts = - versionAlreadyExists && (await checkIfVersionHasArtifacts()); + versionAlreadyExists && (await checkIfVersionHasArtifacts(version)); if (versionHasArtifacts) { console.log( - `Version "${VERSION}" already has artifacts on Sentry, skipping sourcemap upload`, + `Version "${version}" already has artifacts on Sentry, skipping sourcemap upload`, ); return; } + const additionalUploadArgs = []; + if (buildType !== BuildType.main) { + additionalUploadArgs.push('--dist-directory', `dist-${buildType}`); + } // upload sentry source and sourcemaps await runInShell('./development/sentry-upload-artifacts.sh', [ '--release', - VERSION, + version, + ...additionalUploadArgs, ]); } @@ -64,17 +103,17 @@ async function checkIfAuthWorks() { ); } -async function checkIfVersionExists() { +async function checkIfVersionExists(version) { return await doesNotFail(() => - runCommand('sentry-cli', ['releases', 'info', VERSION]), + runCommand('sentry-cli', ['releases', 'info', version]), ); } -async function checkIfVersionHasArtifacts() { +async function checkIfVersionHasArtifacts(version) { const [artifact] = await runCommand('sentry-cli', [ 'releases', 'files', - VERSION, + version, 'list', ]); // When there's no artifacts, we get a response from the shell like this ['', ''] diff --git a/development/sentry-upload-artifacts.sh b/development/sentry-upload-artifacts.sh index 673e1dd73..9d2fd32b4 100755 --- a/development/sentry-upload-artifacts.sh +++ b/development/sentry-upload-artifacts.sh @@ -23,17 +23,20 @@ Upload JavaScript bundles and sourcemaps to Sentry Options: -h, --help Show help text -r, --release Sentry release to upload files to (defaults to 'VERSION' environment variable) + --dist-directory The 'dist' directory to use. Defaults to 'dist'. EOF } function upload_sourcemaps { local release="${1}"; shift + local dist_directory="${1}"; shift - sentry-cli releases files "${release}" upload-sourcemaps ./dist/chrome/*.js ./dist/sourcemaps/ --rewrite --url-prefix 'metamask' + sentry-cli releases files "${release}" upload-sourcemaps "${dist_directory}"/chrome/*.js "${dist_directory}"/sourcemaps/ --rewrite --url-prefix 'metamask' } function main { local release=VERSION + local dist_directory='dist' while :; do case "${1-default}" in @@ -51,6 +54,16 @@ function main { release="${2}" shift ;; + --dist-directory) + if [[ -z $2 ]] + then + printf "'dist-directory' option requires an argument.\\n" >&2 + printf '%s\n' "${__SEE_HELP_MESSAGE__}" >&2 + exit 1 + fi + dist_directory="${2}" + shift + ;; *) break esac @@ -70,7 +83,7 @@ function main { fi printf 'uploading source files and sourcemaps for Sentry release "%s"...\n' "${release}" - upload_sourcemaps "${release}" + upload_sourcemaps "${release}" "${dist_directory}" printf 'all done!\n' }