diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 94c6e74e0c5a98b036c450a112f1f45d5dc36dff..09f3dea9629397a27e056eaad9fcdf36156856d5 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -2501,23 +2501,13 @@ image: my.new.registry/aRepository/andImage:1.21-alpine ## registryAliases You can use the `registryAliases` object to set registry aliases. -This feature only works with these managers: -- `helm-requirements` -- `helmv3` -- `helmfile` +This feature works with the following managers: -The managers listed above all have this default registryAlias: - -```json -{ - "registryAliases": { - "stable": "https://charts.helm.sh/stable" - } -} -``` - -Alias values must be properly formatted URIs. +- [`helm-requirements`](/modules/manager/helm-requirements/) +- [`helmv3`](/modules/manager/helmv3/) +- [`helmfile`](/modules/manager/helmfile/) +- [`gitlabci`](/modules/manager/gitlabci/) ## registryUrls diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index 71d62885ac790964c6a1a5a103b0081529e6c1e4..a1b99953f926197154a88435946743e9007a3e33 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -822,7 +822,7 @@ const options: RenovateOptions[] = [ type: 'string', format: 'uri', }, - supportedManagers: ['helm-requirements', 'helmv3', 'helmfile'], + supportedManagers: ['helm-requirements', 'helmv3', 'helmfile', 'gitlabci'], }, { name: 'defaultRegistryUrls', diff --git a/lib/modules/manager/dockerfile/extract.spec.ts b/lib/modules/manager/dockerfile/extract.spec.ts index 5335b635f653b72c11e3f62534178d0805518f34..7025eedd99ef65759291840141841efd4aa150df 100644 --- a/lib/modules/manager/dockerfile/extract.spec.ts +++ b/lib/modules/manager/dockerfile/extract.spec.ts @@ -1,4 +1,5 @@ import { Fixtures } from '../../../../test/fixtures'; +import type { PackageDependency } from '../types'; import { extractVariables, getDep } from './extract'; import { extractPackageFile } from '.'; @@ -1149,6 +1150,34 @@ describe('modules/manager/dockerfile/extract', () => { } `); }); + + const defaultAutoReplaceStringTemplate = + '{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}'; + + it.each` + name | registryAliases | imageName | dep + ${'multiple aliases'} | ${{ foo: 'foo.registry.com', bar: 'bar.registry.com' }} | ${'foo/image:1.0'} | ${{ depName: 'foo.registry.com/image', currentValue: '1.0', autoReplaceStringTemplate: `foo/${defaultAutoReplaceStringTemplate}` }} + ${'aliased variable'} | ${{ $CI_REGISTRY: 'registry.com' }} | ${'$CI_REGISTRY/image:1.0'} | ${{ depName: 'registry.com/image', currentValue: '1.0', autoReplaceStringTemplate: `$CI_REGISTRY/${defaultAutoReplaceStringTemplate}` }} + ${'variables with brackets'} | ${{ '${CI_REGISTRY}': 'registry.com' }} | ${'${CI_REGISTRY}/image:1.0'} | ${{ depName: 'registry.com/image', currentValue: '1.0', autoReplaceStringTemplate: `$\{CI_REGISTRY}/${defaultAutoReplaceStringTemplate}` }} + ${'not aliased variable'} | ${{}} | ${'$CI_REGISTRY/image:1.0'} | ${{ autoReplaceStringTemplate: `${defaultAutoReplaceStringTemplate}` }} + ${'plain image'} | ${{}} | ${'registry.com/image:1.0'} | ${{ depName: 'registry.com/image', currentValue: '1.0', autoReplaceStringTemplate: `${defaultAutoReplaceStringTemplate}` }} + `( + 'supports registry aliases - $name', + ({ + registryAliases, + imageName, + dep, + }: { + registryAliases: Record<string, string>; + imageName: string; + dep: PackageDependency; + }) => { + expect(getDep(imageName, true, registryAliases)).toMatchObject({ + ...dep, + replaceString: imageName, + }); + } + ); }); describe('extractVariables()', () => { diff --git a/lib/modules/manager/dockerfile/extract.ts b/lib/modules/manager/dockerfile/extract.ts index 95d6d514ad1bf9d5a28f2832aeac834fb2a2ebcb..d870d4901923012abcf0ffbd10314fbe616323c9 100644 --- a/lib/modules/manager/dockerfile/extract.ts +++ b/lib/modules/manager/dockerfile/extract.ts @@ -1,6 +1,6 @@ import is from '@sindresorhus/is'; import { logger } from '../../../logger'; -import { newlineRegex, regEx } from '../../../util/regex'; +import { escapeRegExp, newlineRegex, regEx } from '../../../util/regex'; import { DockerDatasource } from '../../datasource/docker'; import * as debianVersioning from '../../versioning/debian'; import * as ubuntuVersioning from '../../versioning/ubuntu'; @@ -154,13 +154,31 @@ const quayRegex = regEx(/^quay\.io(?::[1-9][0-9]{0,4})?/i); export function getDep( currentFrom: string | null | undefined, - specifyReplaceString = true + specifyReplaceString = true, + registryAliases?: Record<string, string> ): PackageDependency { if (!is.string(currentFrom)) { return { skipReason: 'invalid-value', }; } + + // Resolve registry aliases first so that we don't need special casing later on: + for (const [name, value] of Object.entries(registryAliases ?? {})) { + const escapedName = escapeRegExp(name); + const groups = regEx(`(?<prefix>${escapedName}/)(?<depName>.+)`).exec( + currentFrom + )?.groups; + if (groups) { + const dep = { + ...getDep(`${value}/${groups.depName}`), + replaceString: currentFrom, + }; + dep.autoReplaceStringTemplate = `${groups.prefix}${dep.autoReplaceStringTemplate}`; + return dep; + } + } + const dep = splitImageParts(currentFrom); if (specifyReplaceString) { if (!dep.replaceString) { diff --git a/lib/modules/manager/gitlabci/extract.spec.ts b/lib/modules/manager/gitlabci/extract.spec.ts index a45d88a1aa2ab9e587d0ab847e41f830021c3400..96a720b30eeae826f79f6fc7f2c102e751a714e6 100644 --- a/lib/modules/manager/gitlabci/extract.spec.ts +++ b/lib/modules/manager/gitlabci/extract.spec.ts @@ -24,7 +24,7 @@ describe('modules/manager/gitlabci/extract', () => { describe('extractAllPackageFile()', () => { it('extracts from empty file', () => { - expect(extractPackageFile('')).toBeNull(); + expect(extractPackageFile('', '', {})).toBeNull(); }); }); @@ -169,7 +169,8 @@ describe('modules/manager/gitlabci/extract', () => { }); it('extract images from dependency proxy', () => { - const res = extractPackageFile(` + const res = extractPackageFile( + ` image: name: $\{CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/renovate/renovate:31.65.1-slim @@ -177,7 +178,10 @@ describe('modules/manager/gitlabci/extract', () => { - $CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX/mariadb:10.4.11 - name: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/other/image1:1.0.0 alias: imagealias1 - `); + `, + '', + {} + ); expect(res?.deps).toEqual([ { autoReplaceStringTemplate: @@ -218,6 +222,60 @@ describe('modules/manager/gitlabci/extract', () => { ]); }); + it('extract images via registry aliases', () => { + const registryAliases = { + $CI_REGISTRY: 'registry.com', + foo: 'foo.registry.com', + }; + const res = extractPackageFile( + ` + image: + name: $CI_REGISTRY/renovate/renovate:31.65.1-slim + + services: + - foo/mariadb:10.4.11 + - name: $CI_REGISTRY/other/image1:1.0.0 + alias: imagealias1 + `, + '', + { + registryAliases, + } + ); + expect(res?.deps).toEqual([ + { + autoReplaceStringTemplate: + '$CI_REGISTRY/{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}', + currentDigest: undefined, + currentValue: '31.65.1-slim', + datasource: 'docker', + depName: 'registry.com/renovate/renovate', + depType: 'image-name', + replaceString: '$CI_REGISTRY/renovate/renovate:31.65.1-slim', + }, + { + autoReplaceStringTemplate: + 'foo/{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}', + currentDigest: undefined, + currentValue: '10.4.11', + datasource: 'docker', + depName: 'foo.registry.com/mariadb', + depType: 'service-image', + replaceString: 'foo/mariadb:10.4.11', + }, + { + autoReplaceStringTemplate: + '$CI_REGISTRY/{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}', + currentDigest: undefined, + currentValue: '1.0.0', + datasource: 'docker', + depName: 'registry.com/other/image1', + depType: 'service-image', + replaceString: '$CI_REGISTRY/other/image1:1.0.0', + }, + ]); + }); + it('extracts from image', () => { let expectedRes = { autoReplaceStringTemplate: diff --git a/lib/modules/manager/gitlabci/extract.ts b/lib/modules/manager/gitlabci/extract.ts index 510734f0144744f2a565730ac40eef1c75aab669..6d1bd3daf03c1e3d3d7aee0f18717ee63600fe95 100644 --- a/lib/modules/manager/gitlabci/extract.ts +++ b/lib/modules/manager/gitlabci/extract.ts @@ -8,24 +8,26 @@ import type { GitlabPipeline, Image, Job, Services } from './types'; import { getGitlabDep, replaceReferenceTags } from './utils'; export function extractFromImage( - image: Image | undefined + image: Image | undefined, + registryAliases?: Record<string, string> ): PackageDependency | null { if (is.undefined(image)) { return null; } let dep: PackageDependency | null = null; if (is.string(image)) { - dep = getGitlabDep(image); + dep = getGitlabDep(image, registryAliases); dep.depType = 'image'; } else if (is.string(image?.name)) { - dep = getGitlabDep(image.name); + dep = getGitlabDep(image.name, registryAliases); dep.depType = 'image-name'; } return dep; } export function extractFromServices( - services: Services | undefined + services: Services | undefined, + registryAliases?: Record<string, string> ): PackageDependency[] { if (is.undefined(services)) { return []; @@ -33,11 +35,11 @@ export function extractFromServices( const deps: PackageDependency[] = []; for (const service of services) { if (is.string(service)) { - const dep = getGitlabDep(service); + const dep = getGitlabDep(service, registryAliases); dep.depType = 'service-image'; deps.push(dep); } else if (is.string(service?.name)) { - const dep = getGitlabDep(service.name); + const dep = getGitlabDep(service.name, registryAliases); dep.depType = 'service-image'; deps.push(dep); } @@ -45,7 +47,10 @@ export function extractFromServices( return deps; } -export function extractFromJob(job: Job | undefined): PackageDependency[] { +export function extractFromJob( + job: Job | undefined, + registryAliases?: Record<string, string> +): PackageDependency[] { if (is.undefined(job)) { return []; } @@ -53,19 +58,23 @@ export function extractFromJob(job: Job | undefined): PackageDependency[] { if (is.object(job)) { const { image, services } = { ...job }; if (is.object(image) || is.string(image)) { - const dep = extractFromImage(image); + const dep = extractFromImage(image, registryAliases); if (dep) { deps.push(dep); } } if (is.array(services)) { - deps.push(...extractFromServices(services)); + deps.push(...extractFromServices(services, registryAliases)); } } return deps; } -export function extractPackageFile(content: string): PackageFile | null { +export function extractPackageFile( + content: string, + _fileName: string, + config: ExtractConfig +): PackageFile | null { let deps: PackageDependency[] = []; try { const doc = load(replaceReferenceTags(content), { @@ -76,7 +85,10 @@ export function extractPackageFile(content: string): PackageFile | null { switch (property) { case 'image': { - const dep = extractFromImage(value as Image); + const dep = extractFromImage( + value as Image, + config.registryAliases + ); if (dep) { deps.push(dep); } @@ -84,11 +96,13 @@ export function extractPackageFile(content: string): PackageFile | null { break; case 'services': - deps.push(...extractFromServices(value as Services)); + deps.push( + ...extractFromServices(value as Services, config.registryAliases) + ); break; default: - deps.push(...extractFromJob(value as Job)); + deps.push(...extractFromJob(value as Job, config.registryAliases)); break; } } @@ -102,7 +116,7 @@ export function extractPackageFile(content: string): PackageFile | null { } export async function extractAllPackageFiles( - _config: ExtractConfig, + config: ExtractConfig, packageFiles: string[] ): Promise<PackageFile[] | null> { const filesToExamine = [...packageFiles]; @@ -147,7 +161,7 @@ export async function extractAllPackageFiles( } } - const result = extractPackageFile(content); + const result = extractPackageFile(content, file, config); if (result !== null) { results.push({ packageFile: file, diff --git a/lib/modules/manager/gitlabci/readme.md b/lib/modules/manager/gitlabci/readme.md index a75de08c5306fdce3f3bf3687f14b10926bd3458..4ec8ff8cd505de1600e899e65b49e11f49aba998 100644 --- a/lib/modules/manager/gitlabci/readme.md +++ b/lib/modules/manager/gitlabci/readme.md @@ -6,3 +6,13 @@ If you use Gitlab Dependency Proxy then you can use these predefined variables a - `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` - `CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX` + +If you use the predefined `CI_REGISTRY` variable make sure to configure its value via `registryAliases`: + +```json +{ + "registryAliases": { + "$CI_REGISTRY": "registry.example.com" + } +} +``` diff --git a/lib/modules/manager/gitlabci/utils.spec.ts b/lib/modules/manager/gitlabci/utils.spec.ts index 73ded7c8d613008f0ae0474a4908a6f89d41dc0d..f9f929780aba486c561d5c7b386b999d16e870b4 100644 --- a/lib/modules/manager/gitlabci/utils.spec.ts +++ b/lib/modules/manager/gitlabci/utils.spec.ts @@ -1,3 +1,4 @@ +import type { PackageDependency } from '../types'; import { getGitlabDep } from './utils'; describe('modules/manager/gitlabci/utils', () => { @@ -42,6 +43,31 @@ describe('modules/manager/gitlabci/utils', () => { } ); + it.each` + name | registryAliases | imageName | dep + ${'multiple aliases'} | ${{ foo: 'foo.registry.com', bar: 'bar.registry.com' }} | ${'foo/image:1.0'} | ${{ depName: 'foo.registry.com/image', currentValue: '1.0', autoReplaceStringTemplate: `foo/${defaultAutoReplaceStringTemplate}` }} + ${'aliased variable'} | ${{ $CI_REGISTRY: 'registry.com' }} | ${'$CI_REGISTRY/image:1.0'} | ${{ depName: 'registry.com/image', currentValue: '1.0', autoReplaceStringTemplate: `$CI_REGISTRY/${defaultAutoReplaceStringTemplate}` }} + ${'variables with brackets'} | ${{ '${CI_REGISTRY}': 'registry.com' }} | ${'${CI_REGISTRY}/image:1.0'} | ${{ depName: 'registry.com/image', currentValue: '1.0', autoReplaceStringTemplate: `$\{CI_REGISTRY}/${defaultAutoReplaceStringTemplate}` }} + ${'not aliased variable'} | ${{}} | ${'$CI_REGISTRY/image:1.0'} | ${{ autoReplaceStringTemplate: `${defaultAutoReplaceStringTemplate}` }} + ${'plain image'} | ${{}} | ${'registry.com/image:1.0'} | ${{ depName: 'registry.com/image', currentValue: '1.0', autoReplaceStringTemplate: `${defaultAutoReplaceStringTemplate}` }} + `( + 'supports registry aliases - $name', + ({ + registryAliases, + imageName, + dep, + }: { + registryAliases: Record<string, string>; + imageName: string; + dep: PackageDependency; + }) => { + expect(getGitlabDep(imageName, registryAliases)).toMatchObject({ + ...dep, + replaceString: imageName, + }); + } + ); + it('no Docker hub', () => { expect( getGitlabDep('quay.io/prometheus/node-exporter:v1.3.1') diff --git a/lib/modules/manager/gitlabci/utils.ts b/lib/modules/manager/gitlabci/utils.ts index 40f33bf47bc6f0105963c4fdc9400331abbe163e..8c6092b2ab0219315801082ec680c6ebf138e957 100644 --- a/lib/modules/manager/gitlabci/utils.ts +++ b/lib/modules/manager/gitlabci/utils.ts @@ -24,14 +24,16 @@ const depProxyRe = regEx( * @param imageName as used in .gitlab-ci.yml file * @return package dependency for the image */ -export function getGitlabDep(imageName: string): PackageDependency { +export function getGitlabDep( + imageName: string, + registryAliases?: Record<string, string> +): PackageDependency { const match = depProxyRe.exec(imageName); if (match?.groups) { const dep = { ...getDep(match.groups.depName), replaceString: imageName }; // TODO: #7154 dep.autoReplaceStringTemplate = `${match.groups.prefix}${dep.autoReplaceStringTemplate}`; return dep; - } else { - return getDep(imageName); } + return getDep(imageName, true, registryAliases); } diff --git a/lib/modules/manager/helm-requirements/readme.md b/lib/modules/manager/helm-requirements/readme.md index ab2b5a0e38549346d5321ccde38a4975e7f28448..5e5c09e5ce081e1c021dcb990185e6a561256930 100644 --- a/lib/modules/manager/helm-requirements/readme.md +++ b/lib/modules/manager/helm-requirements/readme.md @@ -1,6 +1,16 @@ Renovate supports updating Helm Chart references within `requirements.yaml` files. -If your Helm charts make use of repository aliases then you will need to configure an `registryAliases` object in your config to tell Renovate where to look for them. +The `helm-requirements` manager defines this default registryAlias: + +```json +{ + "registryAliases": { + "stable": "https://charts.helm.sh/stable" + } +} +``` + +If your Helm charts make use of repository aliases then you will need to configure an `registryAliases` object in your config to tell Renovate where to look for them. Be aware that alias values must be properly formatted URIs. If you need to change the versioning format, read the [versioning](https://docs.renovatebot.com/modules/versioning/) documentation to learn more. diff --git a/lib/modules/manager/helmfile/readme.md b/lib/modules/manager/helmfile/readme.md index e560becc1256311b42cfb0b577b3b01c839fb140..7b91c62dbb5a18dc8a48c0bc2c00a8d7d1f83668 100644 --- a/lib/modules/manager/helmfile/readme.md +++ b/lib/modules/manager/helmfile/readme.md @@ -1,5 +1,15 @@ Checks `helmfile.yaml` files and extracts dependencies for the `helm` datasource. -If your Helm charts make use of repository aliases then you will need to configure an `registryAliases` object in your config to tell Renovate where to look for them. +The `helmfile` manager defines this default registryAlias: + +```json +{ + "registryAliases": { + "stable": "https://charts.helm.sh/stable" + } +} +``` + +If your Helm charts make use of repository aliases then you will need to configure an `registryAliases` object in your config to tell Renovate where to look for them. Be aware that alias values must be properly formatted URIs. If you need to change the versioning format, read the [versioning](https://docs.renovatebot.com/modules/versioning/) documentation to learn more. diff --git a/lib/modules/manager/helmv3/readme.md b/lib/modules/manager/helmv3/readme.md index ea0c9e915deacc62119480643665e65f8cf42120..212d1e339fc40c4d7d7c01c6cb40bce93411fe2f 100644 --- a/lib/modules/manager/helmv3/readme.md +++ b/lib/modules/manager/helmv3/readme.md @@ -1,6 +1,16 @@ Renovate supports updating Helm Chart references within `requirements.yaml` (Helm v2) and `Chart.yaml` (Helm v3) files. -If your Helm charts make use of repository aliases then you will need to configure an `registryAliases` object in your config to tell Renovate where to look for them. +The `helmv3` manager defines this default registryAlias: + +```json +{ + "registryAliases": { + "stable": "https://charts.helm.sh/stable" + } +} +``` + +If your Helm charts make use of repository aliases then you will need to configure an `registryAliases` object in your config to tell Renovate where to look for them. Be aware that alias values must be properly formatted URIs. If you need to change the versioning format, read the [versioning](https://docs.renovatebot.com/modules/versioning/) documentation to learn more.