Skip to content

Instantly share code, notes, and snippets.

@romellem
Created February 23, 2024 22:17
Show Gist options
  • Save romellem/40a4b5761a3f2d715345ecd67ffe8059 to your computer and use it in GitHub Desktop.
Save romellem/40a4b5761a3f2d715345ecd67ffe8059 to your computer and use it in GitHub Desktop.
Yarn workspaces parallel run
const {exec, execSync} = require('child_process');
const util = require('util');
const asyncExec = util.promisify(exec);
const red = (str) => util.format('\x1b[31m%s\x1b[0m', str);
const green = (str) => util.format('\x1b[32m%s\x1b[0m', str);
const magenta = (str) => util.format('\x1b[35m%s\x1b[0m', str);
// @example package.json
// { "scripts": { "workspaces:parallel": "node ./workspaces-parallel.js" } }
async function main() {
const args = process.argv.slice(2);
const [taskName, ...taskOptionsArr] = args;
if (!taskName) {
console.log(' Usage: yarn run workspaces:parallel <taskName> [taskOptions]');
console.log(' example: yarn run workspaces:parallel test');
console.log(' example: yarn run workspaces:parallel tsc --noEmit');
process.exit(1);
}
const taskOptions = taskOptionsArr.join(' ');
// `yarn workspaces info` -> { "@example/name": { "location": "packages/name", ... }, ...}
const workspacesInfoRaw = execSync('yarn workspaces info').toString();
const workspacesInfo = JSON.parse(workspacesInfoRaw);
const workspaces = Object.keys(workspacesInfo);
console.log('Running', green(taskName), `against ${workspaces.length} workspaces...\n`);
// Spawn `exec` calls for every workspace at the same time.
const workspaceExecs = workspaces.map((workspace) => {
const taskCommand = [taskName, taskOptions].filter(Boolean).join(' ');
const command = `yarn workspace ${workspace} run ${taskCommand}`;
console.log(command);
return asyncExec(command)
.then((result) => {
console.log(result.stdout);
if (result.stderr) {
console.error(result.stderr);
}
return result;
})
.catch((err) => {
// Only log error and don't rethrow. Inspecting exit codes at the end allow us to report failures
console.log(red('[START ERROR]'), magenta(workspace));
console.log(err.stdout);
console.log(red('[END ERROR]'), magenta(workspace));
// Returned errors have a `.code` property
return err;
});
});
const results = await Promise.all(workspaceExecs);
for (let result of results) {
// Successful results don't have a `.code` property
const resultExitCode = result.code ?? 0;
if (resultExitCode > 0) {
/**
* Exit early since all results have already been reported.
* A non-zero exit code will be reported as a failure in CI
*/
process.exit(resultExitCode);
}
}
// If we made it here, all results exited with an exit code of 0, we are good!
process.exit(0);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment