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:
Родитель
273a5177e0
Коммит
d190ccafd3
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче