mirror of https://github.com/renovatebot/renovate
179 lines
4.9 KiB
TypeScript
179 lines
4.9 KiB
TypeScript
import { z } from 'zod';
|
|
import { logger } from '../../../logger';
|
|
import { Json, LooseArray } from '../../../util/schema-utils';
|
|
|
|
// 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',
|
|
]),
|
|
}),
|
|
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: z.array(
|
|
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, {
|
|
onError: /* istanbul ignore next */ ({ error }) => {
|
|
logger.debug(
|
|
{ error },
|
|
'Docker: Failed to parse some tags from Docker Hub',
|
|
);
|
|
},
|
|
}),
|
|
});
|