Files
renovate/lib/modules/manager/fleet/extract.spec.ts
Sebastian Poxhofer be13c9f5f7 feat(manager): share OCI Helm chart resolution across managers (#45760)
* feat(manager): share OCI Helm chart resolution across managers

Add getOciChartDep() to helmv3/oci.ts and use it in argocd, fleet, flux,
helmfile, helmsman, helmv3, kustomize, sveltos, terraform and vendir.

- set pinDigests: false for OCI charts in all of them
- apply registryAliases to OCI charts in argocd, helmfile and helmsman
- keep the registry port in terraform helm_release repositories
- skip fleet OCI charts without a version instead of using 'undefined'

* docs(manager/helmv3): correct the OCI chart pinDigests rationale

* refactor(manager/helmfile): drop redundant OCI repository url guard

The repository schema already requires url to be a string, so the falsy
branch could not be reached by parsed input and left the patch partially
covered.
2026-09-09 13:51:30 +00:00

380 lines
12 KiB
TypeScript

import { codeBlock } from 'common-tags';
import { Fixtures } from '~test/fixtures.ts';
import { partial } from '~test/util.ts';
import type { ExtractConfig } from '../types.ts';
import { extractPackageFile } from './index.ts';
const validFleetYaml = Fixtures.get('valid_fleet.yaml');
const inValidFleetYaml = Fixtures.get('invalid_fleet.yaml');
const validGitRepoYaml = Fixtures.get('valid_gitrepo.yaml');
const invalidGitRepoYaml = Fixtures.get('invalid_gitrepo.yaml');
const configMapYaml = Fixtures.get('configmap.yaml', '../kubernetes');
describe('modules/manager/fleet/extract', () => {
afterEach(() => {
vi.restoreAllMocks();
});
describe('extractPackageFile()', () => {
it('should return null if empty content', () => {
const result = extractPackageFile('', 'fleet.yaml', {});
expect(result).toBeNull();
});
it('should return null if a unknown manifest is supplied', () => {
const result = extractPackageFile(configMapYaml, 'fleet.yaml', {});
expect(result).toBeNull();
});
describe('fleet.yaml', () => {
it('should return null if content is a malformed YAML', () => {
const result = extractPackageFile(
`apiVersion: v1
kind: Fleet
< `,
'fleet.yaml',
{},
);
expect(result).toBeNull();
});
it('should parse valid configuration', () => {
const result = extractPackageFile(validFleetYaml, 'fleet.yaml', {});
expect(result).not.toBeNull();
expect(result?.deps).toMatchObject([
{
currentValue: 'v1.8.0',
datasource: 'helm',
depName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
},
{
currentValue: '3.17.7',
datasource: 'helm',
depName: 'logging-operator',
registryUrls: ['https://kubernetes-charts.banzaicloud.com'],
depType: 'fleet',
},
{
currentValue: '25.19.1',
datasource: 'helm',
depName: 'prometheus',
registryUrls: [
'https://prometheus-community.github.io/helm-charts',
],
depType: 'fleet',
},
{
currentValue: '7.1.2',
datasource: 'docker',
depName: 'registry-1.docker.io/bitnamicharts/external-dns',
packageName: 'registry-1.docker.io/bitnamicharts/external-dns',
depType: 'fleet',
pinDigests: false,
},
]);
});
it('should support registryAlias configuration', () => {
const result = extractPackageFile(
codeBlock`
defaultNamespace: cert-manager
helm:
chart: cert-manager
repo: https://registry.com/jetstack
releaseName: cert-manager
version: v1.8.0
---
defaultNamespace: external-dns
helm:
chart: oci://registry.com/docker-io/bitnamicharts/external-dns
version: 7.1.2
`,
'fleet.yaml',
partial<ExtractConfig>({
registryAliases: {
'https://registry.com/jetstack': 'https://charts.jetstack.io',
'registry.com/docker-io': 'registry-1.docker.io',
},
}),
);
expect(result).not.toBeNull();
expect(result?.deps).toMatchObject([
{
currentValue: 'v1.8.0',
datasource: 'helm',
depName: 'cert-manager',
packageName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
},
{
currentValue: '7.1.2',
datasource: 'docker',
depName: 'registry.com/docker-io/bitnamicharts/external-dns',
packageName: 'registry-1.docker.io/bitnamicharts/external-dns',
depType: 'fleet',
pinDigests: false,
},
]);
});
it('should skip OCI charts without version', () => {
const result = extractPackageFile(
codeBlock`
defaultNamespace: external-dns
helm:
chart: oci://registry-1.docker.io/bitnamicharts/external-dns
`,
'fleet.yaml',
{},
);
expect(result?.deps).toEqual([
{
datasource: 'docker',
depName: 'registry-1.docker.io/bitnamicharts/external-dns',
depType: 'fleet',
packageName: 'registry-1.docker.io/bitnamicharts/external-dns',
pinDigests: false,
skipReason: 'unspecified-version',
},
]);
});
it('should parse valid configuration with target customization', () => {
const validFleetYamlWithCustom = codeBlock`
# This should generate two dependencies with different versions
# one with v1.8.0 and one with v1.9.2
defaultNamespace: cert-manager
helm:
chart: cert-manager
repo: https://charts.jetstack.io
releaseName: cert-manager
version: v1.8.0
values:
installCRDs: true
targetCustomizations:
- name: rke2
helm:
version: "v1.9.2"
---
# This should generate two dependencies with different repos
# one with https://charts.jetstack.io and one with https://charts.example.com
defaultNamespace: cert-manager
helm:
chart: cert-manager
repo: https://charts.jetstack.io
releaseName: custom-cert-manager
version: v1.8.0
values:
installCRDs: true
targetCustomizations:
- name: cluster1
helm:
version: "v1.8.2"
repo: https://charts.example.com
---
# This should generate one dependency and a skipped dependency, as there is no version
defaultNamespace: cert-manager
helm:
chart: cert-manager
repo: https://charts.jetstack.io
releaseName: custom-cert-manager
version: v1.8.0
values:
installCRDs: true
targetCustomizations:
- name: cluster1
values:
some: customization
---
# This is a valid target customization with no name
# It should generate one valid dependency, and one skipped dependency, like the one above
defaultNamespace: cert-manager
helm:
chart: cert-manager
repo: https://charts.jetstack.io
releaseName: custom-cert-manager
version: v1.8.0
values:
installCRDs: true
targetCustomizations:
- values:
some: customization
`;
const result = extractPackageFile(
validFleetYamlWithCustom,
'fleet.yaml',
{},
);
expect(result).not.toBeNull();
expect(result?.deps).toMatchObject([
{
currentValue: 'v1.8.0',
datasource: 'helm',
depName: 'cert-manager',
packageName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
},
{
currentValue: 'v1.9.2',
datasource: 'helm',
depName: 'rke2',
packageName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
},
{
currentValue: 'v1.8.0',
datasource: 'helm',
depName: 'cert-manager',
packageName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
},
{
currentValue: 'v1.8.2',
datasource: 'helm',
depName: 'cluster1',
packageName: 'cert-manager',
registryUrls: ['https://charts.example.com'],
depType: 'fleet',
},
{
currentValue: 'v1.8.0',
datasource: 'helm',
depName: 'cert-manager',
packageName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
},
{
datasource: 'helm',
depName: 'cluster1',
packageName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
skipReason: 'unspecified-version',
},
{
currentValue: 'v1.8.0',
datasource: 'helm',
depName: 'cert-manager',
packageName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
},
{
datasource: 'helm',
depName: 'targetCustomization[0]',
packageName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
skipReason: 'unspecified-version',
},
]);
});
it('should parse parse invalid configurations', () => {
const result = extractPackageFile(inValidFleetYaml, 'fleet.yaml', {});
expect(result).not.toBeNull();
expect(result?.deps).toMatchObject([
{
skipReason: 'unspecified-version',
datasource: 'helm',
depName: 'cert-manager',
registryUrls: ['https://charts.jetstack.io'],
depType: 'fleet',
},
{
datasource: 'helm',
depName: 'logging-operator',
skipReason: 'no-repository',
depType: 'fleet',
},
{
datasource: 'helm',
skipReason: 'missing-depname',
depType: 'fleet',
},
{
datasource: 'helm',
depName: './charts/example',
skipReason: 'local-chart',
depType: 'fleet',
},
]);
});
});
describe('GitRepo', () => {
it('should return null if content is a malformed YAML', () => {
const result = extractPackageFile(
`apiVersion: v1
kind: GitRepo
< `,
'test.yaml',
{},
);
expect(result).toBeNull();
});
it('should parse valid configuration', () => {
const result = extractPackageFile(validGitRepoYaml, 'test.yaml', {});
expect(result).not.toBeNull();
expect(result?.deps).toMatchObject([
{
currentValue: 'v0.3.0',
datasource: 'git-tags',
depName: 'https://github.com/rancher/fleet-examples',
depType: 'git_repo',
sourceUrl: 'https://github.com/rancher/fleet-examples',
},
{
currentValue: '32.89.2',
datasource: 'git-tags',
depName: 'https://github.com/renovatebot/renovate',
depType: 'git_repo',
sourceUrl: 'https://github.com/renovatebot/renovate',
},
]);
});
it('should parse invalid configuration', () => {
const result = extractPackageFile(invalidGitRepoYaml, 'test.yaml', {});
expect(result).not.toBeNull();
expect(result?.deps).toMatchObject([
{
datasource: 'git-tags',
depType: 'git_repo',
skipReason: 'missing-depname',
},
{
datasource: 'git-tags',
depName: 'https://github.com/rancher/rancher',
depType: 'git_repo',
skipReason: 'unspecified-version',
sourceUrl: 'https://github.com/rancher/rancher',
},
]);
});
});
});
});