Skip to content

Commit

Permalink
fix(cli): ensure an empty event loop counts as an error (#6399)
Browse files Browse the repository at this point in the history
**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.
  • Loading branch information
merceyz committed Jul 18, 2024
1 parent 35167b2 commit 9cc3c79
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .yarn/versions/3a4fece1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
releases:
"@yarnpkg/cli": patch

declined:
- "@yarnpkg/plugin-compat"
- "@yarnpkg/plugin-constraints"
- "@yarnpkg/plugin-dlx"
- "@yarnpkg/plugin-essentials"
- "@yarnpkg/plugin-init"
- "@yarnpkg/plugin-interactive-tools"
- "@yarnpkg/plugin-nm"
- "@yarnpkg/plugin-npm-cli"
- "@yarnpkg/plugin-pack"
- "@yarnpkg/plugin-patch"
- "@yarnpkg/plugin-pnp"
- "@yarnpkg/plugin-pnpm"
- "@yarnpkg/plugin-stage"
- "@yarnpkg/plugin-typescript"
- "@yarnpkg/plugin-version"
- "@yarnpkg/plugin-workspace-tools"
- "@yarnpkg/builder"
- "@yarnpkg/core"
- "@yarnpkg/doctor"
Original file line number Diff line number Diff line change
Expand Up @@ -900,5 +900,30 @@ describe(`Commands`, () => {
});
}),
);

test(`it should exit with an error code after an unexpected empty event loop`,
makeTemporaryEnv({}, async ({path, run}) => {
await xfs.writeFilePromise(ppath.join(path, `plugin.cjs`), `
module.exports = {
name: 'test',
factory() {
return {
hooks: {
afterAllInstalled: () => new Promise(() => {}),
},
};
},
};
`);
await xfs.writeJsonPromise(ppath.join(path, Filename.rc), {
plugins: [`./plugin.cjs`],
});

await expect(run(`install`)).rejects.toMatchObject({
code: 42,
stdout: expect.stringContaining(`Yarn is terminating due to an unexpected empty event loop`),
});
}),
);
});
});
12 changes: 12 additions & 0 deletions packages/yarnpkg-cli/sources/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,24 @@ export async function getCli({cwd = ppath.cwd(), pluginConfiguration = getPlugin
export async function runExit(argv: Array<string>, {cwd = ppath.cwd(), selfPath, pluginConfiguration}: {cwd: PortablePath, selfPath: PortablePath | null, pluginConfiguration: PluginConfiguration}) {
const cli = getBaseCli({cwd, pluginConfiguration});

function unexpectedTerminationHandler() {
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.`);
}

process.once(`beforeExit`, unexpectedTerminationHandler);

try {
// The exit code is set to an error code before the CLI runs so that
// if the event loop becomes empty and node terminates without
// finishing the execution of this function it counts as an error.
// https://github.com/yarnpkg/berry/issues/6398
process.exitCode = 42;
process.exitCode = await run(cli, argv, {selfPath, pluginConfiguration});
} catch (error) {
Cli.defaultContext.stdout.write(cli.error(error));
process.exitCode = 1;
} finally {
process.off(`beforeExit`, unexpectedTerminationHandler);
await xfs.rmtempPromise();
}
}

0 comments on commit 9cc3c79

Please sign in to comment.