mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-31 01:37:24 +00:00
be13c9f5f7
* 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.
175 lines
4.9 KiB
TypeScript
175 lines
4.9 KiB
TypeScript
import { isEmptyArray, isString } from '@sindresorhus/is';
|
|
import { logger } from '../../../logger/index.ts';
|
|
import { coerceArray } from '../../../util/array.ts';
|
|
import { coerceObject } from '../../../util/object.ts';
|
|
import { regEx } from '../../../util/regex.ts';
|
|
import { parseYaml } from '../../../util/yaml.ts';
|
|
import { DockerDatasource } from '../../datasource/docker/index.ts';
|
|
import { HelmDatasource } from '../../datasource/helm/index.ts';
|
|
import {
|
|
getOciChartDep,
|
|
isOCIRegistry,
|
|
removeOCIPrefix,
|
|
} from '../helmv3/oci.ts';
|
|
import type {
|
|
ExtractConfig,
|
|
PackageDependency,
|
|
PackageFileContent,
|
|
} from '../types.ts';
|
|
import type { Doc, HelmRepository } from './schema.ts';
|
|
import { Doc as Document } from './schema.ts';
|
|
import {
|
|
kustomizationsKeysUsed,
|
|
localChartHasKustomizationsYaml,
|
|
} from './utils.ts';
|
|
|
|
function isValidChartName(name: string | undefined, oci: boolean): boolean {
|
|
if (oci) {
|
|
return !!name && !regEx(/[!@#$%^&*(),.?":{}|<>A-Z]/).test(name);
|
|
}
|
|
return !!name && !regEx(/[!@#$%^&*(),.?":{}/|<>A-Z]/).test(name);
|
|
}
|
|
|
|
function isLocalPath(possiblePath: string): boolean {
|
|
return ['./', '../', '/'].some((localPrefix) =>
|
|
possiblePath.startsWith(localPrefix),
|
|
);
|
|
}
|
|
|
|
export async function extractPackageFile(
|
|
content: string,
|
|
packageFile: string,
|
|
config: ExtractConfig,
|
|
): Promise<PackageFileContent | null> {
|
|
const deps: PackageDependency[] = [];
|
|
let registryData: Record<string, HelmRepository> = {};
|
|
// Record kustomization usage for all deps, since updating artifacts is run on the helmfile.yaml as a whole.
|
|
let needKustomize = false;
|
|
const docs: Doc[] = parseYaml(content, {
|
|
customSchema: Document,
|
|
failureBehaviour: 'filter',
|
|
removeTemplates: true,
|
|
});
|
|
|
|
for (const doc of docs) {
|
|
// Always check for repositories in the current document and override the existing ones if any (as YAML does)
|
|
if (doc.repositories) {
|
|
registryData = {};
|
|
for (const repo of doc.repositories) {
|
|
if (repo.url?.startsWith('git+')) {
|
|
logger.debug(
|
|
{ repo, packageFile },
|
|
`Skipping unsupported helm-git repository.`,
|
|
);
|
|
continue;
|
|
}
|
|
registryData[repo.name] = repo;
|
|
}
|
|
logger.debug(
|
|
{ registryAliases: registryData, packageFile },
|
|
`repositories discovered.`,
|
|
);
|
|
}
|
|
|
|
for (const dep of [
|
|
...coerceArray(doc.releases),
|
|
...Object.values(coerceObject(doc.templates)),
|
|
]) {
|
|
let depName = dep.chart;
|
|
let ociDep: PackageDependency | null = null;
|
|
let repoName: string | null = null;
|
|
|
|
// If it starts with ./ ../ or / then it's a local path
|
|
if (isLocalPath(dep.chart)) {
|
|
if (
|
|
kustomizationsKeysUsed(dep) ||
|
|
(await localChartHasKustomizationsYaml(dep, packageFile))
|
|
) {
|
|
needKustomize = true;
|
|
}
|
|
deps.push({
|
|
depName: dep.name,
|
|
skipReason: 'local-chart',
|
|
});
|
|
continue;
|
|
}
|
|
|
|
if (isOCIRegistry(dep.chart)) {
|
|
depName = removeOCIPrefix(dep.chart);
|
|
ociDep = getOciChartDep(dep.chart, undefined, config.registryAliases);
|
|
} else {
|
|
if (dep.chart.includes('/')) {
|
|
const v = dep.chart.split('/');
|
|
repoName = v.shift()!;
|
|
depName = v.join('/');
|
|
} else {
|
|
repoName = dep.chart;
|
|
}
|
|
const registry = registryData[repoName];
|
|
if (registry?.oci) {
|
|
ociDep = getOciChartDep(
|
|
registry.url,
|
|
depName,
|
|
config.registryAliases,
|
|
);
|
|
repoName = null;
|
|
}
|
|
}
|
|
|
|
if (!isString(dep.version)) {
|
|
deps.push({
|
|
depName,
|
|
skipReason: 'invalid-version',
|
|
});
|
|
continue;
|
|
}
|
|
|
|
const res: PackageDependency = {
|
|
depName,
|
|
currentValue: dep.version,
|
|
};
|
|
if (kustomizationsKeysUsed(dep)) {
|
|
needKustomize = true;
|
|
}
|
|
if (ociDep) {
|
|
Object.assign(res, ociDep);
|
|
} else if (repoName) {
|
|
res.registryUrls = [registryData[repoName]?.url]
|
|
.concat([config.registryAliases?.[repoName]] as string[])
|
|
.filter(isString);
|
|
}
|
|
|
|
// By definition on helm the chart name should be lowercase letter + number + -
|
|
// However helmfile support templating of that field
|
|
if (
|
|
!isValidChartName(
|
|
isOCIRegistry(dep.chart)
|
|
? depName.slice(depName.lastIndexOf('/') + 1)
|
|
: depName,
|
|
!!ociDep,
|
|
)
|
|
) {
|
|
res.skipReason = 'unsupported-chart-type';
|
|
}
|
|
|
|
// Skip in case we cannot locate the registry
|
|
if (
|
|
res.datasource !== DockerDatasource.id &&
|
|
isEmptyArray(res.registryUrls)
|
|
) {
|
|
res.skipReason = 'unknown-registry';
|
|
}
|
|
|
|
deps.push(res);
|
|
}
|
|
}
|
|
|
|
return deps.length
|
|
? {
|
|
deps,
|
|
datasource: HelmDatasource.id,
|
|
...(needKustomize && { managerData: { needKustomize } }),
|
|
}
|
|
: null;
|
|
}
|