Skip to content

Commit

Permalink
feat(config)!: add new option constraintsFiltering (#19992)
Browse files Browse the repository at this point in the history
  • Loading branch information
RahulGautamSingh authored and rarkins committed Jan 28, 2023
1 parent 395e490 commit 1f5ce38
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 20 deletions.
13 changes: 13 additions & 0 deletions docs/usage/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,19 @@ If you need to _override_ constraints that Renovate detects from the repository,
!!! note
Make sure not to mix this up with the term `compatibility`, which Renovate uses in the context of version releases, e.g. if a Docker image is `node:12.16.0-alpine` then the `-alpine` suffix represents `compatibility`.

## constraintsFiltering

This option controls whether Renovate filters new releases based on configured or detected `constraints`.
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.

Note: There must be a `constraints` object in your Renovate config for this to work.
This feature is limited to `pypi` datasource only.

## defaultRegistryUrls

Override a datasource's default registries with this config option.
Expand Down
8 changes: 8 additions & 0 deletions lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ const options: RenovateOptions[] = [
cli: false,
env: false,
},
{
name: 'constraintsFiltering',
description: 'Perform release filtering based on language constraints.',
type: 'string',
allowedValues: ['none', 'strict'],
cli: false,
default: 'none',
},
{
name: 'repositoryCache',
description:
Expand Down
3 changes: 3 additions & 0 deletions lib/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export interface RegExManager extends RegexManagerTemplates {
}

export type UseBaseBranchConfigType = 'merge' | 'none';
export type ConstraintsFilter = 'strict' | 'none';

// TODO: Proper typings
export interface RenovateConfig
Expand Down Expand Up @@ -250,6 +251,8 @@ export interface RenovateConfig

constraints?: Record<string, string>;
skipInstalls?: boolean;

constraintsFiltering?: ConstraintsFilter;
}

export interface AllConfig
Expand Down
91 changes: 91 additions & 0 deletions lib/modules/datasource/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,97 @@ describe('modules/datasource/index', () => {
expect(res).toBeNull();
});
});

describe('relaseConstraintFiltering', () => {
it('keeps all releases by default', async () => {
const registries = {
'https://foo.bar': {
releases: [
{
version: '0.0.1',
constraints: {
python: ['2.7'],
},
},
{
version: '0.0.2',
},
],
},
} satisfies RegistriesMock;
datasources.set(datasource, new DummyDatasource(registries));
const res = await getPkgReleases({
datasource,
depName,
defaultRegistryUrls: ['https://foo.bar'],
});
expect(res).toMatchObject({
releases: [{ version: '0.0.1' }, { version: '0.0.2' }],
});
});

it('keeps all releases if constraints is set but no value defined for constraintsFiltering', async () => {
const registries = {
'https://foo.bar': {
releases: [
{
version: '0.0.1',
constraints: {
python: ['2.7'],
},
},
{
version: '0.0.2',
},
],
},
} satisfies RegistriesMock;
datasources.set(datasource, new DummyDatasource(registries));
const res = await getPkgReleases({
datasource,
depName,
defaultRegistryUrls: ['https://foo.bar'],
constraints: {
python: '2.7.0',
},
});
expect(res).toMatchObject({
releases: [{ version: '0.0.1' }, { version: '0.0.2' }],
});
});

it('filters releases if value is strict', async () => {
const registries = {
'https://foo.bar': {
releases: [
{
version: '0.0.1',
constraints: {
python: ['2.7'],
},
},
{
version: '0.0.2',
constraints: {
python: ['1.0'],
},
},
],
},
} satisfies RegistriesMock;
datasources.set(datasource, new DummyDatasource(registries));
const res = await getPkgReleases({
datasource,
depName,
defaultRegistryUrls: ['https://foo.bar'],
constraints: { python: '2.7.0' },
constraintsFiltering: 'strict',
});
expect(res).toMatchObject({
releases: [{ version: '0.0.1' }],
});
});
});
});
});
});
42 changes: 22 additions & 20 deletions lib/modules/datasource/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,26 +395,28 @@ export async function getPkgReleases(
(findRelease) => findRelease.version === filterRelease.version
) === filterIndex
);
// Filter releases for compatibility
for (const [constraintName, constraintValue] of Object.entries(
config.constraints ?? {}
)) {
// Currently we only support if the constraint is a plain version
// TODO: Support range/range compatibility filtering #8476
if (version.isVersion(constraintValue)) {
res.releases = res.releases.filter((release) => {
const constraint = release.constraints?.[constraintName];
if (!is.nonEmptyArray(constraint)) {
// A release with no constraints is OK
return true;
}
return constraint.some(
// If any of the release's constraints match, then it's OK
(releaseConstraint) =>
!releaseConstraint ||
version.matches(constraintValue, releaseConstraint)
);
});
if (config?.constraintsFiltering === 'strict') {
// Filter releases for compatibility
for (const [constraintName, constraintValue] of Object.entries(
config.constraints ?? {}
)) {
// Currently we only support if the constraint is a plain version
// TODO: Support range/range compatibility filtering #8476
if (version.isVersion(constraintValue)) {
res.releases = res.releases.filter((release) => {
const constraint = release.constraints?.[constraintName];
if (!is.nonEmptyArray(constraint)) {
// A release with no constraints is OK
return true;
}
return constraint.some(
// If any of the release's constraints match, then it's OK
(releaseConstraint) =>
!releaseConstraint ||
version.matches(constraintValue, releaseConstraint)
);
});
}
}
}
// Strip constraints from releases result
Expand Down
2 changes: 2 additions & 0 deletions lib/modules/datasource/pypi/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ describe('modules/datasource/pypi/index', () => {
datasource,
constraints: { python: '2.7' },
depName: 'doit',
constraintsFiltering: 'strict',
})
).toMatchSnapshot();
});
Expand Down Expand Up @@ -518,6 +519,7 @@ describe('modules/datasource/pypi/index', () => {
constraints: { python: '2.7' },
...config,
depName: 'dj-database-url',
constraintsFiltering: 'strict',
})
).toMatchSnapshot();
});
Expand Down
2 changes: 2 additions & 0 deletions lib/modules/datasource/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ConstraintsFilter } from '../../config/types';
import type { ModuleApi } from '../../types';

export interface GetDigestInputConfig {
Expand Down Expand Up @@ -37,6 +38,7 @@ export interface GetPkgReleasesConfig {
constraints?: Record<string, string>;
replacementName?: string;
replacementVersion?: string;
constraintsFiltering?: ConstraintsFilter;
}

export interface Release {
Expand Down

0 comments on commit 1f5ce38

Please sign in to comment.