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