Add retry to monorepo publish script (#42964)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/42964

We've seen npm publishes fail occasionally in CI as part of this script, most recently in S391653. This change adds a single retry, per package, during the execution of this script, in an attempt to reduce the chance of manual interventions after a broken pipeline.

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D53607808

fbshipit-source-id: 526d9c33d51ec57702efba3c199bad313c1bf2d4
This commit is contained in:
Alex Hunt 2024-02-12 10:43:48 -08:00 коммит произвёл Facebook GitHub Bot
Родитель 273a5177e0
Коммит d190ccafd3
2 изменённых файлов: 126 добавлений и 12 удалений

Просмотреть файл

@ -172,6 +172,98 @@ describe('findAndPublishAllBumpedPackages', () => {
]
`);
});
describe('retry behaviour', () => {
beforeEach(() => {
execSync.mockImplementation((command: string) => {
switch (command) {
case 'git log -1 --pretty=%B':
return BUMP_COMMIT_MESSAGE;
}
});
getPackagesMock.mockResolvedValue({
'@react-native/package-a': {
name: '@react-native/package-a',
path: 'absolute/path/to/package-a',
packageJson: {
version: '0.72.1',
},
},
'@react-native/package-b': {
name: '@react-native/package-b',
path: 'absolute/path/to/package-b',
packageJson: {
version: '0.72.1',
},
},
});
fetchMock.mockResolvedValue({
json: () =>
Promise.resolve({
versions: {'0.72.0': {}},
}),
});
});
test('should retry once if `npm publish` fails', async () => {
execMock.mockImplementationOnce(() => ({code: 0}));
execMock.mockImplementationOnce(() => ({
code: 1,
stderr: '503 Service Unavailable',
}));
execMock.mockImplementationOnce(() => ({code: 0}));
const consoleError = jest
.spyOn(console, 'error')
.mockImplementation(() => {});
await findAndPublishAllBumpedPackages();
expect(consoleError.mock.calls.flat().join('\n')).toMatchInlineSnapshot(`
"Failed to publish @react-native/package-b. npm publish exited with code 1:
503 Service Unavailable"
`);
expect(execMock.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"npm publish",
Object {
"cwd": "absolute/path/to/package-a",
},
],
Array [
"npm publish",
Object {
"cwd": "absolute/path/to/package-b",
},
],
Array [
"npm publish",
Object {
"cwd": "absolute/path/to/package-b",
},
],
]
`);
});
test('should exit with error if one or more packages fail after retry', async () => {
execMock.mockImplementationOnce(() => ({code: 0}));
execMock.mockImplementation(() => ({
code: 1,
stderr: '503 Service Unavailable',
}));
const consoleLog = jest
.spyOn(console, 'log')
.mockImplementation(() => {});
await findAndPublishAllBumpedPackages();
expect(consoleLog).toHaveBeenLastCalledWith('--- Retrying once! ---');
expect(process.exitCode).toBe(1);
});
});
});
describe('getTagsFromCommitMessage', () => {

Просмотреть файл

@ -71,6 +71,7 @@ async function findAndPublishAllBumpedPackages() {
console.log('Publishing updated packages to npm');
const tags = getTagsFromCommitMessage(commitMessage);
const failedPackages = [];
for (const packageName of packagesToUpdate) {
const package = packages[packageName];
@ -78,21 +79,23 @@ async function findAndPublishAllBumpedPackages() {
`- Publishing ${package.name} (${package.packageJson.version})`,
);
const result = publishPackage(package.path, {
tags,
otp: NPM_CONFIG_OTP,
});
if (result.code !== 0) {
console.error(
`Failed to publish ${package.name}. npm publish exited with code ${result.code}:`,
);
console.error(result.stderr);
process.exitCode = 1;
return;
try {
runPublish(package.name, package.path, tags);
} catch {
console.log('--- Retrying once! ---');
try {
runPublish(package.name, package.path, tags);
} catch (e) {
failedPackages.push(package.name);
}
}
}
if (failedPackages.length) {
process.exitCode = 1;
return;
}
console.log('Done ✅');
}
@ -106,6 +109,25 @@ function getTagsFromCommitMessage(msg /*: string */) /*: Array<string> */ {
.slice(1);
}
function runPublish(
packageName /*: string */,
packagePath /*: string */,
tags /*: Array<string> */,
) {
const result = publishPackage(packagePath, {
tags,
otp: NPM_CONFIG_OTP,
});
if (result.code !== 0) {
console.error(
`Failed to publish ${packageName}. npm publish exited with code ${result.code}:`,
);
console.error(result.stderr);
throw new Error(result.stderr);
}
}
if (require.main === module) {
// eslint-disable-next-line no-void
void findAndPublishAllBumpedPackages();