From e4b5d3eec9bae44c46795c4be097b31cb48593ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ramos?= Date: Wed, 19 Oct 2022 17:54:22 -0700 Subject: [PATCH] Circle CI: Upload both tarballs to releases, dry-run the release workflow on every commit (#35015) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/35015 React Native releases are cut every few months. Without testing, the workflow is prone to breakage. Dry-run the release workflow on every commit in order to surface any issues as they are introduced instead of at release time. Fixed issues that surfaced during testing of this workflow: - Upload both Hermes tarballs Changelog: [internal] Reviewed By: mdvacca Differential Revision: D40483764 fbshipit-source-id: 5ca6bd4dcdfd64c24882ffb202edbfd701efd462 --- .circleci/config.yml | 95 +++++++++++--- .../__tests__/create_github_release_test.sh | 45 ------- scripts/circleci/create_github_release.sh | 123 ++++++++++++------ scripts/circleci/post-artifacts-link.sh | 1 + scripts/prepare-package-for-release.js | 17 ++- scripts/publish-npm.js | 4 + 6 files changed, 184 insertions(+), 101 deletions(-) delete mode 100755 scripts/__tests__/create_github_release_test.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 0bc5d3191015d2..49c899f1571d18 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1361,6 +1361,9 @@ jobs: latest: type: boolean default: false + dryrun: + type: boolean + default: false executor: reactnativeios steps: - checkout_code_with_cache @@ -1373,13 +1376,15 @@ jobs: - run: name: "Set new react-native version and commit changes" command: | - node ./scripts/prepare-package-for-release.js -v << parameters.version >> -l << parameters.latest >> + node ./scripts/prepare-package-for-release.js -v << parameters.version >> -l << parameters.latest >> --dry-run << parameters.dryrun >> build_npm_package: parameters: - publish_npm_args: - type: string - default: --dry-run + release_type: + description: The type of release to build. Must be one of "nightly", "release", "dry-run". + type: enum + enum: ["nightly", "release", "dry-run"] + default: "dry-run" executor: reactnativeandroid environment: - HERMES_WS_DIR: *hermes_workspace_root @@ -1422,8 +1427,8 @@ jobs: - when: condition: or: - - equal: [ --release, << parameters.publish_npm_args >> ] - - equal: [ --nightly, << parameters.publish_npm_args >> ] + - equal: [ "release", << parameters.release_type >> ] + - equal: [ "nightly", << parameters.release_type >> ] steps: - run: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc - run: | @@ -1432,7 +1437,7 @@ jobs: echo "machine github.com login react-native-bot password $GITHUB_TOKEN" > ~/.netrc # END: Stables and nightlies - - run: node ./scripts/publish-npm.js << parameters.publish_npm_args >> + - run: node ./scripts/publish-npm.js --<< parameters.release_type >> - run: name: Zip Hermes Native Symbols command: zip -r /tmp/hermes-native-symbols.zip ~/react-native/ReactAndroid/hermes-engine/build/intermediates/cmake/ @@ -1443,7 +1448,7 @@ jobs: # Provide a react-native package for this commit as a Circle CI release artifact. - when: condition: - equal: [ --dry-run, << parameters.publish_npm_args >> ] + equal: [ "dry-run", << parameters.release_type >> ] steps: - run: name: Build release package as a job artifact @@ -1475,7 +1480,7 @@ jobs: # START: Stable releases - when: condition: - equal: [ --release, << parameters.publish_npm_args >> ] + equal: [ "release", << parameters.release_type >> ] steps: - run: name: Update rn-diff-purge to generate upgrade-support diff @@ -1484,15 +1489,44 @@ jobs: -H "Accept: application/vnd.github.v3+json" \ -u "$PAT_USERNAME:$PAT_TOKEN" \ -d "{\"event_type\": \"publish\", \"client_payload\": { \"version\": \"${CIRCLE_TAG:1}\" }}" + # END: Stable releases + + # START: Stables and commitlies + - when: + condition: + or: + - equal: [ "release", << parameters.release_type >> ] + - equal: [ "dry-run", << parameters.release_type >> ] + steps: - run: name: Install dependencies command: apt update && apt install -y jq jo - run: name: Create draft GitHub Release and upload Hermes binaries command: | - ARTIFACTS=("$HERMES_WS_DIR/hermes-runtime-darwin/hermes-runtime-darwin-$CIRCLE_TAG.tar.gz") - ./scripts/circleci/create_github_release.sh $CIRCLE_TAG $CIRCLE_PROJECT_USERNAME $CIRCLE_PROJECT_REPONAME $GITHUB_TOKEN "${ARTIFACTS[@]}" - # END: Stable releases + RELEASE_VERSION=$(cat build/.version) + if [[ << parameters.release_type >> == "release" ]]; then + GIT_TAG=$CIRCLE_TAG + elif [[ << parameters.release_type >> == "dry-run" ]]; then + GIT_TAG=v1000.0.0 + fi + + ARTIFACTS=("") + for build_type in "Debug" "Release"; do + TARBALL_FILENAME=$(node ./scripts/hermes/get-tarball-name.js \ + --buildType $build_type \ + --releaseVersion $RELEASE_VERSION) + + ARTIFACTS+=("$HERMES_WS_DIR/hermes-runtime-darwin/$TARBALL_FILENAME") + done + + ./scripts/circleci/create_github_release.sh \ + << parameters.release_type >> \ + $GIT_TAG \ + $RELEASE_VERSION \ + $GITHUB_TOKEN \ + "${ARTIFACTS[@]}" + # END: Stable and commitlies # ------------------------- # JOBS: Nightly @@ -1551,7 +1585,7 @@ workflows: - prepare_hermes_workspace - build_npm_package: # Build a release package on every untagged commit, but do not publish to npm. - publish_npm_args: --dry-run + release_type: "dry-run" requires: - build_hermesc_linux - build_hermes_macos @@ -1639,13 +1673,44 @@ workflows: - build_npm_package: name: build_and_publish_npm_package context: react-native-bot - publish_npm_args: --release + release_type: "release" filters: *only_release_tags requires: - build_hermesc_linux - build_hermes_macos - build_hermesc_windows + package_and_publish_release_dryrun: + jobs: + - prepare_package_for_release: + name: prepare_package_for_release + version: 'v1000.0.1' + latest : false + dryrun: true + - prepare_hermes_workspace: + requires: + - prepare_package_for_release + - build_hermesc_linux: + requires: + - prepare_hermes_workspace + - build_hermes_macos: + requires: + - prepare_hermes_workspace + matrix: + parameters: + flavor: ["Debug", "Release"] + - build_hermesc_windows: + requires: + - prepare_hermes_workspace + - build_npm_package: + name: build_and_publish_npm_package + context: react-native-bot + release_type: "dry-run" + requires: + - build_hermesc_linux + - build_hermes_macos + - build_hermesc_windows + analysis: unless: << pipeline.parameters.run_package_release_workflow_only >> jobs: @@ -1684,7 +1749,7 @@ workflows: requires: - prepare_hermes_workspace - build_npm_package: - publish_npm_args: --nightly + release_type: "nightly" requires: - build_hermesc_linux - build_hermes_macos diff --git a/scripts/__tests__/create_github_release_test.sh b/scripts/__tests__/create_github_release_test.sh deleted file mode 100755 index da66c1bb684fb9..00000000000000 --- a/scripts/__tests__/create_github_release_test.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -# Add your own Personal Access Token here. -# Create it at https://github.com/settings/tokens -GITHUB_TOKEN="" - -# Setup Circle CI envvars -CIRCLE_TAG="" -CIRCLE_PROJECT_USERNAME="" -CIRCLE_PROJECT_REPONAME="" - -if [ -z "$GITHUB_TOKEN" ] -then - echo "\$GITHUB_TOKEN is empty" - exit 1 -fi -if [ -z "$CIRCLE_TAG" ] -then - echo "\$CIRCLE_TAG is empty" - exit 1 -fi -if [ -z "$CIRCLE_PROJECT_USERNAME" ] -then - echo "\$CIRCLE_PROJECT_USERNAME is empty" - exit 1 -fi -if [ -z "$CIRCLE_PROJECT_REPONAME" ] -then - echo "\$CIRCLE_PROJECT_REPONAME is empty" - exit 1 -fi - -# Dummy artifacts to upload -ARTIFACTS=("hermes-runtime-darwin-$CIRCLE_TAG.tar.gz") -for ARTIFACT_PATH in "${ARTIFACTS[@]}" -do - : - head -c 1024 "$ARTIFACT_PATH" -done - -../circleci/create_github_release.sh "$CIRCLE_TAG" "$CIRCLE_PROJECT_USERNAME" "$CIRCLE_PROJECT_REPONAME" "$GITHUB_TOKEN" "${ARTIFACTS[@]}" diff --git a/scripts/circleci/create_github_release.sh b/scripts/circleci/create_github_release.sh index 27681b83f47c5c..e77f5b42fe3817 100755 --- a/scripts/circleci/create_github_release.sh +++ b/scripts/circleci/create_github_release.sh @@ -4,9 +4,21 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. +# This script creates a draft GitHub Release using RELEASE_TEMPLATE.md +# as a template and will upload the provided artifacts to the release. + +# Install dependencies: +# apt update && apt install -y jq jo + +THIS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REACT_NATIVE_PATH="$THIS_DIR/../.." + +GITHUB_OWNER=${CIRCLE_PROJECT_USERNAME:-facebook} +GITHUB_REPO=${CIRCLE_PROJECT_REPONAME:-react-native} + +RELEASE_TYPE="$1"; shift GIT_TAG="$1"; shift -GITHUB_OWNER="$1"; shift -GITHUB_REPO="$1"; shift +RELEASE_VERSION="$1"; shift GITHUB_TOKEN="$1"; shift ARTIFACTS=("$@") @@ -18,63 +30,94 @@ describe () { printf "\\n\\n%s\\n\\n" "$1" } -# Format our desired React Native version strings based on incoming git tag parameter. -# GIT_TAG=v0.69.0-rc.4 -# 0.69.0-rc.4 -RN_VERSION=${GIT_TAG:1} -# 0690rc4 -RN_SHORT_VERSION=${RN_VERSION//[.-]/} +echoerr () { + echo "$@" 1>&2 +} -PRERELEASE=false -if [[ "$RN_VERSION" == *"rc"* ]]; then - PRERELEASE=true +if [[ $RELEASE_TYPE == "release" ]]; then + describe_header "Preparing to create a GitHub release." +elif [[ $RELEASE_TYPE == "dry-run" ]]; then + describe_header "Preparing to create a GitHub release as a dry-run." +elif [[ $RELEASE_TYPE == "nightly" ]]; then + describe "GitHub Releases are not used with nightlies. Skipping." + exit 0 +else + echoerr "Unrecognized release type: $RELEASE_TYPE" + exit 1 fi -RELEASE_TEMPLATE_PATH=../../.github/RELEASE_TEMPLATE.md +# Derive short version string for use in the sample command used +# to create a new RN app in RELEASE_TEMPLATE.md +# 0.69.0-rc.4 -> 0690rc4 +RN_SHORT_VERSION=${RELEASE_VERSION//[.-]/} + +PRERELEASE=false +if [[ "$RELEASE_VERSION" == *"rc"* ]]; then + PRERELEASE=true +fi -# Replace placeholders in template with actual RN version strings -RELEASE_BODY=$(sed -e "s/__VERSION__/$RN_VERSION/g" -e "s/__SHORT_VERSION__/$RN_SHORT_VERSION/g" $RELEASE_TEMPLATE_PATH) +RELEASE_TEMPLATE_PATH="$REACT_NATIVE_PATH/.github/RELEASE_TEMPLATE.md" +if [[ -f $RELEASE_TEMPLATE_PATH ]]; then + # Replace placeholders in template with actual RN version strings + RELEASE_BODY=$(sed -e "s/__VERSION__/$RELEASE_VERSION/g" -e "s/__SHORT_VERSION__/$RN_SHORT_VERSION/g" "$RELEASE_TEMPLATE_PATH") +else + describe "Could not load GitHub Release template. Falling back to placeholder text." + RELEASE_BODY="" +fi # Format and encode JSON payload -RELEASE_DATA=$(jo tag_name="$GIT_TAG" name="$RN_VERSION" body="$RELEASE_BODY" draft=true prerelease="$PRERELEASE" generate_release_notes=false) +RELEASE_DATA=$(jo tag_name="$GIT_TAG" name="$RELEASE_VERSION" body="$RELEASE_BODY" draft=true prerelease="$PRERELEASE" generate_release_notes=false) +if [[ ! $RELEASE_DATA ]]; then + echoerr "Could not format release data." + exit 1 +fi -# Create prerelease GitHub Release draft +# Create GitHub Release draft describe_header "Creating GitHub release." describe "Release payload: $RELEASE_DATA" -CREATE_RELEASE_RESPONSE=$(curl -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: Bearer $GITHUB_TOKEN" \ - -d "$RELEASE_DATA" \ - "https://api.github.com/repos/$GITHUB_OWNER/$GITHUB_REPO/releases" -) -STATUS=$? -if [ $STATUS == 0 ]; then - describe "Created GitHub release successfully." -else - describe "Could not create GitHub release, request failed with $STATUS." +if [[ $RELEASE_TYPE == "release" ]]; then + CREATE_RELEASE_RESPONSE=$(curl -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -d "$RELEASE_DATA" \ + "https://api.github.com/repos/$GITHUB_OWNER/$GITHUB_REPO/releases" + ) + STATUS=$? + if [ $STATUS == 0 ]; then + describe "Created GitHub Release successfully." + RELEASE_ID=$(echo "$CREATE_RELEASE_RESPONSE" | jq '.id') + else + echoerr "Could not create GitHub release, request failed with $STATUS:\n\n$CREATE_RELEASE_RESPONSE" + exit 1 + fi +elif [[ $RELEASE_TYPE == "dry-run" ]]; then + describe "Skipping creating GitHub release because dry-run." fi -RELEASE_ID=$(echo "$CREATE_RELEASE_RESPONSE" | jq '.id') - # Upload artifacts +describe_header "Uploading artifacts to GitHub release." for ARTIFACT_PATH in "${ARTIFACTS[@]}" do : # Upload Hermes artifacts to GitHub Release ARTIFACT_NAME=$(basename "$ARTIFACT_PATH") - describe_header "Uploading $ARTIFACT_NAME..." - - if curl -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: Bearer $GITHUB_TOKEN" \ - -H "Content-Length: $(wc -c "$ARTIFACT_PATH" | awk '{print $1}')" \ - -H "Content-Type: application/gzip" \ - -T "$ARTIFACT_PATH" \ - --progress-bar \ - "https://uploads.github.com/repos/$GITHUB_OWNER/$GITHUB_REPO/releases/$RELEASE_ID/assets?name=$ARTIFACT_NAME"; then + describe "Uploading $ARTIFACT_NAME..." + + if [[ $RELEASE_TYPE == "release" ]]; then + if curl -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Content-Length: $(wc -c "$ARTIFACT_PATH" | awk '{print $1}')" \ + -H "Content-Type: application/gzip" \ + -T "$ARTIFACT_PATH" \ + --progress-bar \ + "https://uploads.github.com/repos/$GITHUB_OWNER/$GITHUB_REPO/releases/$RELEASE_ID/assets?name=$ARTIFACT_NAME"; then describe "Uploaded $ARTIFACT_NAME." - else + else describe "Could not upload $ARTIFACT_NAME to GitHub release." + fi + elif [[ $RELEASE_TYPE == "dry-run" ]]; then + describe "Skipping $ARTIFACT_NAME upload because dry-run." fi done diff --git a/scripts/circleci/post-artifacts-link.sh b/scripts/circleci/post-artifacts-link.sh index 6318dc1e0f624d..21771e4fd493fa 100755 --- a/scripts/circleci/post-artifacts-link.sh +++ b/scripts/circleci/post-artifacts-link.sh @@ -7,5 +7,6 @@ GITHUB_OWNER=${CIRCLE_PROJECT_USERNAME:-facebook} \ GITHUB_REPO=${CIRCLE_PROJECT_REPONAME:-react-native} \ GITHUB_PR_NUMBER="${CIRCLE_PR_NUMBER:-${CIRCLE_PULL_REQUEST##*/}}" \ +GITHUB_REF=${CIRCLE_BRANCH} \ GITHUB_SHA=${CIRCLE_SHA1} \ exec node packages/react-native-bots/post-artifacts-link.js diff --git a/scripts/prepare-package-for-release.js b/scripts/prepare-package-for-release.js index 845303ff8e06b5..4f89743c618827 100755 --- a/scripts/prepare-package-for-release.js +++ b/scripts/prepare-package-for-release.js @@ -36,16 +36,25 @@ const argv = yargs alias: 'latest', type: 'boolean', default: false, + }) + .option('d', { + alias: 'dry-run', + type: 'boolean', + default: false, }).argv; const branch = process.env.CIRCLE_BRANCH; const remote = argv.remote; const releaseVersion = argv.toVersion; const isLatest = argv.latest; +const isDryRun = argv.dryRun; -if (!isReleaseBranch(branch)) { +if (branch && !isReleaseBranch(branch) && !isDryRun) { console.error(`This needs to be on a release branch. On branch: ${branch}`); exit(1); +} else if (!branch && !isDryRun) { + console.error('This needs to be on a release branch.'); + exit(1); } const {version} = parseVersion(releaseVersion); @@ -67,6 +76,12 @@ if (exec('source scripts/update_podfile_lock.sh && update_pods').code) { exit(1); } +echo(`Local checkout has been prepared for release version ${version}.`); +if (isDryRun) { + echo('Changes will not be committed because --dry-run was set to true.'); + exit(0); +} + // Make commit [0.21.0-rc] Bump version numbers if (exec(`git commit -a -m "[${version}] Bump version numbers"`).code) { echo('failed to commit'); diff --git a/scripts/publish-npm.js b/scripts/publish-npm.js index 5de46b96e1651a..2b4834e8de0361 100755 --- a/scripts/publish-npm.js +++ b/scripts/publish-npm.js @@ -186,6 +186,10 @@ if (isCommitly) { generateAndroidArtifacts(releaseVersion, tmpPublishingFolder); +// Write version number to the build folder +const releaseVersionFile = path.join('build', '.version'); +fs.writeFileSync(releaseVersionFile, releaseVersion); + if (dryRunBuild) { echo('Skipping `npm publish` because --dry-run is set.'); exit(0);