Skip to content

Commit

Permalink
feat(batect): Use Zod schema for extract code (renovatebot#33579)
Browse files Browse the repository at this point in the history
  • Loading branch information
zharinov committed Jan 13, 2025
1 parent d4a6be1 commit 5841aa2
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 145 deletions.
132 changes: 9 additions & 123 deletions lib/modules/manager/batect/extract.ts
Original file line number Diff line number Diff line change
@@ -1,121 +1,9 @@
import is from '@sindresorhus/is';
import upath from 'upath';
import { logger } from '../../../logger';
import { readLocalFile } from '../../../util/fs';
import { parseSingleYaml } from '../../../util/yaml';
import { GitTagsDatasource } from '../../datasource/git-tags';
import { id as dockerVersioning } from '../../versioning/docker';
import { id as semverVersioning } from '../../versioning/semver';
import { getDep } from '../dockerfile/extract';
import type { ExtractConfig, PackageDependency, PackageFile } from '../types';
import type {
BatectConfig,
BatectFileInclude,
BatectGitInclude,
BatectInclude,
ExtractionResult,
} from './types';

function loadConfig(content: string): BatectConfig {
const config = parseSingleYaml(content);

if (typeof config !== 'object') {
throw new Error(
`Configuration file does not contain a YAML object (it is ${typeof config}).`,
);
}

return config as BatectConfig;
}

function extractImages(config: BatectConfig): string[] {
if (config.containers === undefined) {
return [];
}

return Object.values(config.containers)
.map((container) => container.image)
.filter(is.string);
}

function createImageDependency(tag: string): PackageDependency {
return {
...getDep(tag),
versioning: dockerVersioning,
};
}

function extractImageDependencies(config: BatectConfig): PackageDependency[] {
const images = extractImages(config);
const deps = images.map((image) => createImageDependency(image));

logger.trace({ deps }, 'Loaded images from Batect configuration file');

return deps;
}

function includeIsGitInclude(
include: BatectInclude,
): include is BatectGitInclude {
return typeof include === 'object' && include.type === 'git';
}

function extractGitBundles(config: BatectConfig): BatectGitInclude[] {
if (config.include === undefined) {
return [];
}

return config.include.filter(includeIsGitInclude);
}

function createBundleDependency(bundle: BatectGitInclude): PackageDependency {
return {
depName: bundle.repo,
currentValue: bundle.ref,
versioning: semverVersioning,
datasource: GitTagsDatasource.id,
commitMessageTopic: 'bundle {{depName}}',
};
}

function extractBundleDependencies(config: BatectConfig): PackageDependency[] {
const bundles = extractGitBundles(config);
const deps = bundles.map((bundle) => createBundleDependency(bundle));

logger.trace({ deps }, 'Loaded bundles from Batect configuration file');

return deps;
}

function includeIsStringFileInclude(include: BatectInclude): include is string {
return typeof include === 'string';
}

function includeIsObjectFileInclude(
include: BatectInclude,
): include is BatectFileInclude {
return typeof include === 'object' && include.type === 'file';
}

function extractReferencedConfigFiles(
config: BatectConfig,
fileName: string,
): string[] {
if (config.include === undefined) {
return [];
}

const dirName = upath.dirname(fileName);

const paths = [
...config.include.filter(includeIsStringFileInclude),
...config.include
.filter(includeIsObjectFileInclude)
.map((include) => include.path),
].filter((p) => p !== undefined && p !== null);

return paths.map((p) => upath.join(dirName, p));
}
import type { ExtractConfig, PackageFile } from '../types';
import { BatectConfigSchema } from './schema';
import type { ExtractionResult } from './types';

export function extractPackageFile(
content: string,
Expand All @@ -124,15 +12,13 @@ export function extractPackageFile(
logger.trace(`batect.extractPackageFile(${packageFile})`);

try {
const config = loadConfig(content);
const deps = [
...extractImageDependencies(config),
...extractBundleDependencies(config),
];
const { imageDependencies, bundleDependencies, fileIncludes } =
BatectConfigSchema.parse(content);
const deps = [...imageDependencies, ...bundleDependencies];

const referencedConfigFiles = extractReferencedConfigFiles(
config,
packageFile,
const dirName = upath.dirname(packageFile);
const referencedConfigFiles = fileIncludes.map((file) =>
upath.join(dirName, file),
);

return { deps, referencedConfigFiles };
Expand Down
62 changes: 62 additions & 0 deletions lib/modules/manager/batect/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { z } from 'zod';
import { LooseArray, LooseRecord, Yaml } from '../../../util/schema-utils';
import { GitTagsDatasource } from '../../datasource/git-tags';
import { id as dockerVersioning } from '../../versioning/docker';
import { id as semverVersioning } from '../../versioning/semver';
import { getDep } from '../dockerfile/extract';
import type { PackageDependency } from '../types';

export const BatectConfigSchema = Yaml.pipe(
z.object({
containers: LooseRecord(
z.string(),
z.object({ image: z.string() }).transform(({ image }) => image),
)
.transform((x) => Object.values(x))
.catch([]),
include: LooseArray(
z.union([
z.object({
type: z.literal('git'),
repo: z.string(),
ref: z.string(),
}),
z.object({
type: z.literal('file'),
path: z.string(),
}),
z.string().transform((path) => ({ type: 'file' as const, path })),
]),
).catch([]),
}),
).transform(({ containers, include }) => {
const imageDependencies = containers.map((image) => ({
...getDep(image),
versioning: dockerVersioning,
}));

const bundleDependencies: PackageDependency[] = [];
const fileIncludes: string[] = [];

for (const item of include) {
if (item.type === 'git') {
bundleDependencies.push({
depName: item.repo,
currentValue: item.ref,
versioning: semverVersioning,
datasource: GitTagsDatasource.id,
commitMessageTopic: 'bundle {{depName}}',
});
} else {
fileIncludes.push(item.path);
}
}

return {
imageDependencies,
bundleDependencies,
fileIncludes,
};
});

export type BatectConfig = z.infer<typeof BatectConfigSchema>;
22 changes: 0 additions & 22 deletions lib/modules/manager/batect/types.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,5 @@
import type { PackageDependency } from '../types';

export interface BatectConfig {
containers?: Record<string, BatectContainer>;
include?: BatectInclude[];
}

export interface BatectContainer {
image?: string;
}

export type BatectInclude = string | BatectFileInclude | BatectGitInclude;

export interface BatectFileInclude {
type: 'file';
path: string;
}

export interface BatectGitInclude {
type: 'git';
repo: string;
ref: string;
}

export interface ExtractionResult {
deps: PackageDependency[];
referencedConfigFiles: string[];
Expand Down

0 comments on commit 5841aa2

Please sign in to comment.