Skip to content

Commit 9cc3c79

Browse files
authored
fix(cli): ensure an empty event loop counts as an error (#6399)
**What's the problem this PR addresses?** If the event loop unexpectedly becomes empty `yarn` will terminate with exit code 0 even though the command didn't complete successfully. Ref nodejs/node#53902 Ref #6398 where that happens and the install is incomplete when `yarn` terminates with exit code 0. It's caught in the following `yarn build` step which can't find the install state. **How did you fix it?** Set `process.exitCode` to an error code before executing the CLI so an unexpected empty event loop counts as an error. **Checklist** - [x] I have read the [Contributing Guide](https://yarnpkg.com/advanced/contributing). - [x] I have set the packages that need to be released for my changes to be effective. - [x] I will check that all automated PR checks pass before the PR gets reviewed.
1 parent 35167b2 commit 9cc3c79

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

.yarn/versions/3a4fece1.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
releases:
2+
"@yarnpkg/cli": patch
3+
4+
declined:
5+
- "@yarnpkg/plugin-compat"
6+
- "@yarnpkg/plugin-constraints"
7+
- "@yarnpkg/plugin-dlx"
8+
- "@yarnpkg/plugin-essentials"
9+
- "@yarnpkg/plugin-init"
10+
- "@yarnpkg/plugin-interactive-tools"
11+
- "@yarnpkg/plugin-nm"
12+
- "@yarnpkg/plugin-npm-cli"
13+
- "@yarnpkg/plugin-pack"
14+
- "@yarnpkg/plugin-patch"
15+
- "@yarnpkg/plugin-pnp"
16+
- "@yarnpkg/plugin-pnpm"
17+
- "@yarnpkg/plugin-stage"
18+
- "@yarnpkg/plugin-typescript"
19+
- "@yarnpkg/plugin-version"
20+
- "@yarnpkg/plugin-workspace-tools"
21+
- "@yarnpkg/builder"
22+
- "@yarnpkg/core"
23+
- "@yarnpkg/doctor"

packages/acceptance-tests/pkg-tests-specs/sources/commands/install.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,5 +900,30 @@ describe(`Commands`, () => {
900900
});
901901
}),
902902
);
903+
904+
test(`it should exit with an error code after an unexpected empty event loop`,
905+
makeTemporaryEnv({}, async ({path, run}) => {
906+
await xfs.writeFilePromise(ppath.join(path, `plugin.cjs`), `
907+
module.exports = {
908+
name: 'test',
909+
factory() {
910+
return {
911+
hooks: {
912+
afterAllInstalled: () => new Promise(() => {}),
913+
},
914+
};
915+
},
916+
};
917+
`);
918+
await xfs.writeJsonPromise(ppath.join(path, Filename.rc), {
919+
plugins: [`./plugin.cjs`],
920+
});
921+
922+
await expect(run(`install`)).rejects.toMatchObject({
923+
code: 42,
924+
stdout: expect.stringContaining(`Yarn is terminating due to an unexpected empty event loop`),
925+
});
926+
}),
927+
);
903928
});
904929
});

packages/yarnpkg-cli/sources/lib.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,24 @@ export async function getCli({cwd = ppath.cwd(), pluginConfiguration = getPlugin
186186
export async function runExit(argv: Array<string>, {cwd = ppath.cwd(), selfPath, pluginConfiguration}: {cwd: PortablePath, selfPath: PortablePath | null, pluginConfiguration: PluginConfiguration}) {
187187
const cli = getBaseCli({cwd, pluginConfiguration});
188188

189+
function unexpectedTerminationHandler() {
190+
Cli.defaultContext.stdout.write(`ERROR: Yarn is terminating due to an unexpected empty event loop.\nPlease report this issue at https://github.com/yarnpkg/berry/issues.`);
191+
}
192+
193+
process.once(`beforeExit`, unexpectedTerminationHandler);
194+
189195
try {
196+
// The exit code is set to an error code before the CLI runs so that
197+
// if the event loop becomes empty and node terminates without
198+
// finishing the execution of this function it counts as an error.
199+
// https://github.com/yarnpkg/berry/issues/6398
200+
process.exitCode = 42;
190201
process.exitCode = await run(cli, argv, {selfPath, pluginConfiguration});
191202
} catch (error) {
192203
Cli.defaultContext.stdout.write(cli.error(error));
193204
process.exitCode = 1;
194205
} finally {
206+
process.off(`beforeExit`, unexpectedTerminationHandler);
195207
await xfs.rmtempPromise();
196208
}
197209
}

0 commit comments

Comments
 (0)