mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-12 06:56:24 +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
|
<!-- prettier-ignore -->
|
||||||
|
!!! note
|
||||||
For log level remapping, use this field to match against the particular log messages.
|
`matchDepNames` now supports pattern matching and should be used instead.
|
||||||
|
Use of `matchDepPrefixes` is now deprecated and will be migrated in future.
|
||||||
For more details on supported syntax see Renovate's [string pattern matching documentation](./string-pattern-matching.md).
|
|
||||||
|
|
||||||
### matchNewValue
|
### matchNewValue
|
||||||
|
|
||||||
|
@ -3007,13 +3006,17 @@ For more details on this syntax see Renovate's [string pattern matching document
|
||||||
|
|
||||||
### matchPackageNames
|
### matchPackageNames
|
||||||
|
|
||||||
Use this field if you want to have one or more exact name matches in your package rule.
|
Use this field to match against the `packageName` field.
|
||||||
See also `excludePackageNames`.
|
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": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
|
"matchDatasources": ["npm"],
|
||||||
"matchPackageNames": ["angular"],
|
"matchPackageNames": ["angular"],
|
||||||
"rangeStrategy": "pin"
|
"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 -->
|
```json title="prefix match using Glob"
|
||||||
!!! 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
|
|
||||||
{
|
{
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
"matchPackagePatterns": ["^angular"],
|
"matchDatasources": ["npm"],
|
||||||
"rangeStrategy": "replace"
|
"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 -->
|
<!-- prettier-ignore -->
|
||||||
!!! note
|
!!! note
|
||||||
`matchPackagePatterns` will try matching `packageName` first and then fall back to matching `depName`.
|
`matchPackageNames` now supports pattern matching and should be used instead.
|
||||||
If the fallback is used, Renovate will log a warning, because the fallback will be removed in a future release.
|
Use of `matchPackagePatterns` is now deprecated and will be migrated in future.
|
||||||
Use `matchDepPatterns` instead.
|
|
||||||
|
|
||||||
### matchPackagePrefixes
|
### 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.
|
Use this field to match a package prefix without needing to write a regex expression.
|
||||||
See also `excludePackagePrefixes`.
|
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`.
|
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
|
### matchRepositories
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import is from '@sindresorhus/is';
|
import is from '@sindresorhus/is';
|
||||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||||
|
import { matchRegexOrGlobList } from '../string-match';
|
||||||
import { Matcher } from './base';
|
import { Matcher } from './base';
|
||||||
|
|
||||||
export class DepNameMatcher extends Matcher {
|
export class DepNameMatcher extends Matcher {
|
||||||
|
@ -13,7 +14,7 @@ export class DepNameMatcher extends Matcher {
|
||||||
if (is.undefined(depName)) {
|
if (is.undefined(depName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return matchDepNames.includes(depName);
|
return matchRegexOrGlobList(depName, matchDepNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
override excludes(
|
override excludes(
|
||||||
|
@ -26,6 +27,6 @@ export class DepNameMatcher extends Matcher {
|
||||||
if (is.undefined(depName)) {
|
if (is.undefined(depName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return excludeDepNames.includes(depName);
|
return matchRegexOrGlobList(depName, excludeDepNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,30 @@ describe('util/package-rules/dep-patterns', () => {
|
||||||
);
|
);
|
||||||
expect(result).toBeFalse();
|
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', () => {
|
describe('exclude', () => {
|
||||||
|
@ -29,5 +53,29 @@ describe('util/package-rules/dep-patterns', () => {
|
||||||
);
|
);
|
||||||
expect(result).toBeFalse();
|
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 is from '@sindresorhus/is';
|
||||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||||
import { logger } from '../../logger';
|
import { matchRegexOrGlobList } from '../string-match';
|
||||||
import { regEx } from '../regex';
|
|
||||||
import { Matcher } from './base';
|
import { Matcher } from './base';
|
||||||
import { massagePattern } from './utils';
|
|
||||||
|
|
||||||
export class DepPatternsMatcher extends Matcher {
|
export class DepPatternsMatcher extends Matcher {
|
||||||
override matches(
|
override matches(
|
||||||
{ depName, updateType }: PackageRuleInputConfig,
|
{ depName }: PackageRuleInputConfig,
|
||||||
{ matchDepPatterns }: PackageRule,
|
{ matchDepPatterns }: PackageRule,
|
||||||
): boolean | null {
|
): boolean | null {
|
||||||
if (is.undefined(matchDepPatterns)) {
|
if (is.undefined(matchDepPatterns)) {
|
||||||
|
@ -18,22 +16,16 @@ export class DepPatternsMatcher extends Matcher {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let isMatch = false;
|
const massagedPatterns = matchDepPatterns.map((pattern) =>
|
||||||
for (const packagePattern of matchDepPatterns) {
|
pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
|
||||||
const packageRegex = regEx(massagePattern(packagePattern));
|
);
|
||||||
if (packageRegex.test(depName)) {
|
return matchRegexOrGlobList(depName, massagedPatterns);
|
||||||
logger.trace(`${depName} matches against ${String(packageRegex)}`);
|
|
||||||
isMatch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isMatch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override excludes(
|
override excludes(
|
||||||
{ depName, updateType }: PackageRuleInputConfig,
|
{ depName }: PackageRuleInputConfig,
|
||||||
{ excludeDepPatterns }: PackageRule,
|
{ excludeDepPatterns }: PackageRule,
|
||||||
): boolean | null {
|
): boolean | null {
|
||||||
// ignore lockFileMaintenance for backwards compatibility
|
|
||||||
if (is.undefined(excludeDepPatterns)) {
|
if (is.undefined(excludeDepPatterns)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -41,14 +33,9 @@ export class DepPatternsMatcher extends Matcher {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let isMatch = false;
|
const massagedPatterns = excludeDepPatterns.map((pattern) =>
|
||||||
for (const pattern of excludeDepPatterns) {
|
pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
|
||||||
const packageRegex = regEx(massagePattern(pattern));
|
);
|
||||||
if (packageRegex.test(depName)) {
|
return matchRegexOrGlobList(depName, massagedPatterns);
|
||||||
logger.trace(`${depName} matches against ${String(packageRegex)}`);
|
|
||||||
isMatch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isMatch;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import is from '@sindresorhus/is';
|
import is from '@sindresorhus/is';
|
||||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||||
|
import { matchRegexOrGlobList } from '../string-match';
|
||||||
import { Matcher } from './base';
|
import { Matcher } from './base';
|
||||||
|
|
||||||
export class PackageNameMatcher extends Matcher {
|
export class PackageNameMatcher extends Matcher {
|
||||||
|
@ -14,7 +15,7 @@ export class PackageNameMatcher extends Matcher {
|
||||||
if (!packageName) {
|
if (!packageName) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return matchPackageNames.includes(packageName);
|
return matchRegexOrGlobList(packageName, matchPackageNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
override excludes(
|
override excludes(
|
||||||
|
@ -28,6 +29,6 @@ export class PackageNameMatcher extends Matcher {
|
||||||
if (!packageName) {
|
if (!packageName) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return excludePackageNames.includes(packageName);
|
return matchRegexOrGlobList(packageName, excludePackageNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,7 @@
|
||||||
import is from '@sindresorhus/is';
|
import is from '@sindresorhus/is';
|
||||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||||
import { logger } from '../../logger';
|
import { matchRegexOrGlobList } from '../string-match';
|
||||||
import { regEx } from '../regex';
|
|
||||||
import { Matcher } from './base';
|
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 {
|
export class PackagePatternsMatcher extends Matcher {
|
||||||
override matches(
|
override matches(
|
||||||
|
@ -32,7 +17,10 @@ export class PackagePatternsMatcher extends Matcher {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchPatternsAgainstName(matchPackagePatterns, packageName);
|
const massagedPatterns = matchPackagePatterns.map((pattern) =>
|
||||||
|
pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
|
||||||
|
);
|
||||||
|
return matchRegexOrGlobList(packageName, massagedPatterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
override excludes(
|
override excludes(
|
||||||
|
@ -48,15 +36,9 @@ export class PackagePatternsMatcher extends Matcher {
|
||||||
return false;
|
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 is from '@sindresorhus/is';
|
||||||
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
|
||||||
|
import { matchRegexOrGlobList } from '../string-match';
|
||||||
import { Matcher } from './base';
|
import { Matcher } from './base';
|
||||||
|
|
||||||
export class PackagePrefixesMatcher extends Matcher {
|
export class PackagePrefixesMatcher extends Matcher {
|
||||||
override matches(
|
override matches(
|
||||||
{ depName, packageName }: PackageRuleInputConfig,
|
{ packageName }: PackageRuleInputConfig,
|
||||||
packageRule: PackageRule,
|
{ matchPackagePrefixes }: PackageRule,
|
||||||
): boolean | null {
|
): boolean | null {
|
||||||
const { matchPackagePrefixes } = packageRule;
|
|
||||||
if (is.undefined(matchPackagePrefixes)) {
|
if (is.undefined(matchPackagePrefixes)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,10 @@ export class PackagePrefixesMatcher extends Matcher {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const massagedPatterns = matchPackagePrefixes.map(
|
||||||
is.string(packageName) &&
|
(pattern) => `${pattern}**`,
|
||||||
matchPackagePrefixes.some((prefix) => packageName.startsWith(prefix))
|
);
|
||||||
) {
|
return matchRegexOrGlobList(packageName, massagedPatterns);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override excludes(
|
override excludes(
|
||||||
|
@ -38,13 +34,9 @@ export class PackagePrefixesMatcher extends Matcher {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const massagedPatterns = excludePackagePrefixes.map(
|
||||||
is.string(packageName) &&
|
(pattern) => `${pattern}**`,
|
||||||
excludePackagePrefixes.some((prefix) => packageName.startsWith(prefix))
|
);
|
||||||
) {
|
return matchRegexOrGlobList(packageName, massagedPatterns);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,3 @@ export function matcherOR(
|
||||||
}
|
}
|
||||||
return matchApplied ? false : null;
|
return matchApplied ? false : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function massagePattern(pattern: string): string {
|
|
||||||
return pattern === '^*$' || pattern === '*' ? '.*' : pattern;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue