Files
renovate/lib/modules/manager/gitlabci/extract.spec.ts
Sebastian Poxhofer 05e1271376 test: use expect().resolves instead of awaiting inside expect (#45672)
Enables the vitest/prefer-expect-resolves oxlint rule and applies its
autofix across the spec suite.
2026-09-04 10:40:46 +00:00

612 lines
20 KiB
TypeScript

import { codeBlock } from 'common-tags';
import { logger } from '~test/util.ts';
import { GlobalConfig } from '../../../config/global.ts';
import type {
InternalGlobalConfigOptions,
RepoGlobalConfig,
} from '../../../config/types.ts';
import type { ExtractConfig, PackageDependency } from '../types.ts';
import { extractAllPackageFiles, extractPackageFile } from './index.ts';
const config: ExtractConfig = {};
const adminConfig: RepoGlobalConfig & InternalGlobalConfigOptions = {
localDir: '',
};
describe('modules/manager/gitlabci/extract', () => {
beforeEach(() => {
GlobalConfig.set(adminConfig);
});
afterEach(() => {
GlobalConfig.reset();
});
describe('extractAllPackageFile()', () => {
it('extracts from empty file', () => {
expect(extractPackageFile('', '', {})).toBeNull();
});
});
describe('extractAllPackageFiles()', () => {
it('returns null for empty', async () => {
await expect(
extractAllPackageFiles(config, [
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.2.yaml',
]),
).resolves.toBeNull();
});
it('extracts from multidoc yaml', async () => {
const res = await extractAllPackageFiles(config, [
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.multi-doc.yaml',
]);
expect(res).toHaveLength(3);
const deps = res?.map((entry) => entry.deps).flat();
expect(deps).toHaveLength(8);
});
it('extracts multiple included image lines', async () => {
const res = await extractAllPackageFiles(config, [
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.3.yaml',
]);
expect(res).toMatchObject([
{
packageFile:
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.3.yaml',
deps: [
{
depName: 'renovate/renovate',
currentValue: '19.70.8-slim',
depType: 'image-name',
},
{
depName: 'mariadb',
currentValue: '10.4.11',
depType: 'service-image',
},
{
depName: 'other/image',
currentValue: '1.0.0',
depType: 'service-image',
},
],
},
{
packageFile:
'lib/modules/manager/gitlabci/__fixtures__/include.1.yml',
deps: [{ depName: 'node', currentValue: '12', depType: 'image' }],
},
{
packageFile: 'lib/modules/manager/gitlabci/__fixtures__/include.yml',
deps: [{ depName: 'alpine', currentValue: '3.11', depType: 'image' }],
},
]);
const deps = res?.map((entry) => entry.deps).flat();
expect(deps).toHaveLength(5);
});
it('extracts named services', async () => {
const res = await extractAllPackageFiles(config, [
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.5.yaml',
]);
expect(res).toMatchObject([
{
packageFile:
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.5.yaml',
deps: [
{
depName: 'renovate/renovate',
currentValue: '19.70.8-slim',
depType: 'image-name',
},
{
depName: 'mariadb',
currentValue: '10.4.11',
depType: 'service-image',
},
{
depName: 'other/image',
currentValue: '1.0.0',
depType: 'service-image',
},
],
},
]);
});
it('extracts multiple named services', async () => {
const res = await extractAllPackageFiles(config, [
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.6.yaml',
]);
expect(res).toMatchObject([
{
packageFile:
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.6.yaml',
deps: [
{
depName: 'renovate/renovate',
currentValue: '19.70.8-slim',
depType: 'image-name',
},
{
depName: 'other/image1',
currentValue: '1.0.0',
depType: 'service-image',
},
{
depName: 'other/image2',
currentValue: '1.0.0',
depType: 'service-image',
},
{
depName: 'mooseagency/postgresql',
currentValue: '12.3-1',
currentDigest:
'sha256:a5a65569456f221ee1f8a0b3b4e2d440eb5830772d9440c9b30b1dbfd454c778',
depType: 'service-image',
},
{
depName: 'mariadb',
currentValue: '10.4.11',
depType: 'service-image',
},
{
depName: 'postgres',
currentValue: '11.7',
depType: 'service-image',
},
{
depName: 'redis',
currentValue: 'latest',
depType: 'service-image',
},
{
depName: 'registry.example.com/myimage',
currentValue: 'latest',
depType: 'service-image',
},
{
depName: 'myimage',
currentDigest: 'sha256:0ecb2ad60',
depType: 'service-image',
},
{
depName: 'tomcat',
currentValue: '7-jre8',
depType: 'service-image',
},
],
},
]);
});
it('extracts multiple image lines', async () => {
const res = await extractAllPackageFiles(config, [
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.yaml',
]);
expect(res).toMatchObject([
{
packageFile:
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.yaml',
deps: [
{ depName: 'ruby', currentValue: '2.5.0', depType: 'image' },
{
depName: 'hadolint/hadolint',
currentValue: 'latest',
depType: 'image',
},
{ depName: 'docker', currentValue: 'latest', depType: 'image' },
{
depName: 'docker',
currentValue: 'dind',
depType: 'service-image',
},
{ depName: 'docker', currentValue: 'latest', depType: 'image' },
{
depName: 'docker',
currentValue: 'dind',
depType: 'service-image',
},
{
depName: 'image-name-test',
currentValue: '1.15',
depType: 'image-name',
},
{
depName: 'image-name-test',
currentValue: '1.15',
depType: 'image-name',
},
],
},
]);
const deps: PackageDependency[] = [];
res?.forEach((e) => {
e.deps.forEach((d) => {
deps.push(d);
});
});
expect(deps).toHaveLength(8);
// TODO #22198
expect(deps.some((dep) => dep.currentValue!.includes("'"))).toBeFalse();
});
it('extracts multiple image lines with comments', async () => {
const res = await extractAllPackageFiles(config, [
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.1.yaml',
]);
expect(res).toMatchObject([
{
packageFile:
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.1.yaml',
deps: [
{
depName: 'renovate/renovate',
currentValue: '19.70.8-slim',
depType: 'image-name',
},
{
depName: 'mariadb',
currentValue: '10.4.11',
depType: 'service-image',
},
{
depName: 'other/image',
currentValue: '1.0.0',
depType: 'service-image',
},
],
},
]);
const deps: PackageDependency[] = [];
res?.forEach((e) => {
e.deps.forEach((d) => {
deps.push(d);
});
});
expect(deps).toHaveLength(3);
});
it('catches errors', async () => {
const res = await extractAllPackageFiles(config, [
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.4.yaml',
]);
expect(res).toBeNull();
expect(logger.logger.debug).toHaveBeenCalled();
});
it('skips images with variables', async () => {
const res = await extractAllPackageFiles(config, [
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.7.yaml',
]);
expect(res).toEqual([
{
deps: [
{
autoReplaceStringTemplate:
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
datasource: 'docker',
depType: 'image-name',
replaceString: '$VARIABLE/renovate/renovate:31.65.1-slim',
skipReason: 'contains-variable',
},
{
autoReplaceStringTemplate:
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
datasource: 'docker',
depType: 'service-image',
replaceString: '$VARIABLE/other/image1:1.0.0',
skipReason: 'contains-variable',
},
{
autoReplaceStringTemplate:
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
datasource: 'docker',
depType: 'service-image',
replaceString: '${VARIABLE}/other/image1:2.0.0',
skipReason: 'contains-variable',
},
{
autoReplaceStringTemplate:
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
datasource: 'docker',
depType: 'service-image',
replaceString: 'docker.io/$VARIABLE/image1:3.0.0',
skipReason: 'contains-variable',
},
{
autoReplaceStringTemplate:
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
datasource: 'docker',
depType: 'service-image',
replaceString: 'docker.io/${VARIABLE}/image1:4.0.0',
skipReason: 'contains-variable',
},
],
packageFile:
'lib/modules/manager/gitlabci/__fixtures__/gitlab-ci.7.yaml',
},
]);
});
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',
packageName: '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',
packageName: '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',
packageName: 'other/image1',
depType: 'service-image',
replaceString:
'$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/other/image1:1.0.0',
},
]);
});
it('extract images via registry aliases', () => {
const registryAliases = {
$CI_REGISTRY: 'registry.com',
$BUILD_IMAGES: 'registry.com/build-images',
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
- $BUILD_IMAGES/image2:1.0.0
`,
'',
{
registryAliases,
},
);
expect(res?.deps).toEqual([
{
autoReplaceStringTemplate:
'$CI_REGISTRY/renovate/renovate:{{#if newValue}}{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
currentDigest: undefined,
currentValue: '31.65.1-slim',
datasource: 'docker',
depName: '$CI_REGISTRY/renovate/renovate',
packageName: 'registry.com/renovate/renovate',
depType: 'image-name',
replaceString: '$CI_REGISTRY/renovate/renovate:31.65.1-slim',
},
{
autoReplaceStringTemplate:
'foo/mariadb:{{#if newValue}}{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
currentDigest: undefined,
currentValue: '10.4.11',
datasource: 'docker',
depName: 'foo/mariadb',
packageName: 'foo.registry.com/mariadb',
depType: 'service-image',
replaceString: 'foo/mariadb:10.4.11',
},
{
autoReplaceStringTemplate:
'$CI_REGISTRY/other/image1:{{#if newValue}}{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
currentDigest: undefined,
currentValue: '1.0.0',
datasource: 'docker',
depName: '$CI_REGISTRY/other/image1',
packageName: 'registry.com/other/image1',
depType: 'service-image',
replaceString: '$CI_REGISTRY/other/image1:1.0.0',
},
{
autoReplaceStringTemplate:
'$BUILD_IMAGES/image2:{{#if newValue}}{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
currentDigest: undefined,
currentValue: '1.0.0',
datasource: 'docker',
depName: '$BUILD_IMAGES/image2',
packageName: 'registry.com/build-images/image2',
depType: 'service-image',
replaceString: '$BUILD_IMAGES/image2:1.0.0',
},
]);
});
it('extracts component references via registry aliases', () => {
const registryAliases = {
$CI_SERVER_HOST: 'gitlab.example.com',
$COMPONENT_REGISTRY: 'gitlab.example.com/a-group',
};
const content = codeBlock`
include:
- component: $CI_SERVER_HOST/an-org/a-project/a-component@1.0
inputs:
stage: build
- component: $CI_SERVER_HOST/an-org/a-subgroup/a-project/a-component@e3262fdd0914fa823210cdb79a8c421e2cef79d8
- component: $CI_SERVER_HOST/an-org/a-subgroup/another-project/a-component@main
- component: $CI_SERVER_HOST/another-org/a-project/a-component@~latest
inputs:
stage: test
- component: $CI_SERVER_HOST/malformed-component-reference
- component:
malformed: true
- component: $CI_SERVER_HOST/an-org/a-component@1.0
- component: other-gitlab.example.com/an-org/a-project/a-component@1.0
- component: $COMPONENT_REGISTRY/a-project/a-component@1.0
`;
const res = extractPackageFile(content, '', {
registryAliases,
});
expect(res?.deps).toMatchObject([
{
currentValue: '1.0',
datasource: 'gitlab-tags',
depName: 'an-org/a-project',
depType: 'repository',
registryUrls: ['https://gitlab.example.com'],
versioning: 'semver-partial',
},
{
currentValue: 'e3262fdd0914fa823210cdb79a8c421e2cef79d8',
datasource: 'gitlab-tags',
depName: 'an-org/a-subgroup/a-project',
depType: 'repository',
registryUrls: ['https://gitlab.example.com'],
versioning: 'semver-partial',
},
{
currentValue: 'main',
datasource: 'gitlab-tags',
depName: 'an-org/a-subgroup/another-project',
depType: 'repository',
registryUrls: ['https://gitlab.example.com'],
versioning: 'semver-partial',
},
{
currentValue: '~latest',
datasource: 'gitlab-tags',
depName: 'another-org/a-project',
depType: 'repository',
registryUrls: ['https://gitlab.example.com'],
versioning: 'semver-partial',
},
{
currentValue: '1.0',
datasource: 'gitlab-tags',
depName: 'an-org/a-project',
depType: 'repository',
registryUrls: ['https://other-gitlab.example.com'],
versioning: 'semver-partial',
},
{
currentValue: '1.0',
datasource: 'gitlab-tags',
depName: 'a-group/a-project',
depType: 'repository',
registryUrls: ['https://gitlab.example.com'],
versioning: 'semver-partial',
},
]);
expect(
res?.deps.find((dep) => dep.currentValue === '~latest'),
).not.toHaveProperty('skipReason');
});
it('extracts component references', () => {
const content = codeBlock`
include:
- component: gitlab.example.com/an-org/a-project/a-component@1.0
inputs:
stage: build
- component: gitlab.example.com/an-org/a-subgroup/a-project/a-component@e3262fdd0914fa823210cdb79a8c421e2cef79d8
- component: gitlab.example.com/an-org/a-subgroup/another-project/a-component@main
- component: gitlab.example.com/another-org/a-project/a-component@~latest
inputs:
stage: test
- component: gitlab.example.com/malformed-component-reference
- component:
malformed: true
- component: gitlab.example.com/an-org/a-component@1.0
- component: other-gitlab.example.com/an-org/a-project/a-component@1.0
`;
const res = extractPackageFile(content, '', {});
expect(res?.deps).toMatchObject([
{
currentValue: '1.0',
datasource: 'gitlab-tags',
depName: 'an-org/a-project',
depType: 'repository',
registryUrls: ['https://gitlab.example.com'],
versioning: 'semver-partial',
},
{
currentValue: 'e3262fdd0914fa823210cdb79a8c421e2cef79d8',
datasource: 'gitlab-tags',
depName: 'an-org/a-subgroup/a-project',
depType: 'repository',
registryUrls: ['https://gitlab.example.com'],
versioning: 'semver-partial',
},
{
currentValue: 'main',
datasource: 'gitlab-tags',
depName: 'an-org/a-subgroup/another-project',
depType: 'repository',
registryUrls: ['https://gitlab.example.com'],
versioning: 'semver-partial',
},
{
currentValue: '~latest',
datasource: 'gitlab-tags',
depName: 'another-org/a-project',
depType: 'repository',
registryUrls: ['https://gitlab.example.com'],
versioning: 'semver-partial',
},
{
currentValue: '1.0',
datasource: 'gitlab-tags',
depName: 'an-org/a-project',
depType: 'repository',
versioning: 'semver-partial',
registryUrls: ['https://other-gitlab.example.com'],
},
]);
expect(
res?.deps.find((dep) => dep.currentValue === '~latest'),
).not.toHaveProperty('skipReason');
});
});
});