Skip to content

Commit cfb6c17

Browse files
huntiefacebook-github-bot
authored andcommitted
Use npm as source of truth for updated packages (make publish script rerunnable)
Summary: Updates `find-and-publish-all-bumped-packages` to use the npm registry as the source of truth, similar to tools like Lerna (`lerna publish from-package`). **This enables safe reruns of the publish script**, and replaces the previous Git-diff-detection implementation. Changelog: [Internal] Differential Revision: D53607807
1 parent ce0f9cd commit cfb6c17

File tree

2 files changed

+68
-69
lines changed

2 files changed

+68
-69
lines changed

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

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ const {
1616

1717
const getPackagesMock = jest.fn();
1818
const execSync = jest.fn();
19-
const spawnSync = jest.fn();
2019
const execMock = jest.fn();
20+
const fetchMock = jest.fn();
2121

22-
jest.mock('child_process', () => ({execSync, spawnSync}));
22+
jest.mock('child_process', () => ({execSync}));
2323
jest.mock('shelljs', () => ({exec: execMock}));
2424
jest.mock('../../releases/utils/monorepo', () => ({
2525
getPackages: getPackagesMock,
2626
}));
27+
// $FlowIgnore[cannot-write]
28+
global.fetch = fetchMock;
2729

2830
const BUMP_COMMIT_MESSAGE =
2931
'bumped packages versions\n\n#publish-packages-to-npm';
@@ -76,7 +78,7 @@ describe('findAndPublishAllBumpedPackages', () => {
7678
`);
7779
});
7880

79-
test('should throw an error if updated version is not 0.x.y', async () => {
81+
test('should throw an error if updated version is not 0.x.x', async () => {
8082
execSync.mockImplementation((command: string) => {
8183
switch (command) {
8284
case 'git log -1 --pretty=%B':
@@ -93,16 +95,16 @@ describe('findAndPublishAllBumpedPackages', () => {
9395
},
9496
});
9597

96-
spawnSync.mockImplementationOnce(() => ({
97-
stdout: `- "version": "0.72.0"\n+ "version": "${mockedPackageNewVersion}"\n`,
98-
}));
98+
fetchMock.mockResolvedValueOnce({
99+
json: () => Promise.resolve({versions: {}}),
100+
});
99101

100102
await expect(findAndPublishAllBumpedPackages()).rejects.toThrow(
101-
`Package version expected to be 0.x.y, but received ${mockedPackageNewVersion}`,
103+
`Package version expected to be 0.x.x, but received ${mockedPackageNewVersion}`,
102104
);
103105
});
104106

105-
test('should publish all changed packages', async () => {
107+
test('should publish all updated packages', async () => {
106108
execSync.mockImplementation((command: string) => {
107109
switch (command) {
108110
case 'git log -1 --pretty=%B':
@@ -111,39 +113,48 @@ describe('findAndPublishAllBumpedPackages', () => {
111113
});
112114
getPackagesMock.mockResolvedValue({
113115
'@react-native/package-a': {
116+
name: '@react-native/package-a',
114117
path: 'absolute/path/to/package-a',
115118
packageJson: {
116119
version: '0.72.1',
117120
},
118121
},
119122
'@react-native/package-b': {
123+
name: '@react-native/package-b',
120124
path: 'absolute/path/to/package-b',
121125
packageJson: {
122126
version: '0.72.1',
123127
},
124128
},
125129
'@react-native/package-c': {
130+
name: '@react-native/package-c',
126131
path: 'absolute/path/to/package-c',
127132
packageJson: {
128133
version: '0.72.0',
129134
},
130135
},
131136
});
132-
133-
spawnSync.mockImplementationOnce(() => ({
134-
stdout: `- "version": "0.72.0"\n+ "version": "0.72.1"\n`,
135-
}));
136-
spawnSync.mockImplementationOnce(() => ({
137-
stdout: `- "version": "0.72.0"\n+ "version": "0.72.1"\n`,
138-
}));
139-
spawnSync.mockImplementationOnce(() => ({
140-
stdout: '\n',
141-
}));
142-
137+
fetchMock.mockResolvedValue({
138+
json: () =>
139+
Promise.resolve({
140+
versions: {'0.72.0': {}},
141+
}),
142+
});
143143
execMock.mockImplementation(() => ({code: 0}));
144144

145+
const consoleLog = jest.spyOn(console, 'log').mockImplementation(() => {});
146+
145147
await findAndPublishAllBumpedPackages();
146148

149+
expect(consoleLog.mock.calls.flat().join('\n')).toMatchInlineSnapshot(`
150+
"Discovering updated packages
151+
- Skipping @react-native/package-c (0.72.0 already present on npm)
152+
Done ✅
153+
Publishing updated packages to npm
154+
- Publishing @react-native/package-a (0.72.1)
155+
- Publishing @react-native/package-b (0.72.1)
156+
Done ✅"
157+
`);
147158
expect(execMock.mock.calls).toMatchInlineSnapshot(`
148159
Array [
149160
Array [

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

Lines changed: 38 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@
1212
const {publishPackage} = require('../npm-utils');
1313
const {getPackages} = require('../releases/utils/monorepo');
1414
const {PUBLISH_PACKAGES_TAG} = require('./constants');
15-
const {execSync, spawnSync} = require('child_process');
16-
const path = require('path');
15+
const {execSync} = require('child_process');
1716

18-
const ROOT_LOCATION = path.join(__dirname, '..', '..');
1917
const NPM_CONFIG_OTP = process.env.NPM_CONFIG_OTP;
2018

2119
async function findAndPublishAllBumpedPackages() {
@@ -36,76 +34,66 @@ async function findAndPublishAllBumpedPackages() {
3634
return;
3735
}
3836

39-
const tags = getTagsFromCommitMessage(commitMessage);
40-
41-
console.log('Traversing all packages inside /packages...');
37+
console.log('Discovering updated packages');
4238

4339
const packages = await getPackages({
4440
includeReactNative: false,
4541
});
42+
const packagesToUpdate = [];
4643

47-
for (const package of Object.values(packages)) {
48-
const {stdout: diff, stderr: commitDiffStderr} = spawnSync(
49-
'git',
50-
[
51-
'log',
52-
'-p',
53-
'--format=""',
54-
'HEAD~1..HEAD',
55-
`${package.path}/package.json`,
56-
],
57-
{cwd: ROOT_LOCATION, shell: true, stdio: 'pipe', encoding: 'utf-8'},
58-
);
44+
await Promise.all(
45+
Object.values(packages).map(async package => {
46+
const version = package.packageJson.version;
5947

60-
if (commitDiffStderr) {
61-
console.log(
62-
`\u274c Failed to get latest committed changes for ${package.name}:`,
63-
);
64-
console.log(commitDiffStderr);
48+
if (!version.startsWith('0.')) {
49+
throw new Error(
50+
`Package version expected to be 0.x.x, but received ${version}`,
51+
);
52+
}
6553

66-
process.exit(1);
67-
}
54+
const response = await fetch(
55+
'https://registry.npmjs.org/' + package.name,
56+
);
57+
const {versions: versionsInRegistry} = await response.json();
6858

69-
const previousVersionPatternMatches = diff
70-
.toString()
71-
.match(/- {2}"version": "([0-9]+.[0-9]+.[0-9]+)"/);
59+
if (version in versionsInRegistry) {
60+
console.log(
61+
`- Skipping ${package.name} (${version} already present on npm)`,
62+
);
63+
return;
64+
}
7265

73-
if (!previousVersionPatternMatches) {
74-
console.log(`\uD83D\uDD0E No version bump for ${package.name}`);
66+
packagesToUpdate.push(package.name);
67+
}),
68+
);
7569

76-
return;
77-
}
70+
console.log('Done ✅');
71+
console.log('Publishing updated packages to npm');
7872

79-
const [, previousVersion] = previousVersionPatternMatches;
80-
const nextVersion = package.packageJson.version;
73+
const tags = getTagsFromCommitMessage(commitMessage);
8174

75+
for (const packageName of packagesToUpdate) {
76+
const package = packages[packageName];
8277
console.log(
83-
`\uD83D\uDCA1 ${package.name} was updated: ${previousVersion} -> ${nextVersion}`,
78+
`- Publishing ${package.name} (${package.packageJson.version})`,
8479
);
8580

86-
if (!nextVersion.startsWith('0.')) {
87-
throw new Error(
88-
`Package version expected to be 0.x.y, but received ${nextVersion}`,
89-
);
90-
}
91-
9281
const result = publishPackage(package.path, {
9382
tags,
9483
otp: NPM_CONFIG_OTP,
9584
});
96-
if (result.code !== 0) {
97-
console.log(
98-
`\u274c Failed to publish version ${nextVersion} of ${package.name}. npm publish exited with code ${result.code}:`,
99-
);
100-
console.log(result.stderr);
10185

102-
process.exit(1);
103-
} else {
104-
console.log(
105-
`\u2705 Successfully published new version of ${package.name}`,
86+
if (result.code !== 0) {
87+
console.error(
88+
`Failed to publish ${package.name}. npm publish exited with code ${result.code}:`,
10689
);
90+
console.error(result.stderr);
91+
process.exitCode = 1;
92+
return;
10793
}
10894
}
95+
96+
console.log('Done ✅');
10997
}
11098

11199
function getTagsFromCommitMessage(msg /*: string */) /*: Array<string> */ {

0 commit comments

Comments
 (0)