Created
January 6, 2022 02:06
-
-
Save craigphicks/c556ab4e2ce505337fa5be6f383d656b to your computer and use it in GitHub Desktop.
Workaround for lerna run bug (not calling in topological order).
This file contains hidden or 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
Lerna issue [ Lerna does not run scripts according to topological order #2731 ](https://github.com/lerna/lerna/issues/2731) | |
reports the bug, but issue was closed although not resolved. | |
Strangely, I didn't **notice** any problem until adding new scopes in the project. | |
But now I have the same bug. | |
I threw up something quick to get builds working again, and posted it here. | |
It's a near replacement but uses regexp instead of globbing. | |
This file contains hidden or 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
import cp from 'child_process'; | |
type LernaListEntry = { | |
name: string; // [@scope/]packagename | |
version: string; | |
private: boolean; | |
location: string; // absolute path | |
}; | |
function getLernaListEntriesTopo(): LernaListEntry[] { | |
const lintext = cp.execSync('lerna la --ndjson --toposort').toString(); | |
const linarr = lintext.split(/\r\n?|\n/).filter((s) => s); | |
return linarr.map((item) => JSON.parse(item) as LernaListEntry); | |
} | |
async function executeOne(name: string, otherArgs: string[]): Promise<number> { | |
return await new Promise((resolve, reject) => { | |
const prog = 'lerna'; | |
const args = ['run', '--scope', name, ...otherArgs]; | |
console.log(`DEBUG: `, prog, ...args); | |
const child = cp.spawn(prog, args, {stdio: 'inherit'}); | |
child.on('error', (err) => { | |
reject(err); | |
}); | |
child.on('close', (code, signal) => { | |
if (signal) | |
reject(new Error(`${name} ${args} terminated with signal ${signal}`)); | |
resolve(code ?? 0); | |
}); | |
}); | |
} | |
function help(): void { | |
const text = ` | |
lenra-serial run [--scope XXX [--scope XXX ... ][ --ignore XXX] ...] <command> | |
where XXX are escaped js strings as constructor args for RegExp(XXX) | |
`; | |
console.error(text); | |
process.exit(1); | |
} | |
const args = process.argv.slice(2); | |
if (args.length < 2) help(); | |
if (args[0] !== 'run') { | |
console.error('first arg must be "run"'); | |
help(); | |
} | |
const includeRe: RegExp[] = []; | |
const excludeRe: RegExp[] = []; | |
const otherArgs: string[] = []; | |
for (let i = 1; i < args.length; i++) { | |
if (args[i] === '--scope') { | |
if (i === args.length) help(); | |
includeRe.push(RegExp(args[++i])); | |
} else if (args[i] === '--ignore') { | |
if (i === args.length) help(); | |
excludeRe.push(RegExp(args[++i])); | |
} else { | |
otherArgs.push(args[i]); | |
} | |
} | |
const lentarr = getLernaListEntriesTopo(); | |
(async (): Promise<number> => { | |
for (let lent of lentarr) { | |
if ( | |
includeRe.some((re) => re.test(lent.name)) && | |
!excludeRe.some((re) => re.test(lent.name)) | |
) { | |
const rcode = await executeOne(lent.name, otherArgs); | |
if (rcode) return rcode; | |
} | |
} | |
return 0; | |
})() | |
.then((rcode) => (process.exitCode = rcode)) | |
.catch((e) => { | |
console.error(e.message); | |
process.exitCode = 127; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment