Skip to content

Commit

Permalink
feat: ignore packages to be released with workspaces and CLI (qiwi#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
davikawasaki committed Feb 4, 2021
1 parent 7f85e89 commit b98e181
Show file tree
Hide file tree
Showing 26 changed files with 11,077 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .gitignore
@@ -1,3 +1,3 @@
/node_modules/
**/node_modules/**
/coverage/
*.log
54 changes: 54 additions & 0 deletions README.md
Expand Up @@ -22,6 +22,25 @@ yarn add @qiwi/multi-semantic-release --dev
multi-semantic-release
```

CLI flag options:

```sh
Options
--dry-run Dry run mode.
--debug Output debugging information.
--sequential-init Avoid hypothetical concurrent initialization collisions.
--first-parent Apply commit filtering to current branch only.
--deps.bump Define deps version updating rule. Allowed: override, satisfy, inherit.
--deps.release Define release type for dependent package if any of its deps changes. Supported values: patch, minor, major, inherit.
--ignore-packages Packages list to be ignored on bumping process (append to the ones that already exist at package.json workspaces)
--help Help info.

Examples
$ multi-semantic-release --debug
$ multi-semantic-release --deps.bump=satisfy --deps.release=patch
$ multi-semantic-release --ignore-packages=packages/a/**,packages/b/**
```
## Configuration
**MSR** requires **semrel** config to be added [in any supported format](https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#configuration) for each package or/and declared in repo root (`globalConfig` is extremely useful if all the modules have the same strategy of release).
NOTE config resolver joins `globalConfig` and `packageConfig` during execution.
Expand All @@ -34,6 +53,41 @@ const { options: pkgOptions } = await getConfig(dir);
const finalOptions = Object.assign({}, globalOptions, pkgOptions);
```
Make sure to have a `workspaces` attribute inside your `package.json` project file. In there, you can set a list of packages that you might want to process in the msr process, as well as ignore others. For example, let's say your project has 4 packages (i.e. a, b, c and d) and you want to process only a and d (ignore b and c). You can set the following structure in your `package.json` file:
```json
{
"name": "msr-test-yarn",
"author": "Dave Houlbrooke <dave@shax.com",
"version": "0.0.0-semantically-released",
"private": true,
"license": "0BSD",
"engines": {
"node": ">=8.3"
},
"workspaces": [
"packages/*",
"!packages/b/**",
"!packages/c/**"
],
"release": {
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator"
],
"noCi": true
}
}
```
You can also ignore it with the CLI:
```bash
$ multi-semantic-release --ignore-packages=packages/b/**,packages/c/**
```
You can also combine the CLI ignore options with the `!` operator at each package inside `workspaces` attribute. Even though you can use the CLI to ignore options, you can't use it to set which packages to be released – i.e. you still need to set the `workspaces` attribute inside the `package.json`.
## Verified examples
We use this tool to release our JS platform code inhouse (GitHub Enterprise + JB TeamCity) and for our OSS (GitHub + Travis CI). Guaranteed working configurations available in projects.
* [qiwi/substrate](https://github.com/qiwi/substrate)
Expand Down
16 changes: 13 additions & 3 deletions bin/cli.js
Expand Up @@ -14,12 +14,14 @@ const cli = meow(
--sequential-init Avoid hypothetical concurrent initialization collisions.
--first-parent Apply commit filtering to current branch only.
--deps.bump Define deps version updating rule. Allowed: override, satisfy, inherit.
--deps.release Define release type for dependent package if any of its deps changes. Supported values: patch, minor, major, inherit.
--deps.release Define release type for dependent package if any of its deps changes. Supported values: patch, minor, major, inherit.
--ignore-packages Packages' list to be ignored on bumping process
--help Help info.
Examples
$ multi-semantic-release --debug
$ multi-semantic-release --deps.bump=satisfy --deps.release=patch
$ multi-semantic-release --deps.bump=satisfy --deps.release=patch
$ multi-semantic-release --ignore-packages=packages/a/**,packages/b/**
`,
{
flags: {
Expand All @@ -40,13 +42,21 @@ const cli = meow(
type: "string",
default: "patch",
},
ignorePackages: {
type: "string",
},
dryRun: {
type: "boolean",
},
},
}
);

const processFlags = (flags) => toPairs(flags).reduce((m, [k, v]) => set(m, k, v), {});
const processFlags = (flags) => {
return toPairs(flags).reduce((m, [k, v]) => {
if (k === "ignorePackages" && v) return set(m, k, v.split(","));
return set(m, k, v);
}, {});
};

runner(processFlags(cli.flags));
2 changes: 1 addition & 1 deletion bin/runner.js
Expand Up @@ -19,7 +19,7 @@ module.exports = (flags) => {
console.log(`flags: ${JSON.stringify(flags, null, 2)}`);

// Get list of package.json paths according to Yarn workspaces.
const paths = getWorkspacesYarn(cwd);
const paths = getWorkspacesYarn(cwd, flags.ignorePackages);
console.log("yarn paths", paths);

// Do multirelease (log out any errors).
Expand Down
10 changes: 7 additions & 3 deletions lib/getWorkspacesYarn.js
Expand Up @@ -6,9 +6,10 @@ const { checker } = require("./blork");
* Return array of package.json for Yarn workspaces.
*
* @param {string} cwd The current working directory where a package.json file can be found.
* @param {string[]|null} ignorePackages (Optional) Packages to be ignored passed via cli.
* @returns {string[]} An array of package.json files corresponding to the workspaces setting in package.json
*/
function getWorkspacesYarn(cwd) {
function getWorkspacesYarn(cwd, ignorePackages = null) {
// Load package.json
const manifest = getManifest(`${cwd}/package.json`);

Expand All @@ -22,13 +23,16 @@ function getWorkspacesYarn(cwd) {
throw new TypeError("package.json: workspaces or workspaces.packages: Must be non-empty array of string");
}

// If packages to be ignored come from CLI, we need to combine them with the ones from manifest workspaces
if (Array.isArray(ignorePackages)) packages.push(...ignorePackages.map((p) => `!${p}`));

// Turn workspaces into list of package.json files.
const workspaces = glob(
packages.map((p) => p.replace(/\/?$/, "/package.json")),
{
cwd: cwd,
realpath: true,
ignore: "**/node_modules/**",
absolute: true,
gitignore: true,
}
);

Expand Down
9 changes: 3 additions & 6 deletions lib/glob.js
@@ -1,10 +1,7 @@
const bashGlob = require("bash-glob");
const bashPath = require("bash-path");
const globby = require("globby");

module.exports = (...args) => {
if (!bashPath) {
throw new TypeError("`bash` must be installed"); // TODO move this check to bash-glob
}
const [pattern, ...options] = args;

return bashGlob.sync(...args);
return globby.sync(pattern, ...options);
};
1 change: 0 additions & 1 deletion lib/updateDeps.js
Expand Up @@ -43,7 +43,6 @@ const getVersionFromTag = (pkg, tag) => (pkg.name ? tag.replace(`${pkg.name}@`,
const getNextPreVersion = (pkg, tags) => {
const tagFilters = [pkg._preRelease];
const lastVersion = pkg._lastRelease && pkg._lastRelease.version;
const version = `1.0.0-${pkg._preRelease}.1`;

// Extract tags:
// 1. Set filter to extract only package tags
Expand Down

0 comments on commit b98e181

Please sign in to comment.