Skip to content

Commit

Permalink
Support specifying dist-tags for monorepo package bumps (#42146)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
lunaleaps authored and facebook-github-bot committed Jan 8, 2024
1 parent 8c14997 commit fe0306d
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 14 deletions.
15 changes: 13 additions & 2 deletions scripts/__tests__/npm-utils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe('npm-utils', () => {
it('should run publish command', () => {
publishPackage(
'path/to/my-package',
{tag: 'latest', otp: 'otp'},
{tags: ['latest'], otp: 'otp'},
{silent: true, cwd: 'i/expect/this/to/be/overriden'},
);
expect(execMock).toHaveBeenCalledWith(
Expand All @@ -105,12 +105,23 @@ describe('npm-utils', () => {
});

it('should run publish command when no execOptions', () => {
publishPackage('path/to/my-package', {tag: 'latest', otp: 'otp'});
publishPackage('path/to/my-package', {tags: ['latest'], otp: 'otp'});
expect(execMock).toHaveBeenCalledWith(
'npm publish --tag latest --otp otp',
{cwd: 'path/to/my-package'},
);
});

it('should handle multiple tags', () => {
publishPackage('path/to/my-package', {
tags: ['next', '0.72-stable'],
otp: 'otp',
});
expect(execMock).toHaveBeenCalledWith(
'npm publish --tag next --tag 0.72-stable --otp otp',
{cwd: 'path/to/my-package'},
);
});
});

describe('getNpmInfo', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
*/

const {PUBLISH_PACKAGES_TAG} = require('../constants');
const findAndPublishAllBumpedPackages = require('../find-and-publish-all-bumped-packages');
const {
findAndPublishAllBumpedPackages,
getTagsFromCommitMessage,
} = require('../find-and-publish-all-bumped-packages');
const forEachPackage = require('../for-each-package');
const {spawnSync} = require('child_process');

Expand Down Expand Up @@ -42,3 +45,14 @@ describe('findAndPublishAllBumpedPackages', () => {
);
});
});

describe('getTagsFromCommitMessage', () => {
it('should parse tags out', () => {
const commitMsg = `This may be any commit message before it like tag a \n\n${PUBLISH_PACKAGES_TAG}&tagA&tagB&tagA\n`;
expect(getTagsFromCommitMessage(commitMsg)).toEqual([
'tagA',
'tagB',
'tagA',
]);
});
});
4 changes: 2 additions & 2 deletions scripts/monorepo/__tests__/get-and-update-packages-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ describe('getAndUpdatePackages', () => {
forEachPackageThatShouldBePublished(package => {
expect(publishPackageMock).toHaveBeenCalledWith(
package.packageAbsolutePath,
{otp: undefined, tag: 'nightly'},
{otp: undefined, tags: ['nightly']},
);
});

Expand Down Expand Up @@ -154,7 +154,7 @@ describe('getAndUpdatePackages', () => {
forEachPackageThatShouldBePublished(package => {
expect(publishPackageMock).toHaveBeenCalledWith(
package.packageAbsolutePath,
{otp: undefined, tag: 'prealpha'},
{otp: undefined, tags: ['prealpha']},
);
});

Expand Down
59 changes: 57 additions & 2 deletions scripts/monorepo/bump-all-updated-packages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
* @format
*/

const {getPackageVersionStrByTag} = require('../../npm-utils');
const {getBranchName} = require('../../scm-utils');
const {isReleaseBranch, parseVersion} = require('../../version-utils');
const alignPackageVersions = require('../align-package-versions');
const checkForGitChanges = require('../check-for-git-changes');
const {
Expand Down Expand Up @@ -148,6 +151,58 @@ const main = async () => {
alignPackageVersions();
echo(chalk.green('Done!\n'));

// Figure out the npm dist-tags we want for all monorepo packages we're bumping
const branchName = getBranchName();
const choices = [];

if (branchName === 'main') {
choices.push({name: '"nightly"', value: 'nightly', checked: true});
} else if (isReleaseBranch(branchName)) {
choices.push({
name: `"${branchName}"`,
value: branchName,
checked: true,
});

const latestVersion = getPackageVersionStrByTag('react-native', 'latest');
const {major, minor} = parseVersion(latestVersion, 'release');
choices.push({
name: '"latest"',
value: 'latest',
checked: `${major}.${minor}-stable` === branchName,
});
} else {
echo(
'You should be running `yarn bump-all-updated-packages` only from release or main branch',
);
exit(1);
}

const {tags} = await inquirer.prompt([
{
type: 'checkbox',
name: 'tags',
message: 'Select suggested npm tags.',
choices,
required: true,
},
]);

const {confirm} = await inquirer.prompt({
type: 'confirm',
name: 'confirm',
message: `Confirm these tags for *ALL* packages being bumped: ${tags
.map(t => `"${t}"`)
.join()}`,
});

if (!confirm) {
echo('Exiting without commiting...');
exit(0);
}

const tagString = '&' + tags.join('&');

await inquirer
.prompt([
{
Expand Down Expand Up @@ -179,7 +234,7 @@ const main = async () => {
}

case COMMIT_WITH_GENERIC_MESSAGE_CHOICE: {
exec(`git commit -am "${GENERIC_COMMIT_MESSAGE}"`, {
exec(`git commit -am "${GENERIC_COMMIT_MESSAGE}${tagString}"`, {
cwd: ROOT_LOCATION,
silent: true,
});
Expand All @@ -197,7 +252,7 @@ const main = async () => {
silent: true,
}).stdout.trim();
const commitMessageWithTag =
enteredCommitMessage + `\n\n${PUBLISH_PACKAGES_TAG}`;
enteredCommitMessage + `\n\n${PUBLISH_PACKAGES_TAG}${tagString}`;

exec(`git commit --amend -m "${commitMessageWithTag}"`, {
cwd: ROOT_LOCATION,
Expand Down
26 changes: 24 additions & 2 deletions scripts/monorepo/find-and-publish-all-bumped-packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ const path = require('path');
const ROOT_LOCATION = path.join(__dirname, '..', '..');
const NPM_CONFIG_OTP = process.env.NPM_CONFIG_OTP;

function getTagsFromCommitMessage(msg) {
// ex message we're trying to parse tags out of
// `_some_message_here_${PUBLISH_PACKAGES_TAG}&tagA&tagB\n`;
return msg
.substring(msg.indexOf(PUBLISH_PACKAGES_TAG))
.trim()
.split('&')
.slice(1);
}

const findAndPublishAllBumpedPackages = () => {
console.log('Traversing all packages inside /packages...');

Expand Down Expand Up @@ -101,7 +111,12 @@ const findAndPublishAllBumpedPackages = () => {
);
}

const result = publishPackage(packageAbsolutePath, {otp: NPM_CONFIG_OTP});
const tags = getTagsFromCommitMessage(commitMessage);

const result = publishPackage(packageAbsolutePath, {
tags,
otp: NPM_CONFIG_OTP,
});
if (result.code !== 0) {
console.log(
`\u274c Failed to publish version ${nextVersion} of ${packageManifest.name}. npm publish exited with code ${result.code}:`,
Expand All @@ -120,4 +135,11 @@ const findAndPublishAllBumpedPackages = () => {
process.exit(0);
};

findAndPublishAllBumpedPackages();
if (require.main === module) {
findAndPublishAllBumpedPackages();
}

module.exports = {
findAndPublishAllBumpedPackages,
getTagsFromCommitMessage,
};
2 changes: 1 addition & 1 deletion scripts/monorepo/get-and-update-packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ function publishPackages(
const version = packageMetadata.packageJson.version;

const result = publishPackage(packageAbsolutePath, {
tag: tag,
tags: [tag],
otp: process.env.NPM_CONFIG_OTP,
});

Expand Down
6 changes: 3 additions & 3 deletions scripts/npm-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ function getNpmInfo(buildType) {
}

function publishPackage(packagePath, packageOptions, execOptions) {
const {tag, otp} = packageOptions;
const tagFlag = tag ? ` --tag ${tag}` : '';
const {otp, tags} = packageOptions;
const tagsFlag = tags ? tags.map(t => ` --tag ${t}`).join('') : '';
const otpFlag = otp ? ` --otp ${otp}` : '';
const options = execOptions
? {...execOptions, cwd: packagePath}
: {cwd: packagePath};

return exec(`npm publish${tagFlag}${otpFlag}`, options);
return exec(`npm publish${tagsFlag}${otpFlag}`, options);
}

function diffPackages(packageSpecA, packageSpecB, options) {
Expand Down
2 changes: 1 addition & 1 deletion scripts/publish-npm.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function publishNpm(buildType) {

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

Expand Down

0 comments on commit fe0306d

Please sign in to comment.