Files
renovate/lib/modules/datasource/docker/schema.ts
T
renovate[bot] 4a2578d18d build(deps): update dependency zod to v4 (main) (#41225)
* build(deps): update dependency zod to v4

| datasource | package | from    | to    |
| ---------- | ------- | ------- | ----- |
| npm        | zod     | 3.25.76 | 4.3.6 |

* fix: zod migration

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
2026-02-18 07:37:50 +00:00

182 lines
5.0 KiB
TypeScript

import { z } from 'zod/v3';
import { logger } from '../../../logger/index.ts';
import { Json, LooseArray } from '../../../util/schema-utils/index.ts';
// OCI manifests
/**
* OCI manifest object
*/
export const ManifestObject = z.object({
schemaVersion: z.literal(2),
mediaType: z.string().nullish(),
});
/**
* Oci descriptor
* https://github.com/opencontainers/image-spec/blob/main/descriptor.md
*/
export const Descriptor = z.object({
mediaType: z.string(),
digest: z.string(),
size: z.number().int().gte(0).nullish(),
});
/**
* OCI platform properties
* https://github.com/opencontainers/image-spec/blob/main/image-index.md
*/
const OciPlatform = z
.object({
architecture: z.string().nullish(),
})
.nullish();
/**
* OCI Image Configuration.
*
* Compatible with old docker configiguration.
* https://github.com/opencontainers/image-spec/blob/main/config.md
*/
export const OciImageConfig = z.object({
// This is required by the spec, but probably not present in the wild.
architecture: z.string().nullish(),
config: z.object({ Labels: z.record(z.string()).nullish() }).nullish(),
});
export type OciImageConfig = z.infer<typeof OciImageConfig>;
/**
* OCI Helm Configuration
* https://helm.sh/docs/topics/charts/#the-chartyaml-file
*/
export const OciHelmConfig = z.object({
name: z.string(),
version: z.string(),
home: z.string().nullish(),
sources: z.array(z.string()).nullish(),
});
export type OciHelmConfig = z.infer<typeof OciHelmConfig>;
/**
* OCI Image Manifest
* The same structure as docker image manifest, but mediaType is not required and is not present in the wild.
* https://github.com/opencontainers/image-spec/blob/main/manifest.md
*/
export const OciImageManifest = ManifestObject.extend({
mediaType: z.literal('application/vnd.oci.image.manifest.v1+json'),
config: Descriptor.extend({
mediaType: z.enum([
'application/vnd.oci.image.config.v1+json',
'application/vnd.cncf.helm.config.v1+json',
'application/vnd.devcontainers',
'application/vnd.oci.empty.v1+json',
'application/vnd.cncf.flux.config.v1+json',
]),
}),
annotations: z.record(z.string()).nullish(),
});
export type OciImageManifest = z.infer<typeof OciImageManifest>;
/**
* OCI Image List
* mediaType is not required.
* https://github.com/opencontainers/image-spec/blob/main/image-index.md
*/
export const OciImageIndexManifest = ManifestObject.extend({
mediaType: z.literal('application/vnd.oci.image.index.v1+json'),
manifests: LooseArray(
Descriptor.extend({
mediaType: z.enum([
'application/vnd.oci.image.manifest.v1+json',
'application/vnd.oci.image.index.v1+json',
]),
platform: OciPlatform,
}),
),
annotations: z.record(z.string()).nullish(),
});
// Old Docker manifests
/**
* Image Manifest
* https://docs.docker.com/registry/spec/manifest-v2-2/#image-manifest
*/
export const DistributionManifest = ManifestObject.extend({
mediaType: z.literal('application/vnd.docker.distribution.manifest.v2+json'),
config: Descriptor.extend({
mediaType: z.literal('application/vnd.docker.container.image.v1+json'),
}),
});
export type DistributionManifest = z.infer<typeof DistributionManifest>;
/**
* Manifest List
* https://docs.docker.com/registry/spec/manifest-v2-2/#manifest-list
*/
export const DistributionListManifest = ManifestObject.extend({
mediaType: z.literal(
'application/vnd.docker.distribution.manifest.list.v2+json',
),
manifests: z.array(
Descriptor.extend({
mediaType: z.literal(
'application/vnd.docker.distribution.manifest.v2+json',
),
platform: OciPlatform,
}),
),
});
// Combined manifests
export const Manifest = ManifestObject.passthrough()
.transform((value, ctx) => {
if (value.mediaType === undefined) {
if ('config' in value) {
value.mediaType = 'application/vnd.oci.image.manifest.v1+json';
} else if ('manifests' in value) {
value.mediaType = 'application/vnd.oci.image.index.v1+json';
} else {
ctx.addIssue({
code: 'custom',
message: 'Invalid manifest, missing mediaType.',
});
return z.NEVER;
}
}
return value;
})
.pipe(
z.discriminatedUnion('mediaType', [
DistributionManifest,
DistributionListManifest,
OciImageManifest,
OciImageIndexManifest,
]),
);
export type Manifest = z.infer<typeof Manifest>;
export const ManifestJson = Json.pipe(Manifest);
export const DockerHubTag = z.object({
id: z.number(),
last_updated: z.string().datetime(),
name: z.string(),
tag_last_pushed: z.string().datetime().nullable().catch(null),
digest: z.string().nullable().catch(null),
});
export type DockerHubTag = z.infer<typeof DockerHubTag>;
export const DockerHubTagsPage = z.object({
count: z.number(),
next: z.string().nullable().catch(null),
results: LooseArray(DockerHubTag, {
/* v8 ignore next -- TODO: add test */
onError: ({ error }) => {
logger.debug(
{ error },
'Docker: Failed to parse some tags from Docker Hub',
);
},
}),
});