Skip to content

Commit fe0306d

Browse files
lunaleapsfacebook-github-bot
authored andcommitted
Support specifying dist-tags for monorepo package bumps (#42146)
Summary: Currently our CI will auto-tag any `npm publish` as `latest` for the monorepo packages. This is because [we do not specify a tag](https://github.com/facebook/react-native/blob/main/scripts/monorepo/find-and-publish-all-bumped-packages.js#L104), so npm will [default to `latest`](https://docs.npmjs.com/cli/v10/commands/npm-dist-tag#description). We encountered a similar issue for `react-native` awhile ago and fixed that with [always specifying a tag](https://github.com/facebook/react-native/blob/main/scripts/npm-utils.js#L84), with the explicit opt-in for `latest`. yarn and npm will resolve `*` dependencies using `latest`. This will be a problem for any React Native version that uses `*` deps. We have actively tried to remove these `*` versions but older patches may still contain them. When we do a monorepo package bump, it may be for 0.71 and for a user who is initializing a 0.72 version project (that still has * deps), they will receive monorepo packages of version `0.71.x`, which is not compatible. (React Native monorepo packages do not faithfully follow semver) This change allows us to specify what tags to use and suggest tags based on what branch you are on and asks for confirmation ``` > branch 0.73-stable ? Select suggested npm tags. (Press <space> to select, <a> to toggle all, <i> to invert selection) ❯◉ "0.73-stable" ◉ "latest" ? Confirm these tags for *ALL* packages being bumped: "0.73-stable","latest" (Y/n) > branch 0.72-stable ? Select suggested npm tags. (Press <space> to select, <a> to toggle all, <i> to invert selection) ❯◉ "0.72-stable" ◯ "latest" ? Confirm these tags for *ALL* packages being bumped: "0.72-stable" (Y/n) > branch main ? Select suggested npm tags. (Press <space> to select, <a> to toggle all, <i> to invert selection) ❯◉ "nightly" ? Confirm these tags for *ALL* packages being bumped: "nightly" (Y/n) ``` ## Changelog: [INTERNAL] [CHANGED] - Support dist-tags in publishing monorepo packages to avoid default "latest" tag. Pull Request resolved: #42146 Test Plan: `yarn test scripts/` Reviewed By: NickGerleman Differential Revision: D52551769 Pulled By: lunaleaps fbshipit-source-id: 52f923464387cffdc6ca22c6f0a45425965a3680
1 parent 8c14997 commit fe0306d

8 files changed

+116
-14
lines changed

scripts/__tests__/npm-utils-test.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ describe('npm-utils', () => {
9595
it('should run publish command', () => {
9696
publishPackage(
9797
'path/to/my-package',
98-
{tag: 'latest', otp: 'otp'},
98+
{tags: ['latest'], otp: 'otp'},
9999
{silent: true, cwd: 'i/expect/this/to/be/overriden'},
100100
);
101101
expect(execMock).toHaveBeenCalledWith(
@@ -105,12 +105,23 @@ describe('npm-utils', () => {
105105
});
106106

107107
it('should run publish command when no execOptions', () => {
108-
publishPackage('path/to/my-package', {tag: 'latest', otp: 'otp'});
108+
publishPackage('path/to/my-package', {tags: ['latest'], otp: 'otp'});
109109
expect(execMock).toHaveBeenCalledWith(
110110
'npm publish --tag latest --otp otp',
111111
{cwd: 'path/to/my-package'},
112112
);
113113
});
114+
115+
it('should handle multiple tags', () => {
116+
publishPackage('path/to/my-package', {
117+
tags: ['next', '0.72-stable'],
118+
otp: 'otp',
119+
});
120+
expect(execMock).toHaveBeenCalledWith(
121+
'npm publish --tag next --tag 0.72-stable --otp otp',
122+
{cwd: 'path/to/my-package'},
123+
);
124+
});
114125
});
115126

116127
describe('getNpmInfo', () => {

scripts/monorepo/__tests__/find-and-publish-all-bumped-packages-test.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
*/
99

1010
const {PUBLISH_PACKAGES_TAG} = require('../constants');
11-
const findAndPublishAllBumpedPackages = require('../find-and-publish-all-bumped-packages');
11+
const {
12+
findAndPublishAllBumpedPackages,
13+
getTagsFromCommitMessage,
14+
} = require('../find-and-publish-all-bumped-packages');
1215
const forEachPackage = require('../for-each-package');
1316
const {spawnSync} = require('child_process');
1417

@@ -42,3 +45,14 @@ describe('findAndPublishAllBumpedPackages', () => {
4245
);
4346
});
4447
});
48+
49+
describe('getTagsFromCommitMessage', () => {
50+
it('should parse tags out', () => {
51+
const commitMsg = `This may be any commit message before it like tag a \n\n${PUBLISH_PACKAGES_TAG}&tagA&tagB&tagA\n`;
52+
expect(getTagsFromCommitMessage(commitMsg)).toEqual([
53+
'tagA',
54+
'tagB',
55+
'tagA',
56+
]);
57+
});
58+
});

scripts/monorepo/__tests__/get-and-update-packages-test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describe('getAndUpdatePackages', () => {
8888
forEachPackageThatShouldBePublished(package => {
8989
expect(publishPackageMock).toHaveBeenCalledWith(
9090
package.packageAbsolutePath,
91-
{otp: undefined, tag: 'nightly'},
91+
{otp: undefined, tags: ['nightly']},
9292
);
9393
});
9494

@@ -154,7 +154,7 @@ describe('getAndUpdatePackages', () => {
154154
forEachPackageThatShouldBePublished(package => {
155155
expect(publishPackageMock).toHaveBeenCalledWith(
156156
package.packageAbsolutePath,
157-
{otp: undefined, tag: 'prealpha'},
157+
{otp: undefined, tags: ['prealpha']},
158158
);
159159
});
160160

scripts/monorepo/bump-all-updated-packages/index.js

+57-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
* @format
88
*/
99

10+
const {getPackageVersionStrByTag} = require('../../npm-utils');
11+
const {getBranchName} = require('../../scm-utils');
12+
const {isReleaseBranch, parseVersion} = require('../../version-utils');
1013
const alignPackageVersions = require('../align-package-versions');
1114
const checkForGitChanges = require('../check-for-git-changes');
1215
const {
@@ -148,6 +151,58 @@ const main = async () => {
148151
alignPackageVersions();
149152
echo(chalk.green('Done!\n'));
150153

154+
// Figure out the npm dist-tags we want for all monorepo packages we're bumping
155+
const branchName = getBranchName();
156+
const choices = [];
157+
158+
if (branchName === 'main') {
159+
choices.push({name: '"nightly"', value: 'nightly', checked: true});
160+
} else if (isReleaseBranch(branchName)) {
161+
choices.push({
162+
name: `"${branchName}"`,
163+
value: branchName,
164+
checked: true,
165+
});
166+
167+
const latestVersion = getPackageVersionStrByTag('react-native', 'latest');
168+
const {major, minor} = parseVersion(latestVersion, 'release');
169+
choices.push({
170+
name: '"latest"',
171+
value: 'latest',
172+
checked: `${major}.${minor}-stable` === branchName,
173+
});
174+
} else {
175+
echo(
176+
'You should be running `yarn bump-all-updated-packages` only from release or main branch',
177+
);
178+
exit(1);
179+
}
180+
181+
const {tags} = await inquirer.prompt([
182+
{
183+
type: 'checkbox',
184+
name: 'tags',
185+
message: 'Select suggested npm tags.',
186+
choices,
187+
required: true,
188+
},
189+
]);
190+
191+
const {confirm} = await inquirer.prompt({
192+
type: 'confirm',
193+
name: 'confirm',
194+
message: `Confirm these tags for *ALL* packages being bumped: ${tags
195+
.map(t => `"${t}"`)
196+
.join()}`,
197+
});
198+
199+
if (!confirm) {
200+
echo('Exiting without commiting...');
201+
exit(0);
202+
}
203+
204+
const tagString = '&' + tags.join('&');
205+
151206
await inquirer
152207
.prompt([
153208
{
@@ -179,7 +234,7 @@ const main = async () => {
179234
}
180235

181236
case COMMIT_WITH_GENERIC_MESSAGE_CHOICE: {
182-
exec(`git commit -am "${GENERIC_COMMIT_MESSAGE}"`, {
237+
exec(`git commit -am "${GENERIC_COMMIT_MESSAGE}${tagString}"`, {
183238
cwd: ROOT_LOCATION,
184239
silent: true,
185240
});
@@ -197,7 +252,7 @@ const main = async () => {
197252
silent: true,
198253
}).stdout.trim();
199254
const commitMessageWithTag =
200-
enteredCommitMessage + `\n\n${PUBLISH_PACKAGES_TAG}`;
255+
enteredCommitMessage + `\n\n${PUBLISH_PACKAGES_TAG}${tagString}`;
201256

202257
exec(`git commit --amend -m "${commitMessageWithTag}"`, {
203258
cwd: ROOT_LOCATION,

scripts/monorepo/find-and-publish-all-bumped-packages.js

+24-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ const path = require('path');
1616
const ROOT_LOCATION = path.join(__dirname, '..', '..');
1717
const NPM_CONFIG_OTP = process.env.NPM_CONFIG_OTP;
1818

19+
function getTagsFromCommitMessage(msg) {
20+
// ex message we're trying to parse tags out of
21+
// `_some_message_here_${PUBLISH_PACKAGES_TAG}&tagA&tagB\n`;
22+
return msg
23+
.substring(msg.indexOf(PUBLISH_PACKAGES_TAG))
24+
.trim()
25+
.split('&')
26+
.slice(1);
27+
}
28+
1929
const findAndPublishAllBumpedPackages = () => {
2030
console.log('Traversing all packages inside /packages...');
2131

@@ -101,7 +111,12 @@ const findAndPublishAllBumpedPackages = () => {
101111
);
102112
}
103113

104-
const result = publishPackage(packageAbsolutePath, {otp: NPM_CONFIG_OTP});
114+
const tags = getTagsFromCommitMessage(commitMessage);
115+
116+
const result = publishPackage(packageAbsolutePath, {
117+
tags,
118+
otp: NPM_CONFIG_OTP,
119+
});
105120
if (result.code !== 0) {
106121
console.log(
107122
`\u274c Failed to publish version ${nextVersion} of ${packageManifest.name}. npm publish exited with code ${result.code}:`,
@@ -120,4 +135,11 @@ const findAndPublishAllBumpedPackages = () => {
120135
process.exit(0);
121136
};
122137

123-
findAndPublishAllBumpedPackages();
138+
if (require.main === module) {
139+
findAndPublishAllBumpedPackages();
140+
}
141+
142+
module.exports = {
143+
findAndPublishAllBumpedPackages,
144+
getTagsFromCommitMessage,
145+
};

scripts/monorepo/get-and-update-packages.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ function publishPackages(
135135
const version = packageMetadata.packageJson.version;
136136

137137
const result = publishPackage(packageAbsolutePath, {
138-
tag: tag,
138+
tags: [tag],
139139
otp: process.env.NPM_CONFIG_OTP,
140140
});
141141

scripts/npm-utils.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,14 @@ function getNpmInfo(buildType) {
9191
}
9292

9393
function publishPackage(packagePath, packageOptions, execOptions) {
94-
const {tag, otp} = packageOptions;
95-
const tagFlag = tag ? ` --tag ${tag}` : '';
94+
const {otp, tags} = packageOptions;
95+
const tagsFlag = tags ? tags.map(t => ` --tag ${t}`).join('') : '';
9696
const otpFlag = otp ? ` --otp ${otp}` : '';
9797
const options = execOptions
9898
? {...execOptions, cwd: packagePath}
9999
: {cwd: packagePath};
100100

101-
return exec(`npm publish${tagFlag}${otpFlag}`, options);
101+
return exec(`npm publish${tagsFlag}${otpFlag}`, options);
102102
}
103103

104104
function diffPackages(packageSpecA, packageSpecB, options) {

scripts/publish-npm.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ function publishNpm(buildType) {
9696

9797
const packagePath = path.join(__dirname, '..', 'packages', 'react-native');
9898
const result = publishPackage(packagePath, {
99-
tag,
99+
tags: [tag],
100100
otp: process.env.NPM_CONFIG_OTP,
101101
});
102102

0 commit comments

Comments
 (0)