mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-11 22:46:27 +00:00
feat(packageRules)!: support glob/regex patterns for matchPackageNames (#28551)
BREAKING CHANGE: matchPackageNames exact matches are now case-insensitive
This commit is contained in:
parent
adcffd2b6b
commit
1e5cf6d07c
8 changed files with 126 additions and 111 deletions
|
@ -2950,13 +2950,12 @@ Use this field to restrict rules to a particular package manager. e.g.
|
|||
}
|
||||
```
|
||||
|
||||
For the full list of available managers, see the [Supported Managers](modules/manager/index.md#supported-managers) documentation.
|
||||
### matchDepPrefixes
|
||||
|
||||
### matchMessage
|
||||
|
||||
For log level remapping, use this field to match against the particular log messages.
|
||||
|
||||
For more details on supported syntax see Renovate's [string pattern matching documentation](./string-pattern-matching.md).
|
||||
<!-- prettier-ignore -->
|
||||
!!! note
|
||||
`matchDepNames` now supports pattern matching and should be used instead.
|
||||
Use of `matchDepPrefixes` is now deprecated and will be migrated in future.
|
||||
|
||||
### matchNewValue
|
||||
|
||||
|
@ -3007,13 +3006,17 @@ For more details on this syntax see Renovate's [string pattern matching document
|
|||
|
||||
### matchPackageNames
|
||||
|
||||
Use this field if you want to have one or more exact name matches in your package rule.
|
||||
See also `excludePackageNames`.
|
||||
Use this field to match against the `packageName` field.
|
||||
This matching can be an exact match, Glob match, or Regular Expression match.
|
||||
|
||||
```json
|
||||
For more details on supported syntax see Renovate's [string pattern matching documentation](./string-pattern-matching.md).
|
||||
Note that Glob matching (including exact name matching) is case-insensitive.
|
||||
|
||||
```json title="exact name match"
|
||||
{
|
||||
"packageRules": [
|
||||
{
|
||||
"matchDatasources": ["npm"],
|
||||
"matchPackageNames": ["angular"],
|
||||
"rangeStrategy": "pin"
|
||||
}
|
||||
|
@ -3021,40 +3024,50 @@ See also `excludePackageNames`.
|
|||
}
|
||||
```
|
||||
|
||||
The above will configure `rangeStrategy` to `pin` only for the package `angular`.
|
||||
The above will configure `rangeStrategy` to `pin` only for the npm package `angular`.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! note
|
||||
`matchPackageNames` will try matching `packageName` first and then fall back to matching `depName`.
|
||||
If the fallback is used, Renovate will log a warning, because the fallback will be removed in a future release.
|
||||
Use `matchDepNames` instead.
|
||||
|
||||
### matchPackagePatterns
|
||||
|
||||
Use this field if you want to have one or more package names patterns in your package rule.
|
||||
See also `excludePackagePatterns`.
|
||||
|
||||
```json
|
||||
```json title="prefix match using Glob"
|
||||
{
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackagePatterns": ["^angular"],
|
||||
"rangeStrategy": "replace"
|
||||
"matchDatasources": ["npm"],
|
||||
"matchPackageNames": ["@angular/*", "!@angular/abc"],
|
||||
"groupName": "Angular"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The above will configure `rangeStrategy` to `replace` for any package starting with `angular`.
|
||||
The above will group together any npm package which starts with `@angular/` except `@angular/abc`.
|
||||
|
||||
```json title="pattern match using RegEx"
|
||||
{
|
||||
"packageRules": [
|
||||
{
|
||||
"matchDatasources": ["npm"],
|
||||
"matchPackageNames": ["/^angular/"],
|
||||
"groupName": "Angular"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The above will group together any npm package which starts with the string `angular`.
|
||||
|
||||
### matchPackagePatterns
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! note
|
||||
`matchPackagePatterns` will try matching `packageName` first and then fall back to matching `depName`.
|
||||
If the fallback is used, Renovate will log a warning, because the fallback will be removed in a future release.
|
||||
Use `matchDepPatterns` instead.
|
||||
`matchPackageNames` now supports pattern matching and should be used instead.
|
||||
Use of `matchPackagePatterns` is now deprecated and will be migrated in future.
|
||||
|
||||
### matchPackagePrefixes
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! note
|
||||
`matchPackageNames` now supports pattern matching and should be used instead.
|
||||
Use of `matchPackagePrefixes` is now deprecated and will be migrated in future.
|
||||
|
||||
Use this field to match a package prefix without needing to write a regex expression.
|
||||
See also `excludePackagePrefixes`.
|
||||
|
||||
|
@ -3071,11 +3084,6 @@ See also `excludePackagePrefixes`.
|
|||
|
||||
Like the earlier `matchPackagePatterns` example, the above will configure `rangeStrategy` to `replace` for any package starting with `angular`.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! note
|
||||
`matchPackagePrefixes` will try matching `packageName` first and then fall back to matching `depName`.
|
||||
If the fallback is used, Renovate will log a warning, because the fallback will be removed in a future release.
|
||||
Use `matchDepPatterns` instead.
|
||||
|
||||
### matchRepositories
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import is from '@sindresorhus/is';
|
||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||
import { matchRegexOrGlobList } from '../string-match';
|
||||
import { Matcher } from './base';
|
||||
|
||||
export class DepNameMatcher extends Matcher {
|
||||
|
@ -13,7 +14,7 @@ export class DepNameMatcher extends Matcher {
|
|||
if (is.undefined(depName)) {
|
||||
return false;
|
||||
}
|
||||
return matchDepNames.includes(depName);
|
||||
return matchRegexOrGlobList(depName, matchDepNames);
|
||||
}
|
||||
|
||||
override excludes(
|
||||
|
@ -26,6 +27,6 @@ export class DepNameMatcher extends Matcher {
|
|||
if (is.undefined(depName)) {
|
||||
return false;
|
||||
}
|
||||
return excludeDepNames.includes(depName);
|
||||
return matchRegexOrGlobList(depName, excludeDepNames);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,30 @@ describe('util/package-rules/dep-patterns', () => {
|
|||
);
|
||||
expect(result).toBeFalse();
|
||||
});
|
||||
|
||||
it('should massage wildcards', () => {
|
||||
const result = depPatternsMatcher.matches(
|
||||
{
|
||||
depName: 'http',
|
||||
},
|
||||
{
|
||||
matchDepPatterns: ['*'],
|
||||
},
|
||||
);
|
||||
expect(result).toBeTrue();
|
||||
});
|
||||
|
||||
it('should convert to regex', () => {
|
||||
const result = depPatternsMatcher.matches(
|
||||
{
|
||||
depName: 'http',
|
||||
},
|
||||
{
|
||||
matchDepPatterns: ['^h'],
|
||||
},
|
||||
);
|
||||
expect(result).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe('exclude', () => {
|
||||
|
@ -29,5 +53,29 @@ describe('util/package-rules/dep-patterns', () => {
|
|||
);
|
||||
expect(result).toBeFalse();
|
||||
});
|
||||
|
||||
it('should massage wildcards', () => {
|
||||
const result = depPatternsMatcher.excludes(
|
||||
{
|
||||
depName: 'http',
|
||||
},
|
||||
{
|
||||
excludeDepPatterns: ['*'],
|
||||
},
|
||||
);
|
||||
expect(result).toBeTrue();
|
||||
});
|
||||
|
||||
it('should convert to regex', () => {
|
||||
const result = depPatternsMatcher.excludes(
|
||||
{
|
||||
depName: 'http',
|
||||
},
|
||||
{
|
||||
excludeDepPatterns: ['^h'],
|
||||
},
|
||||
);
|
||||
expect(result).toBeTrue();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import is from '@sindresorhus/is';
|
||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||
import { logger } from '../../logger';
|
||||
import { regEx } from '../regex';
|
||||
import { matchRegexOrGlobList } from '../string-match';
|
||||
import { Matcher } from './base';
|
||||
import { massagePattern } from './utils';
|
||||
|
||||
export class DepPatternsMatcher extends Matcher {
|
||||
override matches(
|
||||
{ depName, updateType }: PackageRuleInputConfig,
|
||||
{ depName }: PackageRuleInputConfig,
|
||||
{ matchDepPatterns }: PackageRule,
|
||||
): boolean | null {
|
||||
if (is.undefined(matchDepPatterns)) {
|
||||
|
@ -18,22 +16,16 @@ export class DepPatternsMatcher extends Matcher {
|
|||
return false;
|
||||
}
|
||||
|
||||
let isMatch = false;
|
||||
for (const packagePattern of matchDepPatterns) {
|
||||
const packageRegex = regEx(massagePattern(packagePattern));
|
||||
if (packageRegex.test(depName)) {
|
||||
logger.trace(`${depName} matches against ${String(packageRegex)}`);
|
||||
isMatch = true;
|
||||
}
|
||||
}
|
||||
return isMatch;
|
||||
const massagedPatterns = matchDepPatterns.map((pattern) =>
|
||||
pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
|
||||
);
|
||||
return matchRegexOrGlobList(depName, massagedPatterns);
|
||||
}
|
||||
|
||||
override excludes(
|
||||
{ depName, updateType }: PackageRuleInputConfig,
|
||||
{ depName }: PackageRuleInputConfig,
|
||||
{ excludeDepPatterns }: PackageRule,
|
||||
): boolean | null {
|
||||
// ignore lockFileMaintenance for backwards compatibility
|
||||
if (is.undefined(excludeDepPatterns)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -41,14 +33,9 @@ export class DepPatternsMatcher extends Matcher {
|
|||
return false;
|
||||
}
|
||||
|
||||
let isMatch = false;
|
||||
for (const pattern of excludeDepPatterns) {
|
||||
const packageRegex = regEx(massagePattern(pattern));
|
||||
if (packageRegex.test(depName)) {
|
||||
logger.trace(`${depName} matches against ${String(packageRegex)}`);
|
||||
isMatch = true;
|
||||
}
|
||||
}
|
||||
return isMatch;
|
||||
const massagedPatterns = excludeDepPatterns.map((pattern) =>
|
||||
pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
|
||||
);
|
||||
return matchRegexOrGlobList(depName, massagedPatterns);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import is from '@sindresorhus/is';
|
||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||
import { matchRegexOrGlobList } from '../string-match';
|
||||
import { Matcher } from './base';
|
||||
|
||||
export class PackageNameMatcher extends Matcher {
|
||||
|
@ -14,7 +15,7 @@ export class PackageNameMatcher extends Matcher {
|
|||
if (!packageName) {
|
||||
return false;
|
||||
}
|
||||
return matchPackageNames.includes(packageName);
|
||||
return matchRegexOrGlobList(packageName, matchPackageNames);
|
||||
}
|
||||
|
||||
override excludes(
|
||||
|
@ -28,6 +29,6 @@ export class PackageNameMatcher extends Matcher {
|
|||
if (!packageName) {
|
||||
return false;
|
||||
}
|
||||
return excludePackageNames.includes(packageName);
|
||||
return matchRegexOrGlobList(packageName, excludePackageNames);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,7 @@
|
|||
import is from '@sindresorhus/is';
|
||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||
import { logger } from '../../logger';
|
||||
import { regEx } from '../regex';
|
||||
import { matchRegexOrGlobList } from '../string-match';
|
||||
import { Matcher } from './base';
|
||||
import { massagePattern } from './utils';
|
||||
|
||||
function matchPatternsAgainstName(
|
||||
matchPackagePatterns: string[],
|
||||
name: string,
|
||||
): boolean {
|
||||
let isMatch = false;
|
||||
for (const packagePattern of matchPackagePatterns) {
|
||||
if (isPackagePatternMatch(packagePattern, name)) {
|
||||
isMatch = true;
|
||||
}
|
||||
}
|
||||
return isMatch;
|
||||
}
|
||||
|
||||
export class PackagePatternsMatcher extends Matcher {
|
||||
override matches(
|
||||
|
@ -32,7 +17,10 @@ export class PackagePatternsMatcher extends Matcher {
|
|||
return false;
|
||||
}
|
||||
|
||||
return matchPatternsAgainstName(matchPackagePatterns, packageName);
|
||||
const massagedPatterns = matchPackagePatterns.map((pattern) =>
|
||||
pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
|
||||
);
|
||||
return matchRegexOrGlobList(packageName, massagedPatterns);
|
||||
}
|
||||
|
||||
override excludes(
|
||||
|
@ -48,15 +36,9 @@ export class PackagePatternsMatcher extends Matcher {
|
|||
return false;
|
||||
}
|
||||
|
||||
return matchPatternsAgainstName(excludePackagePatterns, packageName);
|
||||
const massagedPatterns = excludePackagePatterns.map((pattern) =>
|
||||
pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
|
||||
);
|
||||
return matchRegexOrGlobList(packageName, massagedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
function isPackagePatternMatch(pckPattern: string, pck: string): boolean {
|
||||
const re = regEx(massagePattern(pckPattern));
|
||||
if (re.test(pck)) {
|
||||
logger.trace(`${pck} matches against ${String(re)}`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import is from '@sindresorhus/is';
|
||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||
import { matchRegexOrGlobList } from '../string-match';
|
||||
import { Matcher } from './base';
|
||||
|
||||
export class PackagePrefixesMatcher extends Matcher {
|
||||
override matches(
|
||||
{ depName, packageName }: PackageRuleInputConfig,
|
||||
packageRule: PackageRule,
|
||||
{ packageName }: PackageRuleInputConfig,
|
||||
{ matchPackagePrefixes }: PackageRule,
|
||||
): boolean | null {
|
||||
const { matchPackagePrefixes } = packageRule;
|
||||
if (is.undefined(matchPackagePrefixes)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -16,14 +16,10 @@ export class PackagePrefixesMatcher extends Matcher {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
is.string(packageName) &&
|
||||
matchPackagePrefixes.some((prefix) => packageName.startsWith(prefix))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
const massagedPatterns = matchPackagePrefixes.map(
|
||||
(pattern) => `${pattern}**`,
|
||||
);
|
||||
return matchRegexOrGlobList(packageName, massagedPatterns);
|
||||
}
|
||||
|
||||
override excludes(
|
||||
|
@ -38,13 +34,9 @@ export class PackagePrefixesMatcher extends Matcher {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
is.string(packageName) &&
|
||||
excludePackagePrefixes.some((prefix) => packageName.startsWith(prefix))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
const massagedPatterns = excludePackagePrefixes.map(
|
||||
(pattern) => `${pattern}**`,
|
||||
);
|
||||
return matchRegexOrGlobList(packageName, massagedPatterns);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,3 @@ export function matcherOR(
|
|||
}
|
||||
return matchApplied ? false : null;
|
||||
}
|
||||
|
||||
export function massagePattern(pattern: string): string {
|
||||
return pattern === '^*$' || pattern === '*' ? '.*' : pattern;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue