In-Range Dependency Updates with Renovate
Renovate is a tool to keep dependencies up-to-date in your software development projects. By default it is very eager to update dependencies regardless of defined ranges. This way you can avoid that updates keep pilling up.
I have a few internal packages that are actively hardened in different, parallel software releases. To better align for different teams working on those, different minor versions are being tracked per software release. For example productA
relies @internal/package@1.3.x
for an ongoing release, and productB
tracks @internal/package@1.4.x
. And to increase complexity newer features already land in the @internal/package@1.5.x
versions.
I want the updates to be automatically distributed to keep manual efforts and overhead low. Enter renovate
: Whenever a new version of @internal/package
is built, a pipeline running renovate
is triggered to update all consumers.
In short I'm aiming to get these changes from renovate
:
# package.json
- "@internal/packageA": "~1.3.3",
+ "@internal/packageA": "~1.3.4",
# package-lock.json
# "node_modules/@internal/packageA": {
- "version": "1.3.3",
+ "version": "1.3.4",
With the base recommended rules from renovate
, it would ignore an update on the 1.3.x
branch and directly update to the latest 1.5.x
version. So I dove deeper into the options how to solve this with renovate
.
rangeStrategy: 'in-range-only'
is not sufficient
The config parameter rangeStrategy
does offer a way to handle only in-range updates: in-range-only
. This will update the package in the package-lock.json
only.
However this is not ideal in my use-case because some of the consuming packages themselves are a dependency in another project. So this update might get lost as only the data from the package.json
is used to resolve the appropriate versions.
Solution: Individual rules with matchCurrentValue
So I needed to go a level deeper and trigger individual update rules based on the currently tracked version. This can be done with matchCurrentValue
which contains the actual string from the package.json
.
Here a full example to have patch updates for tilde-ranges, and minor and patch updates for caret-ranges:
// `config.js` for a self-hosted renovate setup to only update the `@internal/package`
// triggered from pipelines whenever a new version of `@internal/package` has been published
module.exports = {
// ... standalone config
repositories: [
{
// ... repository config
enabledManagers: ['npm'],
major: { enabled: false },
minor: { enabled: false },
patch: { enabled: false },
rangeStrategy: 'bump', // ensures that entries in the `package.json` is being updated
separateMinorPatch: true, // otherwise renovate would not track updates to patch versions,
// if also a new minor exists
packageRules: [
{
// disable all dependencies
enabled: false,
matchDepTypes: ['devDependencies', 'dependencies', 'peerDependencies'],
matchPackagePatterns: ['*'],
matchUpdateTypes: ['minor', 'patch', 'bump']
},
{
// Allow minor and patch updates for caret-ranges, e.g. `^1.3.0`
enabled: true,
matchDepTypes: ['devDependencies', 'dependencies'],
matchPackageNames: ['@internal/package'],
matchUpdateTypes: ['minor', 'patch'],
// RegExp to cover all version strings starting with a caret
matchCurrentValue: "/^\\^/"
},
{
// Allow patch updates for tilde-ranges, e.g. `~1.2.0`
enabled: true,
matchDepTypes: ['devDependencies', 'dependencies'],
matchPackageNames: ['@internal/package'],
matchUpdateTypes: ['patch'],
// RegExp to cover all version strings starting with a tilde
matchCurrentValue: "/^~/"
}
]
}
]
}
With automerge
enabled and renovate
triggered once a new version of @internal/package
is published, this removes pretty much any manual involvement. So we can rely that after a few minutes the changes will automatically land in the consuming project. 🚀