Files
Sebastian Poxhofer 2169a5786f fix(manager): share one local Helm chart path check (#46181)
helmfile, flux, fleet and terraform each decided differently whether a Helm chart reference points at a local path. fleet and terraform used terraform's checkIfStringIsPath, whose regex was unanchored with unescaped dots, so it matched any string containing a slash followed by a non-slash character.

Add isLocalChartPath to helmv3/utils.ts with helmfile's prefix check and use it in all four managers. This drops the fleet to terraform dependency and removes checkIfStringIsPath, which had no other users.
2026-09-22 11:59:41 +00:00

182 lines
4.1 KiB
TypeScript

import { isUndefined } from '@sindresorhus/is';
import { regEx } from '../../../util/regex.ts';
import { parseYaml } from '../../../util/yaml.ts';
import { GitTagsDatasource } from '../../datasource/git-tags/index.ts';
import { HelmDatasource } from '../../datasource/helm/index.ts';
import {
getOciChartDep,
isOCIRegistry,
removeOCIPrefix,
} from '../helmv3/oci.ts';
import { isLocalChartPath } from '../helmv3/utils.ts';
import type {
ExtractConfig,
PackageDependency,
PackageFileContent,
} from '../types.ts';
import { FleetFile, type FleetHelmBlock, GitRepo } from './schema.ts';
function extractGitRepo(doc: GitRepo): PackageDependency {
const dep: PackageDependency = {
depType: 'git_repo',
datasource: GitTagsDatasource.id,
};
const repo = doc.spec?.repo;
if (!repo) {
return {
...dep,
skipReason: 'missing-depname',
};
}
dep.sourceUrl = repo;
dep.depName = repo;
const currentValue = doc.spec.revision;
if (!currentValue) {
return {
...dep,
skipReason: 'unspecified-version',
};
}
return {
...dep,
currentValue,
};
}
function extractFleetHelmBlock(
doc: FleetHelmBlock,
config: ExtractConfig,
): PackageDependency {
const dep: PackageDependency = {
depType: 'fleet',
datasource: HelmDatasource.id,
};
if (!doc.chart) {
return {
...dep,
skipReason: 'missing-depname',
};
}
if (isOCIRegistry(doc.chart)) {
const ociDep: PackageDependency = {
...dep,
...getOciChartDep(doc.chart, undefined, config.registryAliases),
depName: removeOCIPrefix(doc.chart),
};
if (!doc.version) {
return {
...ociDep,
skipReason: 'unspecified-version',
};
}
return {
...ociDep,
currentValue: doc.version,
};
}
dep.depName = doc.chart;
dep.packageName = doc.chart;
if (!doc.repo) {
if (isLocalChartPath(doc.chart)) {
return {
...dep,
skipReason: 'local-chart',
};
}
return {
...dep,
skipReason: 'no-repository',
};
}
const alias = config.registryAliases?.[doc.repo];
if (alias) {
dep.registryUrls = [alias];
} else {
dep.registryUrls = [doc.repo];
}
const currentValue = doc.version;
if (!doc.version) {
return {
...dep,
skipReason: 'unspecified-version',
};
}
return {
...dep,
currentValue,
};
}
function extractFleetFile(
doc: FleetFile,
config: ExtractConfig,
): PackageDependency[] {
const result: PackageDependency[] = [];
result.push(extractFleetHelmBlock(doc.helm, config));
if (!isUndefined(doc.targetCustomizations)) {
// remove version from helm block to allow usage of variables defined in the global block, but do not create PRs
// if there is no version defined in the customization.
const helmBlockContext: FleetHelmBlock = { ...doc.helm };
delete helmBlockContext.version;
for (const [index, custom] of doc.targetCustomizations.entries()) {
const dep = extractFleetHelmBlock(
{
// merge base config with customization
...helmBlockContext,
...custom.helm,
},
config,
);
result.push({
// overwrite name with customization name to allow splitting of PRs
...dep,
depName: custom.name ?? `targetCustomization[${index}]`, // if no name is provided, use the index
});
}
}
return result;
}
export function extractPackageFile(
content: string,
packageFile: string,
config: ExtractConfig,
): PackageFileContent | null {
if (!content) {
return null;
}
const deps: PackageDependency[] = [];
if (regEx('fleet.ya?ml').test(packageFile)) {
const docs = parseYaml(content, {
customSchema: FleetFile,
failureBehaviour: 'filter',
});
const fleetDeps = docs.flatMap((doc) => extractFleetFile(doc, config));
deps.push(...fleetDeps);
} else {
const docs = parseYaml(content, {
customSchema: GitRepo,
failureBehaviour: 'filter',
});
const gitRepoDeps = docs.flatMap(extractGitRepo);
deps.push(...gitRepoDeps);
}
return deps.length ? { deps } : null;
}