From e64b7576e9c75af2e82b6a3d908bd2eebb809bff Mon Sep 17 00:00:00 2001 From: RahulGautamSingh <rahultesnik@gmail.com> Date: Mon, 28 Mar 2022 13:28:20 +0530 Subject: [PATCH] feat: matchSourceUrls (#14813) --- docs/usage/configuration-options.md | 15 ++++++ lib/config/options/index.ts | 12 +++++ lib/config/types.ts | 1 + lib/config/validation.ts | 1 + lib/util/package-rules.spec.ts | 83 +++++++++++++++++++++++++++++ lib/util/package-rules.ts | 11 ++++ 6 files changed, 123 insertions(+) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index c8eb2d5793..cc5194f678 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -1756,6 +1756,21 @@ Here's an example of where you use this to group together all packages from the } ``` +### matchSourceUrls + +Here's an example of where you use this to match exact package urls: + +```json +{ + "packageRules": [ + { + "matchSourceUrls": ["https://github.com/facebook/react"], + "groupName": "React" + } + ] +} +``` + ### matchUpdateTypes Use this field to match rules against types of updates. diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index cdbb0ae6d4..a28240c12e 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -1022,6 +1022,18 @@ const options: RenovateOptions[] = [ cli: false, env: false, }, + { + name: 'matchSourceUrls', + description: 'A list of source URLs to exact match against.', + type: 'array', + subType: 'string', + allowString: true, + stage: 'package', + parent: 'packageRules', + mergeable: true, + cli: false, + env: false, + }, { name: 'replacementName', description: diff --git a/lib/config/types.ts b/lib/config/types.ts index 3bf10bab7e..8c5a068ac3 100644 --- a/lib/config/types.ts +++ b/lib/config/types.ts @@ -274,6 +274,7 @@ export interface PackageRule excludePackagePrefixes?: string[]; matchCurrentVersion?: string | Range; matchSourceUrlPrefixes?: string[]; + matchSourceUrls?: string[]; matchUpdateTypes?: UpdateType[]; } diff --git a/lib/config/validation.ts b/lib/config/validation.ts index 40e40ab33f..383790945c 100644 --- a/lib/config/validation.ts +++ b/lib/config/validation.ts @@ -304,6 +304,7 @@ export async function validateConfig( 'excludePackagePrefixes', 'matchCurrentVersion', 'matchSourceUrlPrefixes', + 'matchSourceUrls', 'matchUpdateTypes', ]; if (key === 'packageRules') { diff --git a/lib/util/package-rules.spec.ts b/lib/util/package-rules.spec.ts index 30816b299a..ffb02321bf 100644 --- a/lib/util/package-rules.spec.ts +++ b/lib/util/package-rules.spec.ts @@ -495,6 +495,68 @@ describe('util/package-rules', () => { const res = applyPackageRules({ ...config, ...dep }); expect(res.x).toBeUndefined(); }); + it('matches matchSourceUrls', () => { + const config: TestConfig = { + packageRules: [ + { + matchSourceUrls: [ + 'https://github.com/foo/bar', + 'https://github.com/renovatebot/presets', + ], + x: 1, + }, + ], + }; + const dep = { + depType: 'dependencies', + depName: 'a', + updateType: 'patch' as UpdateType, + sourceUrl: 'https://github.com/renovatebot/presets', + }; + const res = applyPackageRules({ ...config, ...dep }); + expect(res.x).toBe(1); + }); + it('non-matches matchSourceUrls', () => { + const config: TestConfig = { + packageRules: [ + { + matchSourceUrls: [ + 'https://github.com/foo/bar', + 'https://github.com/facebook/react', + ], + x: 1, + }, + ], + }; + const dep = { + depType: 'dependencies', + depName: 'a', + updateType: 'patch' as UpdateType, + sourceUrl: 'https://github.com/facebook/react-native', + }; + const res = applyPackageRules({ ...config, ...dep }); + expect(res.x).toBeUndefined(); + }); + it('handles matchSourceUrls when missing sourceUrl', () => { + const config: TestConfig = { + packageRules: [ + { + matchSourceUrls: [ + 'https://github.com/foo/bar', + 'https://github.com/renovatebot/', + ], + x: 1, + }, + ], + }; + const dep = { + depType: 'dependencies', + depName: 'a', + updateType: 'patch' as UpdateType, + }; + const res = applyPackageRules({ ...config, ...dep }); + expect(res.x).toBeUndefined(); + }); it('filters naked depType', () => { const config: TestConfig = { packageRules: [ @@ -791,4 +853,25 @@ describe('util/package-rules', () => { const res = applyPackageRules({ ...config, ...dep }); expect(res.x).toBe(1); }); + it('matches matchSourceUrls(case-insensitive)', () => { + const config: TestConfig = { + packageRules: [ + { + matchSourceUrls: [ + 'https://github.com/foo/bar', + 'https://github.com/Renovatebot/renovate', + ], + x: 1, + }, + ], + }; + const dep = { + depType: 'dependencies', + depName: 'a', + updateType: 'patch' as UpdateType, + sourceUrl: 'https://github.com/renovatebot/Renovate', + }; + const res = applyPackageRules({ ...config, ...dep }); + expect(res.x).toBe(1); + }); }); diff --git a/lib/util/package-rules.ts b/lib/util/package-rules.ts index 46a62b37cd..ec18e0f909 100644 --- a/lib/util/package-rules.ts +++ b/lib/util/package-rules.ts @@ -45,6 +45,7 @@ function matchesRule( const excludePackagePatterns = packageRule.excludePackagePatterns || []; const excludePackagePrefixes = packageRule.excludePackagePrefixes || []; const matchSourceUrlPrefixes = packageRule.matchSourceUrlPrefixes || []; + const matchSourceUrls = packageRule.matchSourceUrls || []; const matchCurrentVersion = packageRule.matchCurrentVersion || null; const matchUpdateTypes = packageRule.matchUpdateTypes || []; let positiveMatch = false; @@ -210,6 +211,16 @@ function matchesRule( } positiveMatch = true; } + if (matchSourceUrls.length) { + const upperCaseSourceUrl = sourceUrl?.toUpperCase(); + const isMatch = matchSourceUrls.some( + (url) => upperCaseSourceUrl === url.toUpperCase() + ); + if (!isMatch) { + return false; + } + positiveMatch = true; + } if (matchCurrentVersion) { const version = allVersioning.get(versioning); const matchCurrentVersionStr = matchCurrentVersion.toString(); -- GitLab