@samitouri / QOS-React / commits / c9143b98d0

[compiler] Refactor release script

Updates the release script to publish tags as well as take a `--ci` option Test plan: ``` $ yarn npm:publish --debug --frfr yarn run v1.22.22 $ node scripts/release/publish --debug --frfr ℹ Preparing to publish (for real) [debug=true] ℹ Building packages ✔ Successfully built babel-plugin-react-compiler ✔ Successfully built eslint-plugin-react-compiler ✔ Successfully built react-compiler-healthcheck NPM 2-factor auth code: ****** ✔ Wrote package.json for babel-plugin-react-compiler@0.0.0-experimental-10cf18a-20240806 ========== babel-plugin-react-compiler ========== ⠧ Publishing babel-plugin-react-compiler@0.0.0-experimental-10cf18a-20240806 to npm + babel-plugin-react-compiler@0.0.0-experimental-10cf18a-20240806 ✔ Successfully published babel-plugin-react-compiler to npm ℹ dry-run: npm dist-tag add babel-plugin-react-compiler@0.0.0-experimental-10cf18a-20240806 experimental --otp=****** ✔ Successfully pushed dist-tag experimental for babel-plugin-react-compiler to npm ✔ Wrote package.json for eslint-plugin-react-compiler@0.0.0-experimental-532f76b-20240806 ========== eslint-plugin-react-compiler ========== ⠹ Publishing eslint-plugin-react-compiler@0.0.0-experimental-532f76b-20240806 to npm + eslint-plugin-react-compiler@0.0.0-experimental-532f76b-20240806 ✔ Successfully published eslint-plugin-react-compiler to npm ℹ dry-run: npm dist-tag add eslint-plugin-react-compiler@0.0.0-experimental-532f76b-20240806 experimental --otp=****** ✔ Successfully pushed dist-tag experimental for eslint-plugin-react-compiler to npm ✔ Wrote package.json for react-compiler-healthcheck@0.0.0-experimental-48a8743-20240806 ========== react-compiler-healthcheck ========== ⠙ Publishing react-compiler-healthcheck@0.0.0-experimental-48a8743-20240806 to npm + react-compiler-healthcheck@0.0.0-experimental-48a8743-20240806 ✔ Successfully published react-compiler-healthcheck to npm ℹ dry-run: npm dist-tag add react-compiler-healthcheck@0.0.0-experimental-48a8743-20240806 experimental --otp=****** ✔ Successfully pushed dist-tag experimental for react-compiler-healthcheck to npm ✅ All done ✨ Done in 50.64s. ``` ghstack-source-id: 405cc001c2ab2adaad2bfe4f11fdb7fd28d7e2d1 Pull Request resolved: https://github.com/facebook/react/pull/30614

Lauren Tan committed Aug 6, 2024 at 14:41 UTC c9143b98d0096c909a2cc23290e3044c1390f6e2
6 files changed +184 -95
compiler/package.json
+4 -2
@@ -25,9 +25,11 @@
25 "snap": "yarn workspace babel-plugin-react-compiler run snap",
26 "snap:build": "yarn workspace snap run build",
27 "postinstall": "perl -p -i -e 's/react\\.element/react.transitional.element/' packages/snap/node_modules/fbt/lib/FbtReactUtil.js && perl -p -i -e 's/didWarnAboutUsingAct = false;/didWarnAboutUsingAct = true;/' packages/babel-plugin-react-compiler/node_modules/react-dom/cjs/react-dom-test-utils.development.js",
28 - "npm:publish": "node scripts/release/publish-manual"
28 + "npm:publish": "node scripts/release/publish"
29 + },
30 + "dependencies": {
31 + "fs-extra": "^4.0.2"
32 },
30 - "dependencies": {},
33 "devDependencies": {
34 "@rollup/plugin-commonjs": "^25.0.7",
35 "@rollup/plugin-json": "^6.1.0",
compiler/scripts/release/publish.js renamed
+86 -92
@@ -1,49 +1,16 @@
1 -const cp = require('child_process');
1 const ora = require('ora');
2 const path = require('path');
3 const yargs = require('yargs');
5 -const util = require('util');
4 const {hashElement} = require('folder-hash');
5 const promptForOTP = require('./prompt-for-otp');
8 -
9 -const PUBLISHABLE_PACKAGES = [
10 - 'babel-plugin-react-compiler',
11 - 'eslint-plugin-react-compiler',
12 - 'react-compiler-healthcheck',
13 -];
14 -
15 -function _spawn(command, args, options, cb) {
16 - const child = cp.spawn(command, args, options);
17 - child.on('close', exitCode => {
18 - cb(null, exitCode);
19 - });
20 - return child;
21 -}
22 -const spawnHelper = util.promisify(_spawn);
23 -
24 -function execHelper(command, options, streamStdout = false) {
25 - return new Promise((resolve, reject) => {
26 - const proc = cp.exec(command, options, (error, stdout) =>
27 - error ? reject(error) : resolve(stdout.trim())
28 - );
29 - if (streamStdout) {
30 - proc.stdout.pipe(process.stdout);
31 - }
32 - });
33 -}
34 -
35 -async function getDateStringForCommit(commit) {
36 - let dateString = await execHelper(
37 - `git show -s --no-show-signature --format=%cd --date=format:%Y%m%d ${commit}`
38 - );
39 -
40 - // On CI environment, this string is wrapped with quotes '...'s
41 - if (dateString.startsWith("'")) {
42 - dateString = dateString.slice(1, 9);
43 - }
44 -
45 - return dateString;
46 -}
6 +const {PUBLISHABLE_PACKAGES} = require('./shared/packages');
7 +const {
8 + execHelper,
9 + getDateStringForCommit,
10 + spawnHelper,
11 +} = require('./shared/utils');
12 +const {buildPackages} = require('./shared/build-packages');
13 +const {readJson, writeJson} = require('fs-extra');
14
15 /**
16 * Script for publishing PUBLISHABLE_PACKAGES to npm. By default, this runs in tarball mode, meaning
@@ -83,12 +50,21 @@ async function main() {
50 type: 'boolean',
51 default: false,
52 })
53 + .option('ci', {
54 + description: 'Publish packages via CI',
55 + type: 'boolean',
56 + default: false,
57 + })
58 + .option('tags', {
59 + description: 'Tags to publish to npm',
60 + type: 'choices',
61 + choices: ['experimental'],
62 + default: ['experimental'],
63 + })
64 .help('help')
65 .parseSync();
66
89 - const {packages, forReal, debug} = argv;
90 -
91 - if (debug === false) {
67 + if (argv.debug === false) {
68 const currBranchName = await execHelper('git rev-parse --abbrev-ref HEAD');
69 const isPristine = (await execHelper('git status --porcelain')) === '';
70 if (currBranchName !== 'main' || isPristine === false) {
@@ -98,31 +74,19 @@ async function main() {
74 }
75 }
76
101 - let pkgNames = packages;
102 - if (Array.isArray(packages) === false) {
103 - pkgNames = [packages];
77 + let pkgNames = argv.packages;
78 + if (Array.isArray(argv.packages) === false) {
79 + pkgNames = [argv.packages];
80 }
81 const spinner = ora(
82 `Preparing to publish ${
107 - forReal === true ? '(for real)' : '(dry run)'
108 - } [debug=${debug}]`
83 + argv.forReal === true ? '(for real)' : '(dry run)'
84 + } [debug=${argv.debug}]`
85 ).info();
86
111 - spinner.info('Building packages');
112 - for (const pkgName of pkgNames) {
113 - const command = `yarn workspace ${pkgName} run build`;
114 - spinner.start(`Running: ${command}\n`);
115 - try {
116 - await execHelper(command);
117 - } catch (e) {
118 - spinner.fail(e.toString());
119 - throw e;
120 - }
121 - spinner.succeed(`Successfully built ${pkgName}`);
122 - }
123 - spinner.stop();
87 + await buildPackages(pkgNames);
88
125 - if (forReal === false) {
89 + if (argv.forReal === false) {
90 spinner.info('Dry run: Report tarball contents');
91 for (const pkgName of pkgNames) {
92 console.log(`\n========== ${pkgName} ==========\n`);
@@ -143,8 +107,7 @@ async function main() {
107 );
108 }
109
146 - if (forReal === true) {
147 - const otp = await promptForOTP();
110 + if (argv.forReal === true) {
111 const commit = await execHelper(
112 'git show -s --no-show-signature --format=%h',
113 {
@@ -152,9 +115,14 @@ async function main() {
115 }
116 );
117 const dateString = await getDateStringForCommit(commit);
118 + const otp = argv.ci === false ? await promptForOTP() : null;
119
120 for (const pkgName of pkgNames) {
121 const pkgDir = path.resolve(__dirname, `../../packages/${pkgName}`);
122 + const pkgJsonPath = path.resolve(
123 + __dirname,
124 + `../../packages/${pkgName}/package.json`
125 + );
126 const {hash} = await hashElement(pkgDir, {
127 encoding: 'hex',
128 folders: {exclude: ['node_modules']},
@@ -163,39 +131,36 @@ async function main() {
131 const truncatedHash = hash.slice(0, 7);
132 const newVersion = `0.0.0-experimental-${truncatedHash}-${dateString}`;
133
166 - spinner.start(`Bumping version: ${pkgName}`);
167 - try {
168 - await execHelper(
169 - `yarn version --new-version ${newVersion} --no-git-tag-version`,
170 - {
171 - cwd: pkgDir,
172 - }
173 - );
174 - await execHelper(
175 - `git add package.json && git commit -m "Bump version to ${newVersion}"`,
176 - {
177 - cwd: pkgDir,
178 - }
179 - );
180 - } catch (e) {
181 - spinner.fail(e.toString());
182 - throw e;
183 - }
184 - spinner.succeed(
185 - `Bumped ${pkgName} to ${newVersion} and added a git commit`
134 + spinner.start(`Writing package.json for ${pkgName}@${newVersion}`);
135 + await writeJson(
136 + pkgJsonPath,
137 + {
138 + ...(await readJson(pkgJsonPath)),
139 + version: newVersion,
140 + },
141 + {spaces: 2}
142 );
187 - }
143 + spinner.succeed(`Wrote package.json for ${pkgName}@${newVersion}`);
144
189 - for (const pkgName of pkgNames) {
190 - const pkgDir = path.resolve(__dirname, `../../packages/${pkgName}`);
145 console.log(`\n========== ${pkgName} ==========\n`);
192 - spinner.start(`Publishing ${pkgName} to npm\n`);
146 + spinner.start(`Publishing ${pkgName}@${newVersion} to npm\n`);
147
194 - const opts = debug === true ? ['publish', '--dry-run'] : ['publish'];
148 + let opts = [];
149 + if (argv.debug === true) {
150 + opts.push('--dry-run');
151 + }
152 + if (otp != null) {
153 + opts.push(`--otp=${otp}`);
154 + }
155 try {
156 await spawnHelper(
157 'npm',
198 - [...opts, '--registry=https://registry.npmjs.org', `--otp=${otp}`],
158 + [
159 + 'publish',
160 + ...opts,
161 + '--registry=https://registry.npmjs.org',
162 + '--tag=experimental',
163 + ],
164 {
165 cwd: pkgDir,
166 stdio: 'inherit',
@@ -207,9 +172,38 @@ async function main() {
172 throw e;
173 }
174 spinner.succeed(`Successfully published ${pkgName} to npm`);
175 +
176 + spinner.start('Pushing tags to npm');
177 + for (const tag of argv.tags) {
178 + try {
179 + let opts;
180 + if (otp != null) {
181 + opts = [
182 + 'dist-tag',
183 + 'add',
184 + `${pkgName}@${newVersion}`,
185 + tag,
186 + `--otp=${otp}`,
187 + ];
188 + } else {
189 + opts = ['dist-tag', 'add', `${pkgName}@${newVersion}`, tag];
190 + }
191 + if (argv.debug === true) {
192 + spinner.info(`dry-run: npm ${opts.join(' ')}`);
193 + } else {
194 + await spawnHelper('npm', opts);
195 + }
196 + } catch (e) {
197 + spinner.fail(e.toString());
198 + throw e;
199 + }
200 + spinner.succeed(
201 + `Successfully pushed dist-tag ${tag} for ${pkgName} to npm`
202 + );
203 + }
204 }
205
212 - console.log('\n\n✅ All done, please push version bump commits to GitHub');
206 + console.log('\n\n✅ All done');
207 }
208 }
209
compiler/scripts/release/shared/build-packages.js new
+22
@@ -0,0 +1,22 @@
1 +const ora = require('ora');
2 +const {execHelper} = require('./utils');
3 +
4 +async function buildPackages(pkgNames) {
5 + const spinner = ora(`Building packages`).info();
6 + for (const pkgName of pkgNames) {
7 + const command = `yarn workspace ${pkgName} run build`;
8 + spinner.start(`Running: ${command}\n`);
9 + try {
10 + await execHelper(command);
11 + } catch (e) {
12 + spinner.fail(e.toString());
13 + throw e;
14 + }
15 + spinner.succeed(`Successfully built ${pkgName}`);
16 + }
17 + spinner.stop();
18 +}
19 +
20 +module.exports = {
21 + buildPackages,
22 +};
compiler/scripts/release/shared/packages.js new
+9
@@ -0,0 +1,9 @@
1 +const PUBLISHABLE_PACKAGES = [
2 + 'babel-plugin-react-compiler',
3 + 'eslint-plugin-react-compiler',
4 + 'react-compiler-healthcheck',
5 +];
6 +
7 +module.exports = {
8 + PUBLISHABLE_PACKAGES,
9 +};
compiler/scripts/release/shared/utils.js new
+41
@@ -0,0 +1,41 @@
1 +const cp = require('child_process');
2 +const util = require('util');
3 +
4 +function execHelper(command, options, streamStdout = false) {
5 + return new Promise((resolve, reject) => {
6 + const proc = cp.exec(command, options, (error, stdout) =>
7 + error ? reject(error) : resolve(stdout.trim())
8 + );
9 + if (streamStdout) {
10 + proc.stdout.pipe(process.stdout);
11 + }
12 + });
13 +}
14 +
15 +function _spawn(command, args, options, cb) {
16 + const child = cp.spawn(command, args, options);
17 + child.on('close', exitCode => {
18 + cb(null, exitCode);
19 + });
20 + return child;
21 +}
22 +const spawnHelper = util.promisify(_spawn);
23 +
24 +async function getDateStringForCommit(commit) {
25 + let dateString = await execHelper(
26 + `git show -s --no-show-signature --format=%cd --date=format:%Y%m%d ${commit}`
27 + );
28 +
29 + // On CI environment, this string is wrapped with quotes '...'s
30 + if (dateString.startsWith("'")) {
31 + dateString = dateString.slice(1, 9);
32 + }
33 +
34 + return dateString;
35 +}
36 +
37 +module.exports = {
38 + execHelper,
39 + spawnHelper,
40 + getDateStringForCommit,
41 +};
compiler/yarn.lock
+22 -1
@@ -5434,6 +5434,15 @@ fraction.js@^4.2.0:
5434 resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
5435 integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==
5436
5437 +fs-extra@^4.0.2:
5438 + version "4.0.3"
5439 + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
5440 + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==
5441 + dependencies:
5442 + graceful-fs "^4.1.2"
5443 + jsonfile "^4.0.0"
5444 + universalify "^0.1.0"
5445 +
5446 fs.realpath@^1.0.0:
5447 version "1.0.0"
5448 resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -5686,7 +5695,7 @@ gopd@^1.0.1:
5695 dependencies:
5696 get-intrinsic "^1.1.3"
5697
5689 -graceful-fs@^4.1.2, graceful-fs@^4.2.4:
5698 +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4:
5699 version "4.2.11"
5700 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
5701 integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -7591,6 +7600,13 @@ json5@^2.1.2, json5@^2.2.3:
7600 resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
7601 integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
7602
7603 +jsonfile@^4.0.0:
7604 + version "4.0.0"
7605 + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
7606 + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
7607 + optionalDependencies:
7608 + graceful-fs "^4.1.6"
7609 +
7610 "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3:
7611 version "3.3.3"
7612 resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea"
@@ -9718,6 +9734,11 @@ unicode-property-aliases-ecmascript@^2.0.0:
9734 resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd"
9735 integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==
9736
9737 +universalify@^0.1.0:
9738 + version "0.1.2"
9739 + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
9740 + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
9741 +
9742 universalify@^0.2.0:
9743 version "0.2.0"
9744 resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"