Skip to content

Commit

Permalink
feat(npm): support constraintsFiltering=strict (#22447)
Browse files Browse the repository at this point in the history
Closes #4826

Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
  • Loading branch information
rarkins and HonkingGoose committed Jul 4, 2023
1 parent c2d3ca8 commit ad0479a
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 3 deletions.
30 changes: 28 additions & 2 deletions docs/usage/configuration-options.md
Expand Up @@ -602,11 +602,37 @@ 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.
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 are some examples:

| Your repo engines.node | Dependency release engines.node | Result |
| ------------------------ | ------------------------------- | -------- |
| `18` | `16 \|\| 18` | allowed |
| `^18.10.0` | `>=18` | allowed |
| `^16.10.0 \|\| >=18.0.0` | `>= 16.0.0` | allowed |
| `>=16` | `16 \|\| 18` | filtered |
| `16` | `^16.10.0` | filtered |

When using with `npm`, we recommend you:

- Use `constraintsFiltering` on `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
Expand Up @@ -399,6 +399,7 @@ export async function getPkgReleases(
res.releases = uniq(res.releases, (x, y) => x.version === y.version);

if (config?.constraintsFiltering === 'strict') {
const filteredReleases: string[] = [];
// Filter releases for compatibility
for (const [constraintName, constraintValue] of Object.entries(
config.constraints ?? {}
Expand All @@ -411,17 +412,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
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
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
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 ad0479a

Please sign in to comment.