@samitouri / QOS-React / commits / 4c9392b43e

[ci] Prepare publish workflow (#32488)

Fixes up a few things in the script and workflow to make it possible to run in CI without interactive prompts.

lauren committed Feb 27, 2025 at 15:24 UTC 4c9392b43e9f39e17c18ef1c2cd0f0a14e85669c
9 files changed +154 -72
.github/workflows/runtime_releases_from_npm_manual.yml
+44 -12
@@ -12,22 +12,22 @@ on:
12 description: Version to publish for the specified packages
13 type: string
14 only_packages:
15 - description: Space separated list of packages to publish on NPM. Use this OR skip_packages, not together.
15 + description: Packages to publish (space separated)
16 type: string
17 skip_packages:
18 - description: Space separated list of packages to NOT publish on NPM. Use this OR only_packages, not together.
18 + description: Packages to NOT publish (space separated)
19 type: string
20 tags:
21 - description: Space separated list of tags to tag the release with on NPM
21 + description: NPM tags (space separated)
22 type: string
23 - default: "['untagged']"
23 + default: untagged
24 dry:
25 required: true
26 - description: Don't actually publish, just run a dry run
26 + description: Dry run instead of publish?
27 type: boolean
28 default: true
29 force_notify:
30 - description: Force a Discord notification
30 + description: Force a Discord notification?
31 type: boolean
32 default: false
33
@@ -52,8 +52,8 @@ jobs:
52 embed-author-icon-url: ${{ github.event.sender.avatar_url }}
53 embed-title: '⚠️ Publishing release from NPM'
54 embed-description: |
55 - ```
56 - inputs: ${{ toJson(inputs) }}
55 + ```json
56 + ${{ toJson(inputs) }}
57 ```
58 embed-url: https://github.com/facebook/react/actions/runs/${{ github.run_id }}
59
@@ -80,12 +80,44 @@ jobs:
80 working-directory: scripts/release
81 - run: cp ./scripts/release/ci-npmrc ~/.npmrc
82 - if: '${{ inputs.only_packages }}'
83 + name: 'Prepare and publish ${{ inputs.only_packages }}'
84 run: |
84 - scripts/release/prepare-release-from-npm.js --skipTests --version=${{ inputs.version_to_promote }} --onlyPackages=${{ inputs.only_packages }}
85 + echo -e "===== Preparing release from NPM =====\n"
86 + scripts/release/prepare-release-from-npm.js \
87 + --ci \
88 + --skipTests \
89 + --version=${{ inputs.version_to_promote }} \
90 + --publishVersion=${{ inputs.version_to_publish }} \
91 + --onlyPackages=${{ inputs.only_packages }}
92 +
93 + echo -e "\n\n===== Check prepared files =====\n"
94 ls -R build/node_modules
86 - # scripts/release/publish.js --ci --tags=${{ inputs.tags }} --publishVersion=${{ inputs.version_to_publish }} --onlyPackages=${{ inputs.only_packages }} --dry=${{ inputs.dry || 'false' }}
95 +
96 + echo -e "\n\n===== Publishing to NPM =====\n"
97 + scripts/release/publish.js \
98 + --ci \
99 + --tags=${{ inputs.tags }} \
100 + --publishVersion=${{ inputs.version_to_publish }} \
101 + --onlyPackages=${{ inputs.only_packages }} \
102 + --dry=${{ inputs.dry }}
103 - if: '${{ inputs.skip_packages }}'
104 + name: 'Prepare and publish all packages EXCEPT ${{ inputs.skip_packages }}'
105 run: |
89 - scripts/release/prepare-release-from-npm.js --skipTests --version=${{ inputs.version_to_promote }} --skipPackages=${{ inputs.skip_packages }}
106 + echo -e "===== Preparing release from NPM =====\n"
107 + scripts/release/prepare-release-from-npm.js \
108 + --ci \
109 + --skipTests \
110 + --version=${{ inputs.version_to_promote }} \
111 + --publishVersion=${{ inputs.version_to_publish }} \
112 + --skipPackages=${{ inputs.skip_packages }}
113 +
114 + echo -e "\n\n===== Check prepared files =====\n"
115 ls -R build/node_modules
91 - # scripts/release/publish.js --ci --tags=${{ inputs.tags }} --publishVersion=${{ inputs.version_to_publish }} --skipPackages=${{ inputs.skip_packages }} --dry=${{ inputs.dry || 'false' }}
116 +
117 + echo -e "\n\n===== Publishing to NPM =====\n"
118 + scripts/release/publish.js \
119 + --ci \
120 + --tags=${{ inputs.tags }} \
121 + --publishVersion=${{ inputs.version_to_publish }} \
122 + --skipPackages=${{ inputs.skip_packages }} \
123 + --dry=${{ inputs.dry }}
scripts/release/prepare-release-from-npm-commands/confirm-stable-version-numbers.js
+37 -35
@@ -7,7 +7,7 @@ const semver = require('semver');
7 const theme = require('../theme');
8 const {confirm} = require('../utils');
9
10 -const run = async ({skipPackages}, versionsMap) => {
10 +const run = async ({ci, skipPackages}, versionsMap) => {
11 const groupedVersionsMap = new Map();
12
13 // Group packages with the same source versions.
@@ -22,44 +22,46 @@ const run = async ({skipPackages}, versionsMap) => {
22 }
23 });
24
25 - // Prompt user to confirm or override each version group.
26 - const entries = [...groupedVersionsMap.entries()];
27 - for (let i = 0; i < entries.length; i++) {
28 - const [bestGuessVersion, packages] = entries[i];
29 - const packageNames = packages.map(name => theme.package(name)).join(', ');
25 + if (ci !== true) {
26 + // Prompt user to confirm or override each version group if not running in CI.
27 + const entries = [...groupedVersionsMap.entries()];
28 + for (let i = 0; i < entries.length; i++) {
29 + const [bestGuessVersion, packages] = entries[i];
30 + const packageNames = packages.map(name => theme.package(name)).join(', ');
31
31 - let version = bestGuessVersion;
32 - if (
33 - skipPackages.some(skipPackageName => packages.includes(skipPackageName))
34 - ) {
35 - await confirm(
36 - theme`{spinnerSuccess ✓} Version for ${packageNames} will remain {version ${bestGuessVersion}}`
37 - );
38 - } else {
39 - const defaultVersion = bestGuessVersion
40 - ? theme.version(` (default ${bestGuessVersion})`)
41 - : '';
42 - version =
43 - (await prompt(
44 - theme`{spinnerSuccess ✓} Version for ${packageNames}${defaultVersion}: `
45 - )) || bestGuessVersion;
46 - prompt.done();
47 - }
32 + let version = bestGuessVersion;
33 + if (
34 + skipPackages.some(skipPackageName => packages.includes(skipPackageName))
35 + ) {
36 + await confirm(
37 + theme`{spinnerSuccess ✓} Version for ${packageNames} will remain {version ${bestGuessVersion}}`
38 + );
39 + } else {
40 + const defaultVersion = bestGuessVersion
41 + ? theme.version(` (default ${bestGuessVersion})`)
42 + : '';
43 + version =
44 + (await prompt(
45 + theme`{spinnerSuccess ✓} Version for ${packageNames}${defaultVersion}: `
46 + )) || bestGuessVersion;
47 + prompt.done();
48 + }
49
49 - // Verify a valid version has been supplied.
50 - try {
51 - semver(version);
50 + // Verify a valid version has been supplied.
51 + try {
52 + semver(version);
53
53 - packages.forEach(packageName => {
54 - versionsMap.set(packageName, version);
55 - });
56 - } catch (error) {
57 - console.log(
58 - theme`{spinnerError ✘} Version {version ${version}} is invalid.`
59 - );
54 + packages.forEach(packageName => {
55 + versionsMap.set(packageName, version);
56 + });
57 + } catch (error) {
58 + console.log(
59 + theme`{spinnerError ✘} Version {version ${version}} is invalid.`
60 + );
61
61 - // Prompt again
62 - i--;
62 + // Prompt again
63 + i--;
64 + }
65 }
66 }
67 };
scripts/release/prepare-release-from-npm-commands/guess-stable-version-numbers.js
+35 -21
@@ -5,7 +5,10 @@
5 const semver = require('semver');
6 const {execRead, logPromise} = require('../utils');
7
8 -const run = async ({cwd, packages, skipPackages}, versionsMap) => {
8 +const run = async (
9 + {cwd, packages, skipPackages, ci, publishVersion},
10 + versionsMap
11 +) => {
12 const branch = await execRead('git branch | grep \\* | cut -d " " -f2', {
13 cwd,
14 });
@@ -13,30 +16,41 @@ const run = async ({cwd, packages, skipPackages}, versionsMap) => {
16 for (let i = 0; i < packages.length; i++) {
17 const packageName = packages[i];
18
16 - try {
17 - // In case local package JSONs are outdated,
18 - // guess the next version based on the latest NPM release.
19 - const version = await execRead(`npm show ${packageName} version`);
20 -
21 - if (skipPackages.includes(packageName)) {
22 - versionsMap.set(packageName, version);
19 + if (ci === true) {
20 + if (publishVersion != null) {
21 + versionsMap.set(packageName, publishVersion);
22 } else {
24 - const {major, minor, patch} = semver(version);
25 -
26 - // Guess the next version by incrementing patch.
27 - // The script will confirm this later.
28 - // By default, new releases from mains should increment the minor version number,
29 - // and patch releases should be done from branches.
30 - if (branch === 'main') {
31 - versionsMap.set(packageName, `${major}.${minor + 1}.0`);
23 + console.error(
24 + 'When running in CI mode, a publishVersion must be supplied'
25 + );
26 + process.exit(1);
27 + }
28 + } else {
29 + try {
30 + // In case local package JSONs are outdated,
31 + // guess the next version based on the latest NPM release.
32 + const version = await execRead(`npm show ${packageName} version`);
33 +
34 + if (skipPackages.includes(packageName)) {
35 + versionsMap.set(packageName, version);
36 } else {
33 - versionsMap.set(packageName, `${major}.${minor}.${patch + 1}`);
37 + const {major, minor, patch} = semver(version);
38 +
39 + // Guess the next version by incrementing patch.
40 + // The script will confirm this later.
41 + // By default, new releases from mains should increment the minor version number,
42 + // and patch releases should be done from branches.
43 + if (branch === 'main') {
44 + versionsMap.set(packageName, `${major}.${minor + 1}.0`);
45 + } else {
46 + versionsMap.set(packageName, `${major}.${minor}.${patch + 1}`);
47 + }
48 }
49 + } catch (error) {
50 + // If the package has not yet been published,
51 + // we'll require a version number to be entered later.
52 + versionsMap.set(packageName, null);
53 }
36 - } catch (error) {
37 - // If the package has not yet been published,
38 - // we'll require a version number to be entered later.
39 - versionsMap.set(packageName, null);
54 }
55 }
56 };
scripts/release/prepare-release-from-npm-commands/parse-params.js
+11
@@ -39,6 +39,17 @@ const paramDefinitions = [
39 description:
40 'Version of published "next" release (e.g. 0.0.0-0e526bcec-20210202)',
41 },
42 + {
43 + name: 'publishVersion',
44 + type: String,
45 + description: 'Version to publish',
46 + },
47 + {
48 + name: 'ci',
49 + type: Boolean,
50 + description: 'Run in automated environment, without interactive prompts.',
51 + defaultValue: false,
52 + },
53 ];
54
55 module.exports = () => {
scripts/release/prepare-release-from-npm-commands/update-stable-version-numbers.js
+7 -3
@@ -9,7 +9,7 @@ const {join, relative} = require('path');
9 const {confirm, execRead, printDiff} = require('../utils');
10 const theme = require('../theme');
11
12 -const run = async ({cwd, packages, version}, versionsMap) => {
12 +const run = async ({cwd, packages, version, ci}, versionsMap) => {
13 const nodeModulesPath = join(cwd, 'build/node_modules');
14
15 // Cache all package JSONs for easy lookup below.
@@ -107,7 +107,9 @@ const run = async ({cwd, packages, version}, versionsMap) => {
107 printDependencies(packageJSON.dependencies, 'dependency');
108 printDependencies(packageJSON.peerDependencies, 'peer');
109 }
110 - await confirm('Do the versions above look correct?');
110 + if (ci !== true) {
111 + await confirm('Do the versions above look correct?');
112 + }
113
114 clear();
115
@@ -167,7 +169,9 @@ const run = async ({cwd, packages, version}, versionsMap) => {
169 console.log(
170 theme`A full diff is available at {path ${relative(cwd, diffPath)}}.`
171 );
170 - await confirm('Do the changes above look correct?');
172 + if (ci !== true) {
173 + await confirm('Do the changes above look correct?');
174 + }
175 } else {
176 console.log(
177 theme`Skipping React renderer version update because React is not included in the release.`
scripts/release/prepare-release-from-npm.js
+7
@@ -26,6 +26,13 @@ const run = async () => {
26 params.version = await getLatestNextVersion();
27 }
28
29 + if (params.onlyPackages.length > 0 && params.skipPackages.length > 0) {
30 + console.error(
31 + '--onlyPackages and --skipPackages cannot be used together'
32 + );
33 + process.exit(1);
34 + }
35 +
36 params.packages = await getPublicPackages(isExperimental);
37 params.packages = params.packages.filter(packageName => {
38 if (params.onlyPackages.length > 0) {
scripts/release/publish-commands/confirm-version-and-tags.js
+3
@@ -38,6 +38,9 @@ const run = async ({cwd, packages, tags, ci}) => {
38 console.log(
39 theme`• {package ${packageName}} {version ${packageJSON.version}}`
40 );
41 + if (ci) {
42 + console.log(packageJSON);
43 + }
44 }
45
46 if (!ci) {
scripts/release/publish-commands/publish-to-npm.js
+3 -1
@@ -27,7 +27,9 @@ const run = async ({cwd, dry, tags, ci}, packageName, otp) => {
27 await confirm('Is this expected?');
28 }
29 } else {
30 - console.log(theme`{spinnerSuccess ✓} Publishing {package ${packageName}}`);
30 + console.log(
31 + theme`{spinnerSuccess ✓} Publishing {package ${packageName}}${dry ? ' (dry-run)' : ''}`
32 + );
33
34 // Publish the package and tag it.
35 if (!dry) {
scripts/release/publish.js
+7
@@ -31,6 +31,13 @@ const run = async () => {
31 params.cwd = join(__dirname, '..', '..');
32 params.packages = await getPublicPackages(isExperimental);
33
34 + if (params.onlyPackages.length > 0 && params.skipPackages.length > 0) {
35 + console.error(
36 + '--onlyPackages and --skipPackages cannot be used together'
37 + );
38 + process.exit(1);
39 + }
40 +
41 if (params.onlyPackages.length > 0) {
42 params.packages = params.packages.filter(packageName => {
43 return params.onlyPackages.includes(packageName);