Skip to content

Commit

Permalink
feat(helm-values): Add support for bumpVersion
Browse files Browse the repository at this point in the history
  • Loading branch information
kvanzuijlen committed Dec 27, 2023
1 parent 3a8574b commit 4692efe
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 19 deletions.
18 changes: 15 additions & 3 deletions lib/modules/manager/helm-values/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getDep } from '../dockerfile/extract';
import type { PackageDependency, PackageFileContent } from '../types';
import type { HelmDockerImageDependency } from './types';
import {
getParsedSiblingChartYaml,
matchesHelmValuesDockerHeuristic,
matchesHelmValuesInlineImage,
} from './util';
Expand Down Expand Up @@ -57,10 +58,10 @@ function findDependencies(
return packageDependencies;
}

export function extractPackageFile(
export async function extractPackageFile(
content: string,
packageFile?: string,
): PackageFileContent | null {
packageFile: string,
): Promise<PackageFileContent | null> {
let parsedContent: Record<string, unknown>[] | HelmDockerImageDependency[];
try {
// a parser that allows extracting line numbers would be preferable, with
Expand All @@ -79,6 +80,17 @@ export function extractPackageFile(
}

if (deps.length) {
// in Helm, the current package version is the version of the chart.
// This fetches this version by reading it from the Chart.yaml
// found in the same folder as the currently processed values file.
const siblingChart = await getParsedSiblingChartYaml(packageFile);
const packageFileVersion = siblingChart?.version;
if (packageFileVersion) {
return {
deps,
packageFileVersion,
};
}
return { deps };
}
} catch (err) /* istanbul ignore next */ {
Expand Down
1 change: 1 addition & 0 deletions lib/modules/manager/helm-values/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Category } from '../../../constants';
import { DockerDatasource } from '../../datasource/docker';
export { extractPackageFile } from './extract';
export { bumpPackageVersion } from './update';

export const defaultConfig = {
commitMessageTopic: 'helm values {{depName}}',
Expand Down
56 changes: 56 additions & 0 deletions lib/modules/manager/helm-values/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ReleaseType, inc } from 'semver';
import { logger } from '../../../logger';
import { getSiblingFileName } from '../../../util/fs';
import type { BumpPackageVersionResult } from '../types';
import { getSiblingChartYamlContent } from './util';

export async function bumpPackageVersion(
content: string,
currentValue: string,
bumpVersion: ReleaseType,
packageFile: string,
): Promise<BumpPackageVersionResult> {
logger.debug(
{ bumpVersion, currentValue },
'Checking if we should bump Chart.yaml version',
);
const chartFileName = getSiblingFileName(packageFile, 'Chart.yaml');
const chartYamlContent = await getSiblingChartYamlContent(packageFile);
try {
const newChartVersion = inc(currentValue, bumpVersion);
if (!newChartVersion) {
throw new Error('semver inc failed');
}
if (chartYamlContent === null) {
throw new Error(
'Cannot bump chart version because Chart.yaml could not be read.',
);
}
logger.debug({ newChartVersion });
const bumpedContent = chartYamlContent?.replace(
/^(version:\s*).*$/m,
`$1${newChartVersion}`,
);
if (bumpedContent === chartYamlContent) {
logger.debug('Version was already bumped');
} else {
logger.debug('Bumped Chart.yaml version');
}
return {
bumpedContent: content,
bumpedFiles: [{ fileName: chartFileName, newContent: bumpedContent }],
};
} catch (err) {
logger.warn(
{
chartYamlContent,
currentValue,
bumpVersion,
},
'Failed to bumpVersion',
);
return {
bumpedContent: content,
};
}
}
51 changes: 51 additions & 0 deletions lib/modules/manager/helm-values/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import yaml from 'js-yaml';
import { logger } from '../../../logger';
import { getSiblingFileName, readLocalFile } from '../../../util/fs';
import { hasKey } from '../../../util/object';
import { regEx } from '../../../util/regex';
import type { HelmDockerImageDependency } from './types';
Expand Down Expand Up @@ -41,3 +44,51 @@ export function matchesHelmValuesInlineImage(
): data is string {
return !!(parentKeyRe.test(parentKey) && data && typeof data === 'string');
}

/**
* This function looks for a Chart.yaml in the same directory as @param fileName and
* returns its raw contents.
*
* @param fileName
*/
export async function getSiblingChartYamlContent(
fileName: string,
): Promise<string | null> {
try {
const chartFileName = getSiblingFileName(fileName, 'Chart.yaml');
return await readLocalFile(chartFileName, 'utf8');
} catch (err) {
logger.debug({ fileName }, 'Failed to read helm Chart.yaml');
return null;
}
}

/**
* This function looks for a Chart.yaml in the same directory as @param fileName and
* if it looks like a valid Helm Chart.yaml, it is parsed and returned as an object.
*
* @param fileName
*/
export async function getParsedSiblingChartYaml(
fileName: string,
): Promise<any> {
try {
const chartContents = await getSiblingChartYamlContent(fileName);
if (!chartContents) {
logger.debug({ fileName }, 'Failed to find helm Chart.yaml');
return null;
}
const chart = yaml.load(chartContents, { json: true }) as any;
if (!(chart?.apiVersion && chart.name && chart.version)) {
logger.debug(
{ fileName },
'Failed to find required fields in Chart.yaml',
);
return null;
}
return chart;
} catch (err) {
logger.debug({ fileName }, 'Failed to parse helm Chart.yaml');
return null;
}
}
7 changes: 7 additions & 0 deletions lib/modules/manager/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ export interface UpdateDependencyConfig<T = Record<string, any>> {

export interface BumpPackageVersionResult {
bumpedContent: string | null;
// describes files that was changed instead of or in addition to the packageFile
bumpedFiles?: BumpedPackageFile[];
}
export interface BumpedPackageFile {
fileName: string;
newContent: string;
}

export interface UpdateLockedConfig {
Expand Down Expand Up @@ -235,6 +241,7 @@ export interface ManagerApi extends ModuleApi {
content: string,
currentValue: string,
bumpVersion: ReleaseType,
packageFile?: string,
): Result<BumpPackageVersionResult>;

detectGlobalConfig?(): Result<GlobalManagerConfig>;
Expand Down
60 changes: 44 additions & 16 deletions lib/workers/repository/update/branch/get-updated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { get } from '../../../../modules/manager';
import type {
ArtifactError,
PackageDependency,
BumpedPackageFile,

Check failure on line 9 in lib/workers/repository/update/branch/get-updated.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Member 'BumpedPackageFile' of the import declaration should be sorted alphabetically.
} from '../../../../modules/manager/types';
import { getFile } from '../../../../util/git';
import type { FileAddition, FileChange } from '../../../../util/git/types';
Expand All @@ -20,6 +21,21 @@ export interface PackageFilesResult {
updatedArtifacts: FileChange[];
}

async function getFileContent(
updatedFileContents: Record<string, string>,
filePath: string,
config: BranchConfig,
): Promise<string | null> {
let fileContent: string | null = updatedFileContents[filePath];
if (!fileContent) {
fileContent = await getFile(
filePath,
config.reuseExistingBranch ? config.branchName : config.baseBranch,
);
}
return fileContent;
}

export async function getUpdatedPackageFiles(
config: BranchConfig,
): Promise<PackageFilesResult> {
Expand All @@ -46,23 +62,19 @@ export async function getUpdatedPackageFiles(
packageFileUpdatedDeps[packageFile] =
packageFileUpdatedDeps[packageFile] || [];
packageFileUpdatedDeps[packageFile].push({ ...upgrade });
let packageFileContent: string | null = updatedFileContents[packageFile];
if (!packageFileContent) {
packageFileContent = await getFile(
packageFile,
reuseExistingBranch ? config.branchName : config.baseBranch,
);
}
const packageFileContent = await getFileContent(
updatedFileContents,
packageFile,
config,
);
let lockFileContent: string | null = null;
const lockFile = upgrade.lockFile ?? upgrade.lockFiles?.[0] ?? '';
if (lockFile) {
lockFileContent = updatedFileContents[lockFile];
if (!lockFileContent) {
lockFileContent = await getFile(
lockFile,
reuseExistingBranch ? config.branchName : config.baseBranch,
);
}
lockFileContent = await getFileContent(
updatedFileContents,
lockFile,
config,
);
}
// istanbul ignore if
if (
Expand Down Expand Up @@ -174,24 +186,39 @@ export async function getUpdatedPackageFiles(
);
firstUpdate = false;
if (res) {
let bumpedPackageFiles: BumpedPackageFile[] = [];
if (
bumpPackageVersion &&
upgrade.bumpVersion &&
upgrade.packageFileVersion
) {
const { bumpedContent } = await bumpPackageVersion(
const bumpResult = await bumpPackageVersion(
res,
upgrade.packageFileVersion,
upgrade.bumpVersion,
packageFile,
);
res = bumpedContent;
res = bumpResult.bumpedContent;
bumpedPackageFiles = bumpResult.bumpedFiles ?? [];
}
if (res === packageFileContent) {
logger.debug({ packageFile, depName }, 'No content changed');
} else {
logger.debug({ packageFile, depName }, 'Contents updated');
updatedFileContents[packageFile] = res!;
delete nonUpdatedFileContents[packageFile];
// indicates that the version was bumped in one or more files in
// addition to or instead of the packageFile
if (bumpedPackageFiles) {
for (const bumpedPackageFile of bumpedPackageFiles) {
logger.debug(
{ bumpedPackageFile, depName },
'Updating bumpedPackageFile content',
);
updatedFileContents[bumpedPackageFile.fileName] =
bumpedPackageFile.newContent;
}
}
}
continue;
} else if (reuseExistingBranch) {
Expand All @@ -217,6 +244,7 @@ export async function getUpdatedPackageFiles(
newContent,
upgrade.packageFileVersion,
upgrade.bumpVersion,
packageFile,
);
newContent = bumpedContent;
}
Expand Down

0 comments on commit 4692efe

Please sign in to comment.