diff --git a/lib/modules/manager/gitlabci/extract.spec.ts b/lib/modules/manager/gitlabci/extract.spec.ts index cd218f108b0f2e..16d19ea37b9e59 100644 --- a/lib/modules/manager/gitlabci/extract.spec.ts +++ b/lib/modules/manager/gitlabci/extract.spec.ts @@ -167,6 +167,56 @@ describe('modules/manager/gitlabci/extract', () => { ]); }); + it('extract images from dependency proxy', () => { + const res = extractPackageFile(` + image: + name: $\{CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/renovate/renovate:31.65.1-slim + + services: + - $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: + '${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/' + + '{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}', + currentDigest: undefined, + currentValue: '31.65.1-slim', + datasource: 'docker', + depName: 'renovate/renovate', + depType: 'image-name', + replaceString: + '${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/renovate/renovate:31.65.1-slim', + }, + { + autoReplaceStringTemplate: + '$CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX/' + + '{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}', + currentDigest: undefined, + currentValue: '10.4.11', + datasource: 'docker', + depName: 'mariadb', + depType: 'service-image', + replaceString: + '$CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX/mariadb:10.4.11', + }, + { + autoReplaceStringTemplate: + '$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/' + + '{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}', + currentDigest: undefined, + currentValue: '1.0.0', + datasource: 'docker', + depName: 'other/image1', + depType: 'service-image', + replaceString: + '$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/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 1e0663ea2ef066..510734f0144744 100644 --- a/lib/modules/manager/gitlabci/extract.ts +++ b/lib/modules/manager/gitlabci/extract.ts @@ -3,10 +3,9 @@ import { load } from 'js-yaml'; import { logger } from '../../../logger'; import { readLocalFile } from '../../../util/fs'; import { regEx } from '../../../util/regex'; -import { getDep } from '../dockerfile/extract'; import type { ExtractConfig, PackageDependency, PackageFile } from '../types'; import type { GitlabPipeline, Image, Job, Services } from './types'; -import { replaceReferenceTags } from './utils'; +import { getGitlabDep, replaceReferenceTags } from './utils'; export function extractFromImage( image: Image | undefined @@ -16,10 +15,10 @@ export function extractFromImage( } let dep: PackageDependency | null = null; if (is.string(image)) { - dep = getDep(image); + dep = getGitlabDep(image); dep.depType = 'image'; } else if (is.string(image?.name)) { - dep = getDep(image.name); + dep = getGitlabDep(image.name); dep.depType = 'image-name'; } return dep; @@ -34,11 +33,11 @@ export function extractFromServices( const deps: PackageDependency[] = []; for (const service of services) { if (is.string(service)) { - const dep = getDep(service); + const dep = getGitlabDep(service); dep.depType = 'service-image'; deps.push(dep); } else if (is.string(service?.name)) { - const dep = getDep(service.name); + const dep = getGitlabDep(service.name); dep.depType = 'service-image'; deps.push(dep); } diff --git a/lib/modules/manager/gitlabci/readme.md b/lib/modules/manager/gitlabci/readme.md index a40d170e7d0325..4ffb22a747b860 100644 --- a/lib/modules/manager/gitlabci/readme.md +++ b/lib/modules/manager/gitlabci/readme.md @@ -1,3 +1,5 @@ Extracts Docker dependencies from `gitlab-ci.yml` files. If you need to change the versioning format, read the [versioning](https://docs.renovatebot.com/modules/versioning/) documentation to learn more. + +If you use Gitlab Dependency Proxy usage of the predefined variables `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` and `CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX` as image prefix is supported. diff --git a/lib/modules/manager/gitlabci/utils.spec.ts b/lib/modules/manager/gitlabci/utils.spec.ts new file mode 100644 index 00000000000000..73ded7c8d61300 --- /dev/null +++ b/lib/modules/manager/gitlabci/utils.spec.ts @@ -0,0 +1,56 @@ +import { getGitlabDep } from './utils'; + +describe('modules/manager/gitlabci/utils', () => { + describe('getGitlabDep', () => { + const defaultAutoReplaceStringTemplate = + '{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}'; + + it.each` + name | imagePrefix + ${'no variable'} | ${''} + ${'group proxy'} | ${'$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/'} + ${'group proxy with brackets'} | ${'${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/'} + ${'direct group proxy'} | ${'$CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX/'} + `('offical image - $name', ({ imagePrefix }: { imagePrefix: string }) => { + const imageName = `${imagePrefix}mariadb:10.4.11`; + expect(getGitlabDep(imageName)).toMatchObject({ + autoReplaceStringTemplate: + imagePrefix + defaultAutoReplaceStringTemplate, + replaceString: imageName, + depName: 'mariadb', + currentValue: '10.4.11', + }); + }); + + it.each` + name | imagePrefix + ${'no variable'} | ${''} + ${'group proxy'} | ${'$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/'} + ${'group proxy with brackets'} | ${'${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/'} + ${'direct group proxy'} | ${'$CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX/'} + `( + 'image with organization - $name', + ({ imagePrefix }: { imagePrefix: string }) => { + const imageName = `${imagePrefix}renovate/renovate:19.70.8-slim`; + expect(getGitlabDep(imageName)).toMatchObject({ + autoReplaceStringTemplate: + imagePrefix + defaultAutoReplaceStringTemplate, + replaceString: imageName, + depName: 'renovate/renovate', + currentValue: '19.70.8-slim', + }); + } + ); + + it('no Docker hub', () => { + expect( + getGitlabDep('quay.io/prometheus/node-exporter:v1.3.1') + ).toMatchObject({ + autoReplaceStringTemplate: defaultAutoReplaceStringTemplate, + replaceString: 'quay.io/prometheus/node-exporter:v1.3.1', + depName: 'quay.io/prometheus/node-exporter', + currentValue: 'v1.3.1', + }); + }); + }); +}); diff --git a/lib/modules/manager/gitlabci/utils.ts b/lib/modules/manager/gitlabci/utils.ts index 28124d3179d82e..39b30691354692 100644 --- a/lib/modules/manager/gitlabci/utils.ts +++ b/lib/modules/manager/gitlabci/utils.ts @@ -1,3 +1,7 @@ +import { regEx } from '../../../util/regex'; +import { getDep } from '../dockerfile/extract'; +import type { PackageDependency } from '../types'; + const re = /!reference \[(.*?)\]/g; /** @@ -10,3 +14,24 @@ export function replaceReferenceTags(content: string): string { const res = content.replace(re, ''); return res; } + +const depProxyRe = regEx( + `(?\\$\\{?CI_DEPENDENCY_PROXY_(?:DIRECT_)?GROUP_IMAGE_PREFIX\\}?\\/)(?.+)` +); + +/** + * Get image dependencies respecting Gitlab Dependency Proxy + * @param imageName as used in .gitlab-ci.yml file + * @return package dependency for the image + */ +export function getGitlabDep(imageName: string): PackageDependency { + const match = depProxyRe.exec(imageName); + if (match?.groups) { + const dep = { ...getDep(match.groups.depName), replaceString: imageName }; + dep.autoReplaceStringTemplate = + match.groups.prefix + dep.autoReplaceStringTemplate; + return dep; + } else { + return getDep(imageName); + } +}