Skip to content

Commit 14cba69

Browse files
authored
refactor(managers): yaml schema checks (#26811)
1 parent 4084856 commit 14cba69

File tree

14 files changed

+229
-280
lines changed

14 files changed

+229
-280
lines changed

lib/modules/manager/argocd/extract.ts

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import type {
1111
PackageDependency,
1212
PackageFileContent,
1313
} from '../types';
14-
import type {
14+
import {
1515
ApplicationDefinition,
16-
ApplicationSource,
17-
ApplicationSpec,
18-
} from './types';
16+
type ApplicationSource,
17+
type ApplicationSpec,
18+
} from './schema';
1919
import { fileTestRegex } from './util';
2020

2121
export function extractPackageFile(
@@ -33,27 +33,21 @@ export function extractPackageFile(
3333

3434
let definitions: ApplicationDefinition[];
3535
try {
36-
// TODO: use schema (#9610)
37-
definitions = parseYaml(content);
36+
definitions = parseYaml(content, null, {
37+
customSchema: ApplicationDefinition,
38+
failureBehaviour: 'filter',
39+
});
3840
} catch (err) {
3941
logger.debug({ err, packageFile }, 'Failed to parse ArgoCD definition.');
4042
return null;
4143
}
4244

43-
const deps = definitions.filter(is.plainObject).flatMap(processAppSpec);
45+
const deps = definitions.flatMap(processAppSpec);
4446

4547
return deps.length ? { deps } : null;
4648
}
4749

4850
function processSource(source: ApplicationSource): PackageDependency | null {
49-
if (
50-
!source ||
51-
!is.nonEmptyString(source.repoURL) ||
52-
!is.nonEmptyString(source.targetRevision)
53-
) {
54-
return null;
55-
}
56-
5751
// a chart variable is defined this is helm declaration
5852
if (source.chart) {
5953
// assume OCI helm chart if repoURL doesn't contain explicit protocol
@@ -89,14 +83,10 @@ function processSource(source: ApplicationSource): PackageDependency | null {
8983
function processAppSpec(
9084
definition: ApplicationDefinition,
9185
): PackageDependency[] {
92-
const spec: ApplicationSpec | null | undefined =
86+
const spec: ApplicationSpec =
9387
definition.kind === 'Application'
94-
? definition?.spec
95-
: definition?.spec?.template?.spec;
96-
97-
if (is.nullOrUndefined(spec)) {
98-
return [];
99-
}
88+
? definition.spec
89+
: definition.spec.template.spec;
10090

10191
const deps: (PackageDependency | null)[] = [];
10292

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { z } from 'zod';
2+
3+
export const KubernetesResource = z.object({
4+
apiVersion: z.string(),
5+
});
6+
7+
export const ApplicationSource = z.object({
8+
chart: z.string().optional(),
9+
repoURL: z.string(),
10+
targetRevision: z.string(),
11+
});
12+
export type ApplicationSource = z.infer<typeof ApplicationSource>;
13+
14+
export const ApplicationSpec = z.object({
15+
source: ApplicationSource.optional(),
16+
sources: z.array(ApplicationSource).optional(),
17+
});
18+
export type ApplicationSpec = z.infer<typeof ApplicationSpec>;
19+
20+
export const Application = KubernetesResource.extend({
21+
kind: z.literal('Application'),
22+
spec: ApplicationSpec,
23+
});
24+
25+
export const ApplicationSet = KubernetesResource.extend({
26+
kind: z.literal('ApplicationSet'),
27+
spec: z.object({
28+
template: z.object({
29+
spec: ApplicationSpec,
30+
}),
31+
}),
32+
});
33+
34+
export const ApplicationDefinition = Application.or(ApplicationSet);
35+
export type ApplicationDefinition = z.infer<typeof ApplicationDefinition>;

lib/modules/manager/argocd/types.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

lib/modules/manager/crossplane/extract.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
PackageDependency,
77
PackageFileContent,
88
} from '../types';
9-
import { XPKGSchema } from './schema';
9+
import { type XPKG, XPKGSchema } from './schema';
1010

1111
export function extractPackageFile(
1212
content: string,
@@ -19,9 +19,12 @@ export function extractPackageFile(
1919
return null;
2020
}
2121

22-
let list = [];
22+
let list: XPKG[] = [];
2323
try {
24-
list = parseYaml(content);
24+
list = parseYaml(content, null, {
25+
customSchema: XPKGSchema,
26+
failureBehaviour: 'filter',
27+
});
2528
} catch (err) {
2629
logger.debug(
2730
{ err, packageFile },
@@ -31,16 +34,7 @@ export function extractPackageFile(
3134
}
3235

3336
const deps: PackageDependency[] = [];
34-
for (const item of list) {
35-
const parsed = XPKGSchema.safeParse(item);
36-
if (!parsed.success) {
37-
logger.trace(
38-
{ item, errors: parsed.error },
39-
'Invalid Crossplane package',
40-
);
41-
continue;
42-
}
43-
const xpkg = parsed.data;
37+
for (const xpkg of list) {
4438
const dep = getDep(xpkg.spec.package, true, extractConfig?.registryAliases);
4539
dep.depType = xpkg.kind.toLowerCase();
4640
deps.push(dep);

lib/modules/manager/docker-compose/extract.ts

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { newlineRegex, regEx } from '../../../util/regex';
44
import { parseSingleYaml } from '../../../util/yaml';
55
import { getDep } from '../dockerfile/extract';
66
import type { ExtractConfig, PackageFileContent } from '../types';
7-
import type { DockerComposeConfig } from './types';
7+
import { DockerComposeFile } from './schema';
88

99
class LineMapper {
1010
private imageLines: { line: string; lineNumber: number; used: boolean }[];
@@ -34,24 +34,12 @@ export function extractPackageFile(
3434
extractConfig: ExtractConfig,
3535
): PackageFileContent | null {
3636
logger.debug(`docker-compose.extractPackageFile(${packageFile})`);
37-
let config: DockerComposeConfig;
37+
let config: DockerComposeFile;
3838
try {
39-
// TODO: use schema (#9610)
40-
config = parseSingleYaml(content, { json: true });
41-
if (!config) {
42-
logger.debug(
43-
{ packageFile },
44-
'Null config when parsing Docker Compose content',
45-
);
46-
return null;
47-
}
48-
if (typeof config !== 'object') {
49-
logger.debug(
50-
{ packageFile, type: typeof config },
51-
'Unexpected type for Docker Compose content',
52-
);
53-
return null;
54-
}
39+
config = parseSingleYaml(content, {
40+
json: true,
41+
customSchema: DockerComposeFile,
42+
});
5543
} catch (err) {
5644
logger.debug(
5745
{ err, packageFile },
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { z } from 'zod';
2+
3+
const DockerComposeService = z.object({
4+
image: z.string().optional(),
5+
build: z
6+
.object({
7+
context: z.string().optional(),
8+
dockerfile: z.string().optional(),
9+
})
10+
.optional(),
11+
});
12+
13+
const DockerComposeFileV1 = z.record(DockerComposeService);
14+
const DockerComposeFileModern = z.object({
15+
// compose does not use this strictly, so we shouldn't be either
16+
// https://docs.docker.com/compose/compose-file/04-version-and-name/#version-top-level-element
17+
version: z.string().optional(),
18+
services: z.record(DockerComposeService),
19+
});
20+
21+
export const DockerComposeFile =
22+
DockerComposeFileModern.or(DockerComposeFileV1);
23+
export type DockerComposeFile = z.infer<typeof DockerComposeFile>;

lib/modules/manager/docker-compose/types.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

lib/modules/manager/fleet/extract.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { GitTagsDatasource } from '../../datasource/git-tags';
66
import { HelmDatasource } from '../../datasource/helm';
77
import { checkIfStringIsPath } from '../terraform/util';
88
import type { PackageDependency, PackageFileContent } from '../types';
9-
import type { FleetFile, FleetHelmBlock, GitRepo } from './types';
9+
import { FleetFile, type FleetHelmBlock, GitRepo } from './schema';
1010

1111
function extractGitRepo(doc: GitRepo): PackageDependency {
1212
const dep: PackageDependency = {
@@ -119,23 +119,21 @@ export function extractPackageFile(
119119

120120
try {
121121
if (regEx('fleet.ya?ml').test(packageFile)) {
122-
// TODO: use schema (#9610)
123-
const docs = parseYaml<FleetFile>(content, null, {
122+
const docs = parseYaml(content, null, {
124123
json: true,
124+
customSchema: FleetFile,
125+
failureBehaviour: 'filter',
125126
});
126-
const fleetDeps = docs
127-
.filter((doc) => is.truthy(doc?.helm))
128-
.flatMap((doc) => extractFleetFile(doc));
127+
const fleetDeps = docs.flatMap(extractFleetFile);
129128

130129
deps.push(...fleetDeps);
131130
} else {
132-
// TODO: use schema (#9610)
133-
const docs = parseYaml<GitRepo>(content, null, {
131+
const docs = parseYaml(content, null, {
134132
json: true,
133+
customSchema: GitRepo,
134+
failureBehaviour: 'filter',
135135
});
136-
const gitRepoDeps = docs
137-
.filter((doc) => doc.kind === 'GitRepo') // ensure only GitRepo manifests are processed
138-
.flatMap((doc) => extractGitRepo(doc));
136+
const gitRepoDeps = docs.flatMap(extractGitRepo);
139137
deps.push(...gitRepoDeps);
140138
}
141139
} catch (err) {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { z } from 'zod';
2+
3+
const FleetHelmBlock = z.object({
4+
chart: z.string().optional(),
5+
repo: z.string().optional(),
6+
version: z.string().optional(),
7+
});
8+
export type FleetHelmBlock = z.infer<typeof FleetHelmBlock>;
9+
10+
const FleetFileHelm = FleetHelmBlock.extend({
11+
releaseName: z.string(),
12+
});
13+
14+
/**
15+
Represent a GitRepo Kubernetes manifest of Fleet.
16+
@link https://fleet.rancher.io/gitrepo-add/#create-gitrepo-instance
17+
*/
18+
export const GitRepo = z.object({
19+
metadata: z.object({
20+
name: z.string(),
21+
}),
22+
kind: z.string(),
23+
spec: z.object({
24+
repo: z.string().optional(),
25+
revision: z.string().optional(),
26+
}),
27+
});
28+
export type GitRepo = z.infer<typeof GitRepo>;
29+
30+
/**
31+
Represent a Bundle configuration of Fleet, which is located in `fleet.yaml` files.
32+
@link https://fleet.rancher.io/gitrepo-structure/#fleetyaml
33+
*/
34+
export const FleetFile = z.object({
35+
helm: FleetFileHelm,
36+
targetCustomizations: z
37+
.array(
38+
z.object({
39+
name: z.string(),
40+
helm: FleetHelmBlock.partial().optional(),
41+
}),
42+
)
43+
.optional(),
44+
});
45+
export type FleetFile = z.infer<typeof FleetFile>;

lib/modules/manager/fleet/types.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)