Skip to content

Commit 7e36d8b

Browse files
authored
Some release script fixes (facebook#20718)
* Retry loop should not start over from beginning When the otp times out, we should not retry the packages that were already successfully published. We should pick up where we left off. * Don't crash if build-info.json doesn't exist The "print follow up instructions" step crashes if build-info.json is not found. The new build workflow doesn't include those yet (might not need to) and since the instructions that depend on it only affect semver (latest) releases, I've moved the code to that branch. Will follow up with a proper fix, either by adding back a build-info.json file or removing that dependency and reading the commit some other way.
1 parent c47f3cf commit 7e36d8b

File tree

3 files changed

+121
-111
lines changed

3 files changed

+121
-111
lines changed

scripts/release/publish-commands/print-follow-up-instructions.js

Lines changed: 70 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@ const theme = require('../theme');
1010
const {execRead} = require('../utils');
1111

1212
const run = async ({cwd, packages, tags}) => {
13-
// All packages are built from a single source revision,
14-
// so it is safe to read build info from any one of them.
15-
const arbitraryPackageName = packages[0];
16-
const {commit} = readJsonSync(
17-
join(cwd, 'build', 'node_modules', arbitraryPackageName, 'build-info.json')
18-
);
19-
2013
// Tags are named after the react version.
2114
const {version} = readJsonSync(
2215
`${cwd}/build/node_modules/react/package.json`
@@ -28,6 +21,10 @@ const run = async ({cwd, packages, tags}) => {
2821
console.log(
2922
theme`{header A "next" release} {version ${version}} {header has been published!}`
3023
);
24+
} else if (tags.length === 1 && tags[0] === 'experimental') {
25+
console.log(
26+
theme`{header An "experimental" release} {version ${version}} {header has been published!}`
27+
);
3128
} else {
3229
const nodeModulesPath = join(cwd, 'build/node_modules');
3330

@@ -36,6 +33,23 @@ const run = async ({cwd, packages, tags}) => {
3633
);
3734

3835
if (tags.includes('latest')) {
36+
// All packages are built from a single source revision,
37+
// so it is safe to read build info from any one of them.
38+
const arbitraryPackageName = packages[0];
39+
// FIXME: New build script does not output build-info.json. It's only used
40+
// by this post-publish print job, and only for "latest" releases, so I've
41+
// disabled it as a workaround so the publish script doesn't crash for
42+
// "next" and "experimental" pre-releases.
43+
const {commit} = readJsonSync(
44+
join(
45+
cwd,
46+
'build',
47+
'node_modules',
48+
arbitraryPackageName,
49+
'build-info.json'
50+
)
51+
);
52+
3953
console.log();
4054
console.log(
4155
theme.header`Please review and commit all local, staged changes.`
@@ -54,61 +68,61 @@ const run = async ({cwd, packages, tags}) => {
5468
if (status) {
5569
console.log(theme.path`• packages/shared/ReactVersion.js`);
5670
}
57-
}
5871

59-
console.log();
60-
console.log(
61-
theme`{header Don't forget to also update and commit the }{path CHANGELOG}`
62-
);
72+
console.log();
73+
console.log(
74+
theme`{header Don't forget to also update and commit the }{path CHANGELOG}`
75+
);
6376

64-
// Prompt the release engineer to tag the commit and update the CHANGELOG.
65-
// (The script could automatically do this, but this seems safer.)
66-
console.log();
67-
console.log(
68-
theme.header`Tag the source for this release in Git with the following command:`
69-
);
70-
console.log(
71-
theme` {command git tag -a v}{version %s} {command -m "v%s"} {version %s}`,
72-
version,
73-
version,
74-
commit
75-
);
76-
console.log(theme.command` git push origin --tags`);
77+
// Prompt the release engineer to tag the commit and update the CHANGELOG.
78+
// (The script could automatically do this, but this seems safer.)
79+
console.log();
80+
console.log(
81+
theme.header`Tag the source for this release in Git with the following command:`
82+
);
83+
console.log(
84+
theme` {command git tag -a v}{version %s} {command -m "v%s"} {version %s}`,
85+
version,
86+
version,
87+
commit
88+
);
89+
console.log(theme.command` git push origin --tags`);
7790

78-
console.log();
79-
console.log(theme.header`Lastly, please fill in the release on GitHub.`);
80-
console.log(
81-
theme.link`https://github.com/facebook/react/releases/tag/v%s`,
82-
version
83-
);
84-
console.log(
85-
theme`\nThe GitHub release should also include links to the following artifacts:`
86-
);
87-
for (let i = 0; i < packages.length; i++) {
88-
const packageName = packages[i];
89-
if (existsSync(join(nodeModulesPath, packageName, 'umd'))) {
90-
const {version: packageVersion} = readJsonSync(
91-
join(nodeModulesPath, packageName, 'package.json')
92-
);
93-
console.log(
94-
theme`{path • %s:} {link https://unpkg.com/%s@%s/umd/}`,
95-
packageName,
96-
packageName,
97-
packageVersion
98-
);
91+
console.log();
92+
console.log(theme.header`Lastly, please fill in the release on GitHub.`);
93+
console.log(
94+
theme.link`https://github.com/facebook/react/releases/tag/v%s`,
95+
version
96+
);
97+
console.log(
98+
theme`\nThe GitHub release should also include links to the following artifacts:`
99+
);
100+
for (let i = 0; i < packages.length; i++) {
101+
const packageName = packages[i];
102+
if (existsSync(join(nodeModulesPath, packageName, 'umd'))) {
103+
const {version: packageVersion} = readJsonSync(
104+
join(nodeModulesPath, packageName, 'package.json')
105+
);
106+
console.log(
107+
theme`{path • %s:} {link https://unpkg.com/%s@%s/umd/}`,
108+
packageName,
109+
packageName,
110+
packageVersion
111+
);
112+
}
99113
}
100-
}
101114

102-
// Update reactjs.org so the React version shown in the header is up to date.
103-
console.log();
104-
console.log(
105-
theme.header`Once you've pushed changes, update the docs site.`
106-
);
107-
console.log(
108-
'This will ensure that any newly-added error codes can be decoded.'
109-
);
115+
// Update reactjs.org so the React version shown in the header is up to date.
116+
console.log();
117+
console.log(
118+
theme.header`Once you've pushed changes, update the docs site.`
119+
);
120+
console.log(
121+
'This will ensure that any newly-added error codes can be decoded.'
122+
);
110123

111-
console.log();
124+
console.log();
125+
}
112126
}
113127
};
114128

scripts/release/publish-commands/publish-to-npm.js

Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,70 +3,60 @@
33
'use strict';
44

55
const {exec} = require('child-process-promise');
6-
const clear = require('clear');
76
const {readJsonSync} = require('fs-extra');
87
const {join} = require('path');
98
const {confirm, execRead} = require('../utils');
109
const theme = require('../theme');
1110

12-
const run = async ({cwd, dry, packages, tags}, otp) => {
13-
clear();
11+
const run = async ({cwd, dry, tags}, packageName, otp) => {
12+
const packagePath = join(cwd, 'build/node_modules', packageName);
13+
const {version} = readJsonSync(join(packagePath, 'package.json'));
1414

15-
for (let i = 0; i < packages.length; i++) {
16-
const packageName = packages[i];
17-
const packagePath = join(cwd, 'build/node_modules', packageName);
18-
const {version} = readJsonSync(join(packagePath, 'package.json'));
15+
// Check if this package version has already been published.
16+
// If so we might be resuming from a previous run.
17+
// We could infer this by comparing the build-info.json,
18+
// But for now the easiest way is just to ask if this is expected.
19+
const info = await execRead(`npm view ${packageName}@${version}`);
20+
if (info) {
21+
console.log(
22+
theme`{package ${packageName}} {version ${version}} has already been published.`
23+
);
24+
await confirm('Is this expected?');
25+
} else {
26+
console.log(theme`{spinnerSuccess ✓} Publishing {package ${packageName}}`);
1927

20-
// Check if this package version has already been published.
21-
// If so we might be resuming from a previous run.
22-
// We could infer this by comparing the build-info.json,
23-
// But for now the easiest way is just to ask if this is expected.
24-
const info = await execRead(`npm view ${packageName}@${version}`);
25-
if (info) {
26-
console.log(
27-
theme`{package ${packageName}} {version ${version}} has already been published.`
28-
);
29-
await confirm('Is this expected?');
30-
} else {
31-
console.log(
32-
theme`{spinnerSuccess ✓} Publishing {package ${packageName}}`
33-
);
28+
// Publish the package and tag it.
29+
if (!dry) {
30+
await exec(`npm publish --tag=${tags[0]} --otp=${otp}`, {
31+
cwd: packagePath,
32+
});
33+
}
34+
console.log(theme.command(` cd ${packagePath}`));
35+
console.log(theme.command(` npm publish --tag=${tags[0]} --otp=${otp}`));
3436

35-
// Publish the package and tag it.
37+
for (let j = 1; j < tags.length; j++) {
3638
if (!dry) {
37-
await exec(`npm publish --tag=${tags[0]} --otp=${otp}`, {
38-
cwd: packagePath,
39-
});
40-
}
41-
console.log(theme.command(` cd ${packagePath}`));
42-
console.log(theme.command(` npm publish --tag=${tags[0]} --otp=${otp}`));
43-
44-
for (let j = 1; j < tags.length; j++) {
45-
if (!dry) {
46-
await exec(
47-
`npm dist-tag add ${packageName}@${version} ${tags[j]} --otp=${otp}`,
48-
{cwd: packagePath}
49-
);
50-
}
51-
console.log(
52-
theme.command(
53-
` npm dist-tag add ${packageName}@${version} ${tags[j]} --otp=${otp}`
54-
)
39+
await exec(
40+
`npm dist-tag add ${packageName}@${version} ${tags[j]} --otp=${otp}`,
41+
{cwd: packagePath}
5542
);
5643
}
44+
console.log(
45+
theme.command(
46+
` npm dist-tag add ${packageName}@${version} ${tags[j]} --otp=${otp}`
47+
)
48+
);
49+
}
5750

58-
if (tags.includes('untagged')) {
59-
// npm doesn't let us publish without a tag at all,
60-
// so for one-off publishes we clean it up ourselves.
61-
if (!dry) {
62-
await exec(`npm dist-tag rm ${packageName} untagged --otp=${otp}`);
63-
}
64-
console.log(
65-
theme.command(
66-
` npm dist-tag rm ${packageName} untagged --otp=${otp}`
67-
)
68-
);
51+
if (tags.includes('untagged')) {
52+
// npm doesn't let us publish without a tag at all,
53+
// so for one-off publishes we clean it up ourselves.
54+
if (!dry) {
55+
await exec(`npm dist-tag rm ${packageName} untagged --otp=${otp}`);
6956
}
57+
console.log(
58+
theme.command(` npm dist-tag rm ${packageName} untagged --otp=${otp}`)
59+
);
7060
}
7161
}
7262
};

scripts/release/publish.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
const {join} = require('path');
66
const {readJsonSync} = require('fs-extra');
7+
const clear = require('clear');
78
const {getPublicPackages, handleError} = require('./utils');
89
const theme = require('./theme');
910

@@ -49,17 +50,22 @@ const run = async () => {
4950
await validateSkipPackages(params);
5051
await checkNPMPermissions(params);
5152

52-
while (true) {
53+
clear();
54+
let otp = await promptForOTP(params);
55+
const packageNames = params.packages;
56+
for (let i = 0; i < packageNames.length; ) {
57+
const packageName = packageNames[i];
5358
try {
54-
const otp = await promptForOTP(params);
55-
await publishToNPM(params, otp);
56-
break;
59+
await publishToNPM(params, packageName, otp);
60+
i++;
5761
} catch (error) {
5862
console.error(error.message);
5963
console.log();
6064
console.log(
6165
theme.error`Publish failed. Enter a fresh otp code to retry.`
6266
);
67+
otp = await promptForOTP(params);
68+
// Try publishing package again
6369
continue;
6470
}
6571
}

0 commit comments

Comments
 (0)