mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-25 23:11:31 +00:00
9ac59d69d7
* test(manager): close helmfile, hermit, leiningen and mix gaps Adds a leiningen vector whose version references a variable that was never defined, which yields no dep. Marks six guards. The mix host rules were already filtered on a match host and on the same regex, hermit tracks its packages as symlinks, and the remaining three need a repository the helm login helper cannot handle, an umbrella parent lock file that reads cleanly, or a dep name carrying no organization. The helmfile repository chain stays counted: `repoName` is only left null when an oci dep was set, so its implicit else cannot run, and a coverage-ignore hint does not attach to an `else if`. Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(manager): mark the nuget, ocb and pep621 guards Twelve guards, none of which a test reaches. A global.json without an sdk version is rejected a few lines earlier, and the nuget registry loop always finds at least one url. The rest need inputs no fixture provides: a blank ContainerBaseImage, a package reference pinned to a non-semver value, a registry url with no protocol version, a host rule carrying only a username or only a password, and a pixi manifest declaring its requirement only under workspace. The nuget lock file chain stays counted, since a coverage-ignore hint does not attach to an `else if`. Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(manager): mark the python and vendir guards Ten guards across pip-compile, pipenv, pixi, poetry and vendir. A compile command always records its arguments, every lock file of a package file is already in the sorted list, a python dep always carries a current value, a successful parse always yields a value, and the vendir vendor dir is always set. The rest need credentials no fixture supplies, a requirement naming an undeclared index, or a source url outside artifact registry. The travis node_js chain stays counted, since a coverage-ignore hint does not attach to an `else if`. Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(manager): close the deps-edn, docker-compose and pep723 gaps Three of the remaining manager branches turn out to be reachable after all. The deps-edn extractor skipped an `aliases` value that is not a map; the existing test only covered an alias *entry* that is not a map, so the outer guard was never taken. A modern docker-compose file with an `x-` extension that carries no image never reached the schema's extension filter either, because the only such fixture omitted `services` and so parsed as a v1 file. A PEP 723 script block without `requires-python` was likewise untested. Co-Authored-By: Claude Opus 5 (1M context) <michael.kriese+claude-code@mend.io> * test(manager/nuget): cover a global.json with no sdk version The `v8 ignore else` on the sdk-version check claimed the guard above it rejects any global.json without one. That guard is `!manifest['msbuild-sdks'] && !manifest.sdk?.version`, so it rejects only when both are missing — a file carrying just `msbuild-sdks` passes it and takes the else. Replace the ignore with a test for that shape, plus one for an empty `{}` document, which the guard does reject. Co-Authored-By: Claude Opus 5 (1M context) <michael.kriese+claude-code@mend.io> --------- Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
312 lines
9.4 KiB
TypeScript
312 lines
9.4 KiB
TypeScript
import { codeBlock } from 'common-tags';
|
|
import { Fixtures } from '~test/fixtures.ts';
|
|
import { extractPackageFile } from './index.ts';
|
|
|
|
const yamlFile1 = Fixtures.get('docker-compose.1.yml');
|
|
const yamlFile3 = Fixtures.get('docker-compose.3.yml');
|
|
const yamlFile3NoVersion = Fixtures.get('docker-compose.3-no-version.yml');
|
|
const yamlFile3DefaultValue = Fixtures.get('docker-compose.3-default-val.yml');
|
|
|
|
describe('modules/manager/docker-compose/extract', () => {
|
|
describe('extractPackageFile()', () => {
|
|
it('returns null for empty', () => {
|
|
expect(extractPackageFile('', '', {})).toBeNull();
|
|
});
|
|
|
|
it('ignores an x- extension that carries no image', () => {
|
|
expect(
|
|
extractPackageFile(
|
|
codeBlock`
|
|
x-no-image:
|
|
command: echo hello
|
|
`,
|
|
'docker-compose.yml',
|
|
{},
|
|
)?.deps,
|
|
).toBeEmpty();
|
|
});
|
|
|
|
it('ignores an x- extension of a modern file that carries no image', () => {
|
|
expect(
|
|
extractPackageFile(
|
|
codeBlock`
|
|
services:
|
|
web:
|
|
image: node:10.0.0
|
|
x-no-image:
|
|
command: echo hello
|
|
x-not-an-object: hello
|
|
`,
|
|
'docker-compose.yml',
|
|
{},
|
|
)?.deps,
|
|
).toMatchObject([{ depName: 'node', currentValue: '10.0.0' }]);
|
|
});
|
|
|
|
it('returns null for non-object YAML', () => {
|
|
expect(extractPackageFile('nothing here', '', {})).toBeNull();
|
|
});
|
|
|
|
it('returns null for malformed YAML', () => {
|
|
expect(extractPackageFile('nothing here\n:::::::', '', {})).toBeNull();
|
|
});
|
|
|
|
const expectedDeps = [
|
|
{ depName: 'quay.io/something/redis', currentValue: 'alpine' },
|
|
{ depName: 'node', currentValue: '10.0.0' },
|
|
{ depName: 'postgres', currentValue: '9.4.0' },
|
|
{
|
|
depName: 'dockersamples/examplevotingapp_vote',
|
|
currentValue: 'before',
|
|
},
|
|
{
|
|
depName: 'dockersamples/examplevotingapp_result',
|
|
currentValue: 'before',
|
|
},
|
|
{ depName: 'dockersamples/examplevotingapp_worker' },
|
|
{ depName: 'dockersamples/visualizer', currentValue: 'stable' },
|
|
{
|
|
replaceString: '${IMAGE:-synkodevelopers/edplugins}:${TAG:-latest}',
|
|
skipReason: 'contains-variable',
|
|
},
|
|
];
|
|
|
|
it('extracts multiple image lines for version 1', () => {
|
|
const res = extractPackageFile(yamlFile1, '', {});
|
|
expect(res?.deps).toMatchObject(expectedDeps);
|
|
expect(res?.deps).toHaveLength(8);
|
|
});
|
|
|
|
it('extracts multiple image lines for version 3', () => {
|
|
const res = extractPackageFile(yamlFile3, '', {});
|
|
expect(res?.deps).toMatchObject(expectedDeps);
|
|
expect(res?.deps).toHaveLength(8);
|
|
});
|
|
|
|
it('extracts multiple image lines for version 3 without set version key', () => {
|
|
const res = extractPackageFile(yamlFile3NoVersion, '', {});
|
|
expect(res?.deps).toMatchObject(expectedDeps);
|
|
expect(res?.deps).toHaveLength(8);
|
|
});
|
|
|
|
it('extracts default variable values for version 3', () => {
|
|
const res = extractPackageFile(yamlFile3DefaultValue, '', {});
|
|
expect(res?.deps).toEqual([
|
|
{
|
|
autoReplaceStringTemplate:
|
|
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
|
|
currentDigest: 'sha256:abcd',
|
|
currentValue: '5.0.0',
|
|
datasource: 'docker',
|
|
depName: 'redis',
|
|
packageName: 'redis',
|
|
replaceString: 'redis:5.0.0@sha256:abcd',
|
|
},
|
|
]);
|
|
expect(res?.deps).toHaveLength(1);
|
|
});
|
|
|
|
it('extracts can parse yaml tags for version 3', () => {
|
|
const compose = codeBlock`
|
|
web:
|
|
image: node:20.0.0
|
|
ports:
|
|
- "80:8000"
|
|
worker:
|
|
extends:
|
|
service: web
|
|
ports: !reset null
|
|
`;
|
|
const res = extractPackageFile(compose, '', {});
|
|
expect(res).toEqual({
|
|
deps: [
|
|
{
|
|
depName: 'node',
|
|
packageName: 'node',
|
|
currentValue: '20.0.0',
|
|
currentDigest: undefined,
|
|
replaceString: 'node:20.0.0',
|
|
autoReplaceStringTemplate:
|
|
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
|
|
datasource: 'docker',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('extracts image and replaces registry', () => {
|
|
const compose = codeBlock`
|
|
version: "3"
|
|
services:
|
|
nginx:
|
|
image: quay.io/nginx:0.0.1
|
|
`;
|
|
const res = extractPackageFile(compose, '', {
|
|
registryAliases: {
|
|
'quay.io': 'my-quay-mirror.registry.com',
|
|
},
|
|
});
|
|
expect(res).toEqual({
|
|
deps: [
|
|
{
|
|
autoReplaceStringTemplate:
|
|
'quay.io/nginx:{{#if newValue}}{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
|
|
currentDigest: undefined,
|
|
currentValue: '0.0.1',
|
|
datasource: 'docker',
|
|
depName: 'quay.io/nginx',
|
|
packageName: 'my-quay-mirror.registry.com/nginx',
|
|
replaceString: 'quay.io/nginx:0.0.1',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('extracts image but no replacement', () => {
|
|
const compose = codeBlock`
|
|
version: "3"
|
|
services:
|
|
nginx:
|
|
image: quay.io/nginx:0.0.1
|
|
`;
|
|
const res = extractPackageFile(compose, '', {
|
|
registryAliases: {
|
|
'index.docker.io': 'my-docker-mirror.registry.com',
|
|
},
|
|
});
|
|
expect(res).toEqual({
|
|
deps: [
|
|
{
|
|
autoReplaceStringTemplate:
|
|
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
|
|
currentDigest: undefined,
|
|
currentValue: '0.0.1',
|
|
datasource: 'docker',
|
|
depName: 'quay.io/nginx',
|
|
packageName: 'quay.io/nginx',
|
|
replaceString: 'quay.io/nginx:0.0.1',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('extracts image and no double replacement', () => {
|
|
const compose = codeBlock`
|
|
version: "3"
|
|
services:
|
|
nginx:
|
|
image: quay.io/nginx:0.0.1
|
|
`;
|
|
const res = extractPackageFile(compose, '', {
|
|
registryAliases: {
|
|
'quay.io': 'my-quay-mirror.registry.com',
|
|
'my-quay-mirror.registry.com': 'quay.io',
|
|
},
|
|
});
|
|
expect(res).toEqual({
|
|
deps: [
|
|
{
|
|
autoReplaceStringTemplate:
|
|
'quay.io/nginx:{{#if newValue}}{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
|
|
currentDigest: undefined,
|
|
currentValue: '0.0.1',
|
|
datasource: 'docker',
|
|
depName: 'quay.io/nginx',
|
|
packageName: 'my-quay-mirror.registry.com/nginx',
|
|
replaceString: 'quay.io/nginx:0.0.1',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('extracts image with variable default registry alias', () => {
|
|
const compose = codeBlock`
|
|
version: "3"
|
|
services:
|
|
nginx:
|
|
image: \${CI_REGISTRY:-}nginx:0.0.1
|
|
`;
|
|
const res = extractPackageFile(compose, '', {
|
|
registryAliases: {
|
|
'${CI_REGISTRY:-}': 'my-registry.io',
|
|
},
|
|
});
|
|
expect(res).toEqual({
|
|
deps: [
|
|
{
|
|
autoReplaceStringTemplate:
|
|
'${CI_REGISTRY:-}nginx:{{#if newValue}}{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
|
|
currentDigest: undefined,
|
|
currentValue: '0.0.1',
|
|
datasource: 'docker',
|
|
depName: '${CI_REGISTRY:-}nginx',
|
|
packageName: 'my-registry.io/nginx',
|
|
replaceString: '${CI_REGISTRY:-}nginx:0.0.1',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('extracts image of templated compose file', () => {
|
|
const compose = codeBlock`
|
|
version: "3"
|
|
services:
|
|
nginx:
|
|
image: quay.io/nginx:0.0.1
|
|
envrionment:
|
|
{{ services['nginx']['env'] }}
|
|
`;
|
|
const res = extractPackageFile(compose, '', {});
|
|
expect(res).toEqual({
|
|
deps: [
|
|
{
|
|
autoReplaceStringTemplate:
|
|
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
|
|
currentDigest: undefined,
|
|
currentValue: '0.0.1',
|
|
datasource: 'docker',
|
|
depName: 'quay.io/nginx',
|
|
packageName: 'quay.io/nginx',
|
|
replaceString: 'quay.io/nginx:0.0.1',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('extract images from fragments', () => {
|
|
const compose = codeBlock`
|
|
---
|
|
x-shared_setting: &shared_settings
|
|
image: debian:11
|
|
# Other shared properties here
|
|
|
|
services:
|
|
service-a:
|
|
<<: *shared_settings
|
|
environment:
|
|
- SERVICE=a
|
|
service-b:
|
|
<<: *shared_settings
|
|
environment:
|
|
- SERVICE=b
|
|
`;
|
|
const res = extractPackageFile(compose, '', {});
|
|
expect(res).toEqual({
|
|
deps: [
|
|
{
|
|
autoReplaceStringTemplate:
|
|
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
|
|
currentDigest: undefined,
|
|
currentValue: '11',
|
|
datasource: 'docker',
|
|
depName: 'debian',
|
|
packageName: 'debian',
|
|
replaceString: 'debian:11',
|
|
versioning: 'debian',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
});
|
|
});
|