1#!/usr/bin/env node 2/** 3 * Copyright (c) Meta Platforms, Inc. and affiliates. 4 * 5 * This source code is licensed under the MIT license found in the 6 * LICENSE file in the root directory of this source tree. 7 * 8 * @format 9 */ 10 11'use strict'; 12 13/** 14 * This script walks a releaser through bumping the version for a release 15 * It will commit the appropriate tags to trigger the CircleCI jobs. 16 */ 17const {exit} = require('shelljs'); 18const yargs = require('yargs'); 19const inquirer = require('inquirer'); 20const request = require('request'); 21const {getBranchName, exitIfNotOnGit} = require('./scm-utils'); 22 23const {parseVersion, isReleaseBranch} = require('./version-utils'); 24const {failIfTagExists} = require('./release-utils'); 25 26let argv = yargs 27 .option('r', { 28 alias: 'remote', 29 default: 'origin', 30 }) 31 .option('t', { 32 alias: 'token', 33 describe: 34 'Your CircleCI personal API token. See https://circleci.com/docs/2.0/managing-api-tokens/#creating-a-personal-api-token to set one', 35 required: true, 36 }) 37 .option('v', { 38 alias: 'to-version', 39 describe: 'Version you aim to release, ex. 0.67.0-rc.1, 0.66.3', 40 required: true, 41 }) 42 .check(() => { 43 const branch = exitIfNotOnGit( 44 () => getBranchName(), 45 "Not in git. You can't invoke bump-oss-versions.js from outside a git repo.", 46 ); 47 exitIfNotOnReleaseBranch(branch); 48 return true; 49 }).argv; 50 51function exitIfNotOnReleaseBranch(branch) { 52 if (!isReleaseBranch(branch)) { 53 console.log( 54 'This script must be run in a react-native git repository checkout and on a release branch', 55 ); 56 exit(1); 57 } 58} 59 60function triggerReleaseWorkflow(options) { 61 return new Promise((resolve, reject) => { 62 request(options, function (error, response, body) { 63 if (error) { 64 reject(error); 65 } else { 66 resolve(body); 67 } 68 }); 69 }); 70} 71 72async function main() { 73 const branch = exitIfNotOnGit( 74 () => getBranchName(), 75 "Not in git. You can't invoke bump-oss-versions.js from outside a git repo.", 76 ); 77 const token = argv.token; 78 const releaseVersion = argv.toVersion; 79 failIfTagExists(releaseVersion, 'release'); 80 81 const {pushed} = await inquirer.prompt({ 82 type: 'confirm', 83 name: 'pushed', 84 message: `This script will trigger a release with whatever changes are on the remote branch: ${branch}. \nMake sure you have pushed any updates remotely.`, 85 }); 86 87 if (!pushed) { 88 console.log(`Please run 'git push ${argv.remote} ${branch}'`); 89 exit(1); 90 return; 91 } 92 93 let latest = false; 94 const {version, prerelease} = parseVersion(releaseVersion, 'release'); 95 if (!prerelease) { 96 const {setLatest} = await inquirer.prompt({ 97 type: 'confirm', 98 name: 'setLatest', 99 message: `Do you want to set ${version} as "latest" release on npm?`, 100 }); 101 latest = setLatest; 102 } 103 104 const npmTag = latest ? 'latest' : !prerelease ? branch : 'next'; 105 const {confirmRelease} = await inquirer.prompt({ 106 type: 'confirm', 107 name: 'confirmRelease', 108 message: `Releasing version "${version}" with npm tag "${npmTag}". Is this correct?`, 109 }); 110 111 if (!confirmRelease) { 112 console.log('Aborting.'); 113 return; 114 } 115 116 const parameters = { 117 release_version: version, 118 release_latest: latest, 119 run_package_release_workflow_only: true, 120 }; 121 122 const options = { 123 method: 'POST', 124 url: 'https://circleci.com/api/v2/project/github/facebook/react-native/pipeline', 125 headers: { 126 'Circle-Token': token, 127 'content-type': 'application/json', 128 }, 129 body: { 130 branch, 131 parameters, 132 }, 133 json: true, 134 }; 135 136 // See response: https://circleci.com/docs/api/v2/#operation/triggerPipeline 137 const body = await triggerReleaseWorkflow(options); 138 console.log( 139 `Monitor your release workflow: https://app.circleci.com/pipelines/github/facebook/react-native/${body.number}`, 140 ); 141 142 // TODO 143 // - Output the release changelog to paste into Github releases 144 // - Link to release discussions to update 145 // - Verify RN-diff publish is through 146} 147 148main().then(() => { 149 exit(0); 150}); 151