diff --git a/bin/cli.js b/bin/cli.js index e7ae273..cf0278c 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -1,40 +1,36 @@ #!/usr/bin/env node -if (process.argv.includes("--watchspawn")) { - require("../lib/spawnHook").hook(); -} +const meow = require("meow"); +const pairs = require("lodash.pairs"); +const set = require("lodash.set"); +const runner = require("./runner"); +const cli = meow( + ` + Usage + $ multi-semantic-release -// Execa hook. -if (process.argv.includes("--execasync")) { - require("../lib/execaHook").hook(); -} + Options + --sync, Forces all execa calls to be synchronous + --debug, Enables all additional logging + --debug.spawn Turns on logging for process.spawn -// Imports. -const getWorkspacesYarn = require("../lib/getWorkspacesYarn"); -const multiSemanticRelease = require("../lib/multiSemanticRelease"); - -// Get directory. -const cwd = process.cwd(); + Examples + $ multi-semantic-release --sync --debug + $ multi-semantic-release --debug.spawn +`, + { + flags: { + sync: { + type: "boolean", + alias: "execasync" // Legacy + }, + debug: { + type: "boolean" + } + } + } +); -// Catch errors. -try { - // Get list of package.json paths according to Yarn workspaces. - const paths = getWorkspacesYarn(cwd); +const processFlags = flags => pairs(flags).reduce((m, [k, v]) => set(m, k, v), {}); - // Do multirelease (log out any errors). - multiSemanticRelease(paths, {}, { cwd }).then( - () => { - // Success. - process.exit(0); - }, - error => { - // Log out errors. - console.error(`[multi-semantic-release]:`, error); - process.exit(1); - } - ); -} catch (error) { - // Log out errors. - console.error(`[multi-semantic-release]:`, error); - process.exit(1); -} +runner(processFlags(cli.flags)); diff --git a/bin/runner.js b/bin/runner.js new file mode 100644 index 0000000..7752e81 --- /dev/null +++ b/bin/runner.js @@ -0,0 +1,42 @@ +const get = require("lodash.get"); + +module.exports = flags => { + if (flags.watchspawn || get(flags, "debug.spawn")) { + require("../lib/spawnHook").hook(); + } + + // Execa hook. + if (flags.sync) { + require("../lib/execaHook").hook(); + } + + // Imports. + const getWorkspacesYarn = require("../lib/getWorkspacesYarn"); + const multiSemanticRelease = require("../lib/multiSemanticRelease"); + + // Get directory. + const cwd = process.cwd(); + + // Catch errors. + try { + // Get list of package.json paths according to Yarn workspaces. + const paths = getWorkspacesYarn(cwd); + + // Do multirelease (log out any errors). + multiSemanticRelease(paths, {}, { cwd }).then( + () => { + // Success. + process.exit(0); + }, + error => { + // Log out errors. + console.error(`[multi-semantic-release]:`, error); + process.exit(1); + } + ); + } catch (error) { + // Log out errors. + console.error(`[multi-semantic-release]:`, error); + process.exit(1); + } +}; diff --git a/lib/createInlinePluginCreator.js b/lib/createInlinePluginCreator.js index 422e291..29cf9b6 100644 --- a/lib/createInlinePluginCreator.js +++ b/lib/createInlinePluginCreator.js @@ -22,6 +22,36 @@ function createInlinePluginCreator(packages, multiContext) { // List of packages which are still todo (don't yet have a result). const todo = () => packages.filter(p => p.result === undefined); + /** + * Update pkg deps. + * @param {Package} pkg The package this function is being called on. + * @param {string} path Path to package.json file + * @returns {undefined} + * @internal + */ + const updateManifestDeps = (pkg, path) => { + // Get and parse manifest file contents. + const manifest = getManifest(path); + + // Loop through localDeps to update dependencies/devDependencies/peerDependencies in manifest. + pkg._localDeps.forEach(d => { + // Get version of dependency. + const release = d._nextRelease || d._lastRelease; + + // Cannot establish version. + if (!release || !release.version) + throw Error(`Cannot release because dependency ${d.name} has not been released`); + + // Update version of dependency in manifest. + if (manifest.dependencies.hasOwnProperty(d.name)) manifest.dependencies[d.name] = release.version; + if (manifest.devDependencies.hasOwnProperty(d.name)) manifest.devDependencies[d.name] = release.version; + if (manifest.peerDependencies.hasOwnProperty(d.name)) manifest.peerDependencies[d.name] = release.version; + }); + + // Write package.json back out. + writeFileSync(path, JSON.stringify(manifest)); + }; + /** * Create an inline plugin for an individual package in a multirelease. * This is called once per package and returns the inline plugin used for semanticRelease() @@ -151,38 +181,12 @@ function createInlinePluginCreator(packages, multiContext) { * @internal */ async function prepare(pluginOptions, context) { - // Get and parse manifest file contents. - const manifest = getManifest(path); - - // Loop through localDeps to update dependencies/devDependencies/peerDependencies in manifest. - pkg._localDeps.forEach(d => { - // Get version of dependency. - const release = d._nextRelease || d._lastRelease; - - // Cannot establish version. - if (!release || !release.version) - throw Error(`Cannot release because dependency ${d.name} has not been released`); - - // Update version of dependency in manifest. - if (manifest.dependencies.hasOwnProperty(d.name)) manifest.dependencies[d.name] = release.version; - if (manifest.devDependencies.hasOwnProperty(d.name)) manifest.devDependencies[d.name] = release.version; - if (manifest.peerDependencies.hasOwnProperty(d.name)) - manifest.peerDependencies[d.name] = release.version; - }); - - // Write package.json back out. - writeFileSync(path, JSON.stringify(manifest)); + // Update pkg deps. + updateManifestDeps(pkg, path); // Call other plugins. await plugins.prepare(context); - // Prevent deps change rollback. - const _manifest = getManifest(path); - Object.assign(_manifest.dependencies, manifest.dependencies); - Object.assign(_manifest.devDependencies, manifest.devDependencies); - Object.assign(_manifest.peerDependencies, manifest.peerDependencies); - writeFileSync(path, JSON.stringify(_manifest)); - // Package is prepared. pkg._prepared = true; @@ -195,6 +199,9 @@ function createInlinePluginCreator(packages, multiContext) { // Need this because: when semanticRelease() does several `git push` simultaneously some will fail due to refs not being locked. // (semantic-release should probably use `execa.sync()` to ensure Git operations are atomic — if they do there should be no issues with doing several releases at once). await wait(() => todo()[0] === pkg); + + // Prevent deps change rollback. + updateManifestDeps(pkg, path); } // These steps just passthrough to plugins. diff --git a/package.json b/package.json index 218a853..546b385 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,10 @@ "execa": "^4.0.0", "get-stream": "^5.1.0", "git-log-parser": "^1.2.0", + "lodash.get": "^4.4.2", + "lodash.pairs": "^3.0.1", + "lodash.set": "^4.3.2", + "meow": "^6.0.1", "require-in-the-middle": "^5.0.3", "semantic-release": "^17.0.4", "semver": "^7.1.3", @@ -67,16 +71,16 @@ "@semantic-release/git": "^9.0.0", "@semantic-release/github": "^7.0.4", "@semantic-release/npm": "^7.0.3", + "codeclimate-test-reporter": "^0.5.1", "commitlint": "^8.3.5", + "coveralls": "^3.0.9", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.0", "eslint-plugin-prettier": "^3.1.2", "file-url": "^3.0.0", "husky": "^4.2.3", "jest": "^25.1.0", - "prettier": "^1.19.1", - "coveralls": "^3.0.9", - "codeclimate-test-reporter": "^0.5.1" + "prettier": "^1.19.1" }, "release": { "branch": "master", diff --git a/yarn.lock b/yarn.lock index bcd06a9..46f610a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -977,6 +977,11 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/minimist@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" + integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= + "@types/node@>= 8": version "13.7.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.0.tgz#b417deda18cf8400f278733499ad5547ed1abec4" @@ -1631,6 +1636,15 @@ camelcase-keys@^4.0.0: map-obj "^2.0.0" quick-lru "^1.0.0" +camelcase-keys@^6.1.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.0.tgz#07302c9a9b78ffda16169e4cee18ed104aa4e8ea" + integrity sha512-bwHzjmUuw92j16zHWIV1YjpDk9S4sq/gB0x4upHcNrdV/U5RU0NCg3bxo6Ymu0+kjhT5L0KTfqCMaMkP2RrXnQ== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -2243,7 +2257,7 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize-keys@^1.0.0: +decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -3274,6 +3288,11 @@ har-validator@~5.1.0: ajv "^6.5.5" har-schema "^2.0.0" +hard-rejection@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -4687,6 +4706,11 @@ lodash._createset@~4.0.0: resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -4717,6 +4741,16 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" @@ -4732,6 +4766,22 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.pairs@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.pairs/-/lodash.pairs-3.0.1.tgz#bbe08d5786eeeaa09a15c91ebf0dcb7d2be326a9" + integrity sha1-u+CNV4bu6qCaFckevw3LfSvjJqk= + dependencies: + lodash.keys "^3.0.0" + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -4892,6 +4942,11 @@ map-obj@^2.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= +map-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" + integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -4960,6 +5015,23 @@ meow@^4.0.0: redent "^2.0.0" trim-newlines "^2.0.0" +meow@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-6.0.1.tgz#f9b3f912c9aa039142cebcf74315129f4cd1ce1c" + integrity sha512-kxGTFgT/b7/oSRSQsJ0qsT5IMU+bgZ1eAdSA3kIV7onkW0QWo/hL5RbGlMfvBjHJKPE1LaPX0kdecYFiqYWjUw== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.1.1" + decamelize-keys "^1.1.0" + hard-rejection "^2.0.0" + minimist-options "^4.0.1" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.0" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.8.1" + yargs-parser "^16.1.0" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -5019,6 +5091,11 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +min-indent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" + integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -5034,6 +5111,14 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" +minimist-options@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.0.2.tgz#29c4021373ded40d546186725e57761e4b1984a7" + integrity sha512-seq4hpWkYSUh1y7NXxzucwAN9yVlBc3Upgdjz8vLCP97jG8kaOmzYrVH/m7tQ1NYD1wdtZbSLfdy4zFmRWuc/w== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -6126,6 +6211,11 @@ quick-lru@^1.0.0: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + qw@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/qw/-/qw-1.0.1.tgz#efbfdc740f9ad054304426acb183412cc8b996d4" @@ -6288,6 +6378,14 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + redeyed@~2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" @@ -7182,6 +7280,13 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" @@ -7435,6 +7540,11 @@ trim-newlines@^2.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= +trim-newlines@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" + integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== + trim-off-newlines@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3"