Skip to content

Commit

Permalink
feat(npm): support constraintsFiltering=strict
Browse files Browse the repository at this point in the history
Closes #4826
  • Loading branch information
rarkins committed May 27, 2023
1 parent 135858d commit d5afee1
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 3 deletions.
31 changes: 29 additions & 2 deletions docs/usage/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -594,11 +594,38 @@ Renovate supports two options:
- `none`: No release filtering (all releases allowed)
- `strict`: If the release's constraints match the package file constraints, then it's included

We are working on adding more advanced filtering options.
More advanced filtering options may come in future.

Note: There must be a `constraints` object in your Renovate config for this to work.
Note: There must be a `constraints` object in your Renovate config, or constraints detected from package files, for this to work.
This feature is limited to `packagist`, `npm`, and `pypi` datasources.

<!-- prettier-ignore -->
!!! warning
Enabling this feature may result in many package updates being filtered out silently.
See below for a description of how it works.

When `constraintsFiltering=strict`, the following logic applies:

- Are there `constraints` for this repository, either detected from source or from config?
- Does this package's release declare constraints of its own (e.g. `engines` in Node.js)?
- If so, filter out this release unless the repository constraint is a _subset_ of the release constraint

Here's some examples of when a release is allowed:

- The `package.json` declares its `engines.node` as `18` which is a subset of the package release `16 || 18`
- The `package.json` declares its `engines.node` as `^18.10.0` which is a subset of the package release `>=18`
- The `package.json` declares its `engines.node` as `^16.10.0 || >=18.0.0` which is a subset of the package release `>= 16.0.0`

Here's some examples of when a release is filtered out (disallowed):

- The `package.json` declares its `engines.node` as `>=16` while the package release has a narrower `16 || 18`
- The `package.json` declares its `engines.node` as `16` while the package release has a narrower `^16.10.0`

When using with `npm`, it's recommended:

- Use with `dependencies`, not `devDependencies` (usually you do not need to be strict about development dependencies)
- Do not enable `rollbackPrs` at the same time (otherwise your _current_ version may be rolled back if it's incompatible)

## defaultRegistryUrls

Override a datasource's default registries with this config option.
Expand Down
16 changes: 15 additions & 1 deletion lib/modules/datasource/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ export async function getPkgReleases(
) === filterIndex
);
if (config?.constraintsFiltering === 'strict') {
const filteredReleases: string[] = [];
// Filter releases for compatibility
for (const [constraintName, constraintValue] of Object.entries(
config.constraints ?? {}
Expand All @@ -413,17 +414,30 @@ export async function getPkgReleases(
return true;
}

return constraint.some(
const satisfiesConstraints = constraint.some(
// If the constraint value is a subset of any release's constraints, then it's OK
// fallback to release's constraint match if subset is not supported by versioning
(releaseConstraint) =>
!releaseConstraint ||
(version.subset?.(constraintValue, releaseConstraint) ??
version.matches(constraintValue, releaseConstraint))
);
if (!satisfiesConstraints) {
filteredReleases.push(release.version);
}
return satisfiesConstraints;
});
}
}
if (filteredReleases.length) {
logger.debug(
`Filtered ${
filteredReleases.length
} releases for ${packageName} due to constraintsFiltering=strict: ${filteredReleases.join(
', '
)}`
);
}
}
// Strip constraints from releases result
res.releases.forEach((release) => {
Expand Down
3 changes: 3 additions & 0 deletions lib/modules/datasource/npm/get.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ describe('modules/datasource/npm/get', () => {
type: 'git',
url: 'https://github.com/vuejs/vue-next.git',
},
engines: {
node: '>= 8.9.0',
},
},
},
'dist-tags': { latest: '2.0.0' },
Expand Down
4 changes: 4 additions & 0 deletions lib/modules/datasource/npm/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ export async function getDependency(
if (res.versions?.[version].deprecated) {
release.isDeprecated = true;
}
const nodeConstraint = res.versions?.[version].engines?.node;
if (is.nonEmptyString(nodeConstraint)) {
release.constraints = { node: [nodeConstraint] };
}
const source = PackageSource.parse(res.versions?.[version].repository);
if (source.sourceUrl && source.sourceUrl !== dep.sourceUrl) {
release.sourceUrl = source.sourceUrl;
Expand Down
1 change: 1 addition & 0 deletions lib/modules/datasource/npm/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface NpmResponseVersion {
gitHead?: string;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
engines?: Record<string, string>;
}

export interface NpmResponse {
Expand Down

0 comments on commit d5afee1

Please sign in to comment.