hacky semantic-release for monorepos
This fork of dhoub/multi-semantic-release replaces setImmediate
loops
and execa.sync
hooks with event-driven flow and finally makes possible to run the most release operations in parallel.
🎉 🎉 🎉
We're still using this lib as a part of our release infra, but we're gradually migrating to bulk-release. This means that we do not have many resources to develop this implementation actively, but we will continue to do it on a leftover basis.
yarn add @qiwi/multi-semantic-release --dev
multi-semantic-release
multi-semantic-release can be configured a number of ways:
- A
.multi-releaserc
file, written in YAML or JSON, with optional extensions:.yaml
/.yml
/.json
/.js
- A
multi-release.config.js
file that exports an object - A
multi-release
key in the workspace root package.json
Alternatively some options may be set via CLI flags.
Note: CLI arguments take precedence over options configured in the configuration file.
Option | Type | CLI Flag | Description |
---|---|---|---|
dryRun | boolean |
--dry-run |
Dry run mode. |
logLevel | String |
--log-level |
Sets the internal logger verbosity level: error, warn, info, debug, trace . Defaults to info . |
debug | boolean |
--debug |
Output debugging information. Shortcut for --logLevel=debug . |
silent | boolean |
--silent |
Turns off any log outputs. |
extends | String | Array |
N/A | List of modules or file paths containing a shareable configuration. If multiple shareable configurations are set, they will be imported in the order defined with each configuration option taking precedence over the options defined in the previous. |
sequentialInit | boolean |
--sequential-init |
Avoid hypothetical concurrent initialization collisions. |
sequentialPrepare | boolean |
--sequential-prepare |
Avoid hypothetical concurrent preparation collisions. True by default. |
firstParent | boolean |
--first-parent |
Apply commit filtering to current branch only. |
ignorePrivate | boolean |
--ignore-private |
Exclude private packages. True by default. |
ignorePackages | String | Array |
--ignore-packages |
Packages list to be ignored on bumping process (appended to the ones that already exist at package.json workspaces). If using the CLI flag, supply a comma seperated list of strings. |
tagFormat | String |
--tag-format |
Format to use when creating tag names. Should include "name" and "version" vars. Default: "${name}@${version}" which generates "package-name@1.0.0" |
deps | Object |
N/A | Dependency handling, see below for possible values. |
Option | Type | CLI Flag | Description |
---|---|---|---|
bump | override | satisfy | inherit |
--deps.bump |
Define deps version updating rule. Allowed: override, satisfy, inherit. override by default.
|
release | patch | minor | major | inherit |
--deps.release |
Define release type for dependent package if any of its deps changes. Supported values: patch, minor, major, inherit. patch by default
|
prefix | '^' | '~' | '' |
--deps.prefix |
Optional prefix to be attached to the next version if bump is set to override . '' by default. |
pullTagsForPrerelease | boolean |
--deps.pullTagsForPrerelease |
Optional flag to use release tags for evaluating prerelease version bumping. Normally, this option will lead to dumping dependencies to a version past what was just released and tagged by semantic release. Only set this option to true if you previously had a workflow that compensated for the previous bug behavior. 'false' by default. |
- Via multi-release key in the project's package.json file:
{
"multi-release": {
"ignorePackages": [
"!packages/b/**",
"!packages/c/**"
],
"deps": {
"bump": "inherit"
}
}
}
- Via
.multi-releaserc
file:
{
"ignorePackages": [
"!packages/b/**",
"!packages/c/**"
],
"deps": {
"bump": "inherit"
}
}
- Via CLI:
$ multi-semantic-release --ignore-packages=packages/a/**,packages/b/** --deps.bump=inherit
MSR requires semrel config to be added in any supported format 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.
// Load the package-specific options.
const { options: pkgOptions } = await getConfig(dir);
// The 'final options' are the global options merged with package-specific options.
// We merge this ourselves because package-specific options can override global options.
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:
{
"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:
$ 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
.
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.