mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-31 09:41:46 +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.
144 lines
3.9 KiB
TypeScript
144 lines
3.9 KiB
TypeScript
import { isNonEmptyObject, isTruthy } from '@sindresorhus/is';
|
|
import { logger } from '../../../logger/index.ts';
|
|
import { coerceArray } from '../../../util/array.ts';
|
|
import { regEx } from '../../../util/regex.ts';
|
|
import { withDebugMessage } from '../../../util/schema-utils/index.ts';
|
|
import { trimTrailingSlash } from '../../../util/url.ts';
|
|
import { GitTagsDatasource } from '../../datasource/git-tags/index.ts';
|
|
import { HelmDatasource } from '../../datasource/helm/index.ts';
|
|
import { getDep } from '../dockerfile/extract.ts';
|
|
import {
|
|
getOciChartDep,
|
|
isOCIRegistry,
|
|
removeOCIPrefix,
|
|
} from '../helmv3/oci.ts';
|
|
import type {
|
|
ExtractConfig,
|
|
PackageDependency,
|
|
PackageFileContent,
|
|
} from '../types.ts';
|
|
import {
|
|
type ApplicationDefinition,
|
|
ApplicationDefinitions,
|
|
type ApplicationSource,
|
|
type ApplicationSpec,
|
|
} from './schema.ts';
|
|
import { fileTestRegex } from './util.ts';
|
|
|
|
const kustomizeImageRe = regEx(/=(?<image>.+)$/);
|
|
|
|
export function extractPackageFile(
|
|
content: string,
|
|
packageFile: string,
|
|
config?: ExtractConfig,
|
|
): PackageFileContent | null {
|
|
// check for argo reference. API version for the kind attribute is used
|
|
if (fileTestRegex.test(content) === false) {
|
|
logger.debug(
|
|
`Skip file ${packageFile} as no argoproj.io apiVersion could be found in matched file`,
|
|
);
|
|
return null;
|
|
}
|
|
|
|
const definitions = ApplicationDefinitions.catch(
|
|
withDebugMessage([], `${packageFile} does not match schema`),
|
|
).parse(content);
|
|
|
|
const deps = definitions.flatMap((definition) =>
|
|
processAppSpec(definition, config?.registryAliases),
|
|
);
|
|
|
|
return deps.length ? { deps } : null;
|
|
}
|
|
|
|
function processSource(
|
|
source: ApplicationSource,
|
|
registryAliases: Record<string, string> | undefined,
|
|
): PackageDependency[] {
|
|
// a chart variable is defined this is helm declaration
|
|
if (source.chart) {
|
|
// assume OCI helm chart if repoURL doesn't contain explicit protocol
|
|
if (isOCIRegistry(source.repoURL) || !source.repoURL.includes('://')) {
|
|
const registryURL = trimTrailingSlash(removeOCIPrefix(source.repoURL));
|
|
|
|
return [
|
|
{
|
|
...getOciChartDep(source.repoURL, source.chart, registryAliases),
|
|
depName: `${registryURL}/${source.chart}`,
|
|
currentValue: source.targetRevision,
|
|
},
|
|
];
|
|
}
|
|
|
|
return [
|
|
{
|
|
depName: source.chart,
|
|
registryUrls: [source.repoURL],
|
|
currentValue: source.targetRevision,
|
|
datasource: HelmDatasource.id,
|
|
},
|
|
];
|
|
}
|
|
|
|
// Handle OCI Helm chart without explicit chart field
|
|
if (isOCIRegistry(source.repoURL)) {
|
|
return [
|
|
{
|
|
...getOciChartDep(source.repoURL, undefined, registryAliases),
|
|
depName: trimTrailingSlash(removeOCIPrefix(source.repoURL)),
|
|
currentValue: source.targetRevision,
|
|
},
|
|
];
|
|
}
|
|
|
|
const dependencies: PackageDependency[] = [
|
|
{
|
|
depName: source.repoURL,
|
|
currentValue: source.targetRevision,
|
|
datasource: GitTagsDatasource.id,
|
|
},
|
|
];
|
|
|
|
// Git repo is pointing to a Kustomize resources
|
|
if (source.kustomize?.images) {
|
|
dependencies.push(
|
|
...source.kustomize.images.map(processKustomizeImage).filter(isTruthy),
|
|
);
|
|
}
|
|
|
|
return dependencies;
|
|
}
|
|
|
|
function processAppSpec(
|
|
definition: ApplicationDefinition,
|
|
registryAliases: Record<string, string> | undefined,
|
|
): PackageDependency[] {
|
|
const spec: ApplicationSpec =
|
|
definition.kind === 'Application'
|
|
? definition.spec
|
|
: definition.spec.template.spec;
|
|
|
|
const deps: PackageDependency[] = [];
|
|
|
|
if (isNonEmptyObject(spec.source)) {
|
|
deps.push(...processSource(spec.source, registryAliases));
|
|
}
|
|
|
|
for (const source of coerceArray(spec.sources)) {
|
|
deps.push(...processSource(source, registryAliases));
|
|
}
|
|
|
|
return deps;
|
|
}
|
|
|
|
function processKustomizeImage(
|
|
kustomizeImage: string,
|
|
): PackageDependency | null {
|
|
const parts = kustomizeImageRe.exec(kustomizeImage);
|
|
if (!parts?.groups?.image) {
|
|
return null;
|
|
}
|
|
|
|
return getDep(parts.groups.image);
|
|
}
|