feat(package-rules): add DepNamePrefix matcher (#28542)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
This commit is contained in:
Yusuke Iinuma 2024-04-21 00:05:27 +09:00 committed by GitHub
parent 99c99f01e0
commit 7b66b9f3c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 226 additions and 0 deletions

View file

@ -2510,6 +2510,8 @@ Invalid if used outside of a `packageRule`.
### excludeDepPatterns ### excludeDepPatterns
### excludeDepPrefixes
### excludePackageNames ### excludePackageNames
**Important**: Do not mix this up with the option `ignoreDeps`. **Important**: Do not mix this up with the option `ignoreDeps`.
@ -2830,6 +2832,8 @@ It is recommended that you avoid using "negative" globs, like `**/!(package.json
### matchDepPatterns ### matchDepPatterns
### matchDepPrefixes
### matchNewValue ### matchNewValue
This option is matched against the `newValue` field of a dependency. This option is matched against the `newValue` field of a dependency.

View file

@ -1375,6 +1375,34 @@ const options: RenovateOptions[] = [
cli: false, cli: false,
env: false, env: false,
}, },
{
name: 'matchDepPrefixes',
description:
'Dep names prefixes to match. Valid only within a `packageRules` object.',
type: 'array',
subType: 'string',
allowString: true,
stage: 'package',
parents: ['packageRules'],
mergeable: true,
cli: false,
env: false,
advancedUse: true,
},
{
name: 'excludeDepPrefixes',
description:
'Dep names prefixes to exclude. Valid only within a `packageRules` object.',
type: 'array',
subType: 'string',
allowString: true,
stage: 'package',
parents: ['packageRules'],
mergeable: true,
cli: false,
env: false,
advancedUse: true,
},
{ {
name: 'matchPackagePatterns', name: 'matchPackagePatterns',
description: description:

View file

@ -353,6 +353,7 @@ export interface PackageRule
description?: string | string[]; description?: string | string[];
excludeDepNames?: string[]; excludeDepNames?: string[];
excludeDepPatterns?: string[]; excludeDepPatterns?: string[];
excludeDepPrefixes?: string[];
excludePackageNames?: string[]; excludePackageNames?: string[];
excludePackagePatterns?: string[]; excludePackagePatterns?: string[];
excludePackagePrefixes?: string[]; excludePackagePrefixes?: string[];
@ -367,6 +368,7 @@ export interface PackageRule
matchDatasources?: string[]; matchDatasources?: string[];
matchDepNames?: string[]; matchDepNames?: string[];
matchDepPatterns?: string[]; matchDepPatterns?: string[];
matchDepPrefixes?: string[];
matchDepTypes?: string[]; matchDepTypes?: string[];
matchFileNames?: string[]; matchFileNames?: string[];
matchManagers?: string[]; matchManagers?: string[];

View file

@ -396,11 +396,13 @@ export async function validateConfig(
'matchDepTypes', 'matchDepTypes',
'matchDepNames', 'matchDepNames',
'matchDepPatterns', 'matchDepPatterns',
'matchDepPrefixes',
'matchPackageNames', 'matchPackageNames',
'matchPackagePatterns', 'matchPackagePatterns',
'matchPackagePrefixes', 'matchPackagePrefixes',
'excludeDepNames', 'excludeDepNames',
'excludeDepPatterns', 'excludeDepPatterns',
'excludeDepPrefixes',
'excludePackageNames', 'excludePackageNames',
'excludePackagePatterns', 'excludePackagePatterns',
'excludePackagePrefixes', 'excludePackagePrefixes',

View file

@ -0,0 +1,105 @@
import { DepPrefixesMatcher } from './dep-prefixes';
describe('util/package-rules/dep-prefixes', () => {
const depPrefixesMatcher = new DepPrefixesMatcher();
describe('match', () => {
it('should return null if matchDepPrefixes is not defined', () => {
const result = depPrefixesMatcher.matches(
{
depName: 'abc1',
},
{
matchDepPrefixes: undefined,
},
);
expect(result).toBeNull();
});
it('should return false if depName is not defined', () => {
const result = depPrefixesMatcher.matches(
{
depName: undefined,
},
{
matchDepPrefixes: ['@opentelemetry'],
},
);
expect(result).toBeFalse();
});
it('should return true if depName matched', () => {
const result = depPrefixesMatcher.matches(
{
depName: 'abc1',
},
{
matchDepPrefixes: ['abc'],
},
);
expect(result).toBeTrue();
});
it('should return false if depName does not match', () => {
const result = depPrefixesMatcher.matches(
{
depName: 'abc1',
},
{
matchDepPrefixes: ['def'],
},
);
expect(result).toBeFalse();
});
});
describe('exclude', () => {
it('should return null if excludeDepPrefixes is not defined', () => {
const result = depPrefixesMatcher.excludes(
{
depName: 'abc1',
},
{
excludeDepPrefixes: undefined,
},
);
expect(result).toBeNull();
});
it('should return false if depName is not defined', () => {
const result = depPrefixesMatcher.excludes(
{
depName: undefined,
},
{
excludeDepPrefixes: ['@opentelemetry'],
},
);
expect(result).toBeFalse();
});
it('should return true if depName matched', () => {
const result = depPrefixesMatcher.excludes(
{
depName: 'abc1',
},
{
excludeDepPrefixes: ['abc'],
},
);
expect(result).toBeTrue();
});
it('should return false if depName does not match', () => {
const result = depPrefixesMatcher.excludes(
{
depName: 'abc1',
},
{
excludeDepPrefixes: ['def'],
},
);
expect(result).toBeFalse();
});
});
});

View file

@ -0,0 +1,35 @@
import is from '@sindresorhus/is';
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
import { Matcher } from './base';
export class DepPrefixesMatcher extends Matcher {
override matches(
{ depName }: PackageRuleInputConfig,
{ matchDepPrefixes }: PackageRule,
): boolean | null {
if (is.undefined(matchDepPrefixes)) {
return null;
}
if (is.undefined(depName)) {
return false;
}
return matchDepPrefixes.some((prefix) => depName.startsWith(prefix));
}
override excludes(
{ depName }: PackageRuleInputConfig,
{ excludeDepPrefixes }: PackageRule,
): boolean | null {
if (is.undefined(excludeDepPrefixes)) {
return null;
}
if (is.undefined(depName)) {
return false;
}
return excludeDepPrefixes.some((prefix) => depName.startsWith(prefix));
}
}

View file

@ -1230,4 +1230,52 @@ describe('util/package-rules/index', () => {
expect(res1.x).toBeUndefined(); expect(res1.x).toBeUndefined();
expect(res2.x).toBe(1); expect(res2.x).toBe(1);
}); });
it('matches matchDepPrefixes(depName)', () => {
const config: TestConfig = {
packageRules: [
{
matchDepPrefixes: ['abc'],
x: 1,
},
],
};
const res1 = applyPackageRules({
...config,
depName: 'abc1',
});
const res2 = applyPackageRules({
...config,
depName: 'def1',
});
applyPackageRules(config); // coverage
expect(res1.x).toBe(1);
expect(res2.x).toBeUndefined();
});
it('matches excludeDepPrefixes(depName)', () => {
const config: TestConfig = {
packageRules: [
{
excludeDepPrefixes: ['abc'],
x: 1,
},
],
};
const res1 = applyPackageRules({
...config,
depName: 'abc1',
});
const res2 = applyPackageRules({
...config,
depName: 'def1',
});
applyPackageRules(config); // coverage
expect(res1.x).toBeUndefined();
expect(res2.x).toBe(1);
});
}); });

View file

@ -6,6 +6,7 @@ import { CurrentVersionMatcher } from './current-version';
import { DatasourcesMatcher } from './datasources'; import { DatasourcesMatcher } from './datasources';
import { DepNameMatcher } from './dep-names'; import { DepNameMatcher } from './dep-names';
import { DepPatternsMatcher } from './dep-patterns'; import { DepPatternsMatcher } from './dep-patterns';
import { DepPrefixesMatcher } from './dep-prefixes';
import { DepTypesMatcher } from './dep-types'; import { DepTypesMatcher } from './dep-types';
import { FileNamesMatcher } from './files'; import { FileNamesMatcher } from './files';
import { ManagersMatcher } from './managers'; import { ManagersMatcher } from './managers';
@ -32,6 +33,7 @@ matchers.push([new MergeConfidenceMatcher()]);
matchers.push([ matchers.push([
new DepNameMatcher(), new DepNameMatcher(),
new DepPatternsMatcher(), new DepPatternsMatcher(),
new DepPrefixesMatcher(),
new PackageNameMatcher(), new PackageNameMatcher(),
new PackagePatternsMatcher(), new PackagePatternsMatcher(),
new PackagePrefixesMatcher(), new PackagePrefixesMatcher(),