diff --git a/README.md b/README.md index ff94a1e..dceb825 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ The integration with semantic release is pretty janky — this is a quick summar - Had to duplicate the internal cosmiconfig setup from semantic release to get this working :( 4. I found Git getting itself into weird states because e.g. `git tag` is done asynchronously - To get around this I had to stagger package publishing so they were done one at a time (which slows things down) - - I think calls to `execa()` in semantic release should be replaced with `execa.sync()` to ensure Git's internal state is atomic. + - I think calls to `execa()` in semantic release should be replaced with `execa.sync()` to ensure Git's internal state is atomic. For an experiment, you may add `--execasync` CLI flag that makes all calls synchronous through [ritm-hook](https://github.com/elastic/require-in-the-middle). ### Git tags diff --git a/bin/cli.js b/bin/cli.js index 0b772ab..ec6c08c 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -1,5 +1,10 @@ #!/usr/bin/env node +// Execa hook. +if (require("yargs").argv.execasync) { + require("../lib/execaHook").hook() +} + // Imports. const getWorkspacesYarn = require("../lib/getWorkspacesYarn"); const multiSemanticRelease = require("../lib/multiSemanticRelease"); diff --git a/lib/execaHook.js b/lib/execaHook.js new file mode 100644 index 0000000..5fdefa7 --- /dev/null +++ b/lib/execaHook.js @@ -0,0 +1,54 @@ +// NOTE this workaround forces execa calls to be always sync +// Discussion: https://github.com/semantic-release/semantic-release/issues/193#issuecomment-462063871 + +const execa = require('execa') +const ritm = require('require-in-the-middle') + +let interceptor + +const uncache = () => delete require.cache[require.resolve('execa')] + +const getExecaSyncPromisified = () => Object.assign((...args) => { + const result = new Promise((resolve, reject) => { + try { + resolve(execa.sync(...args)) + } catch (e) { + reject(e) + } + }) + + result.stdout = new String('') + result.stderr = new String('') + result.stdout.pipe = () => {} + result.stderr.pipe = () => {} + + return result +}, execa) + +const hook = () => { + if (interceptor) { + return + } + + const execaHooked = getExecaSyncPromisified() + + interceptor = ritm(['execa'], () => execaHooked) + uncache() + + console.log('"execa" hooked') +} + +const unhook = () => { + if (interceptor) { + interceptor.unhook() + interceptor = null + uncache() + + console.log('"execa" unhooked') + } +} + +module.exports = { + hook, + unhook +} diff --git a/package.json b/package.json index 0394967..cb299df 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,8 @@ "semver": "^5.6.0", "signale": "^1.2.0", "stream-buffers": "^3.0.2", - "tempy": "^0.2.1" + "tempy": "^0.2.1", + "yargs": "^13.2.2" }, "devDependencies": { "@commitlint/config-conventional": "^7.3.1",