Created
February 23, 2024 22:17
-
-
Save romellem/40a4b5761a3f2d715345ecd67ffe8059 to your computer and use it in GitHub Desktop.
Yarn workspaces parallel run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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