Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(npm): support constraintsFiltering=strict #22447

Merged
merged 7 commits into from Jun 18, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 28 additions & 2 deletions docs/usage/configuration-options.md
Expand Up @@ -594,11 +594,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.
rarkins marked this conversation as resolved.
Show resolved Hide resolved

<!-- 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)
rarkins marked this conversation as resolved.
Show resolved Hide resolved

## 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 @@ -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
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