@norskvideo/ctl-product-template-schema 0.1.30 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -1
- package/product-channels.d.ts +65 -0
- package/product-channels.js +75 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@norskvideo/ctl-product-template-schema",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"./migration-socket": {
|
|
31
31
|
"types": "./migration-socket.d.ts",
|
|
32
32
|
"default": "./migration-socket.js"
|
|
33
|
+
},
|
|
34
|
+
"./product-channels": {
|
|
35
|
+
"types": "./product-channels.d.ts",
|
|
36
|
+
"default": "./product-channels.js"
|
|
33
37
|
}
|
|
34
38
|
},
|
|
35
39
|
"main": "./index.js",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const PRODUCT_CHANNELS_SCHEMA_VERSION = 1;
|
|
3
|
+
/** Public read URL of a product's channels object. */
|
|
4
|
+
export declare function productChannelsUrl(product: string): string;
|
|
5
|
+
/** Write target of a product's channels object. */
|
|
6
|
+
export declare function productChannelsS3Uri(product: string): string;
|
|
7
|
+
export declare const ProductChannelSchema: z.ZodEnum<{
|
|
8
|
+
nightly: "nightly";
|
|
9
|
+
rc: "rc";
|
|
10
|
+
latest: "latest";
|
|
11
|
+
}>;
|
|
12
|
+
export type ProductChannel = z.infer<typeof ProductChannelSchema>;
|
|
13
|
+
export declare const ProductChannelRowSchema: z.ZodObject<{
|
|
14
|
+
image: z.ZodString;
|
|
15
|
+
sha: z.ZodString;
|
|
16
|
+
publishedAt: z.ZodString;
|
|
17
|
+
runtimeImages: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
18
|
+
gate: z.ZodObject<{
|
|
19
|
+
checks: z.ZodLiteral<"passed">;
|
|
20
|
+
guides: z.ZodUnion<readonly [z.ZodLiteral<"passed">, z.ZodObject<{
|
|
21
|
+
skipped: z.ZodString;
|
|
22
|
+
}, z.core.$loose>]>;
|
|
23
|
+
run: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, z.core.$loose>;
|
|
25
|
+
}, z.core.$loose>;
|
|
26
|
+
export type ProductChannelRow = z.infer<typeof ProductChannelRowSchema>;
|
|
27
|
+
export declare const ProductChannelsSchema: z.ZodObject<{
|
|
28
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
29
|
+
product: z.ZodString;
|
|
30
|
+
imageRef: z.ZodString;
|
|
31
|
+
updated: z.ZodString;
|
|
32
|
+
channels: z.ZodRecord<z.ZodEnum<{
|
|
33
|
+
nightly: "nightly";
|
|
34
|
+
rc: "rc";
|
|
35
|
+
latest: "latest";
|
|
36
|
+
}> & z.core.$partial, z.ZodObject<{
|
|
37
|
+
image: z.ZodString;
|
|
38
|
+
sha: z.ZodString;
|
|
39
|
+
publishedAt: z.ZodString;
|
|
40
|
+
runtimeImages: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
41
|
+
gate: z.ZodObject<{
|
|
42
|
+
checks: z.ZodLiteral<"passed">;
|
|
43
|
+
guides: z.ZodUnion<readonly [z.ZodLiteral<"passed">, z.ZodObject<{
|
|
44
|
+
skipped: z.ZodString;
|
|
45
|
+
}, z.core.$loose>]>;
|
|
46
|
+
run: z.ZodOptional<z.ZodString>;
|
|
47
|
+
}, z.core.$loose>;
|
|
48
|
+
}, z.core.$loose>>;
|
|
49
|
+
}, z.core.$loose>;
|
|
50
|
+
export type ProductChannels = z.infer<typeof ProductChannelsSchema>;
|
|
51
|
+
export declare function parseProductChannels(raw: unknown): {
|
|
52
|
+
ok: true;
|
|
53
|
+
value: ProductChannels;
|
|
54
|
+
} | {
|
|
55
|
+
ok: false;
|
|
56
|
+
error: string;
|
|
57
|
+
};
|
|
58
|
+
/** `current` with `channel` set to `row`; every other channel is kept. */
|
|
59
|
+
export declare function withChannelRow(current: ProductChannels | null, opts: {
|
|
60
|
+
product: string;
|
|
61
|
+
imageRef: string;
|
|
62
|
+
channel: ProductChannel;
|
|
63
|
+
row: ProductChannelRow;
|
|
64
|
+
updated: string;
|
|
65
|
+
}): ProductChannels;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Which product image each release channel offers, written by the product's
|
|
2
|
+
// gated publish and read by ctl when it registers the product.
|
|
3
|
+
//
|
|
4
|
+
// One object per product (`products/<name>/channels.json` on the norsk.video
|
|
5
|
+
// bucket), not rows in the shared channel-manifest.json: each product repo's
|
|
6
|
+
// role can then write only its own prefix, and six writers never race a
|
|
7
|
+
// read-modify-write on one object. Only a publish that passed the gate writes a
|
|
8
|
+
// row, so a row means "this image passed checks and the guides" -- the thing a
|
|
9
|
+
// hand-pinned deployment/products.json could never say, and went six weeks
|
|
10
|
+
// stale instead (task 57).
|
|
11
|
+
//
|
|
12
|
+
// Readers ignore fields they do not know: an older ctl reads objects written by
|
|
13
|
+
// newer dev-kits.
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
export const PRODUCT_CHANNELS_SCHEMA_VERSION = 1;
|
|
16
|
+
const BUCKET = "norsk.video";
|
|
17
|
+
const BUCKET_URL = `https://s3.eu-west-1.amazonaws.com/${BUCKET}`;
|
|
18
|
+
/** A product's licence name, e.g. `norsk-probe`. It names an S3 prefix, so it
|
|
19
|
+
* is held to a shape that cannot leave that prefix. */
|
|
20
|
+
const PRODUCT_NAME = /^[a-z0-9][a-z0-9-]*$/;
|
|
21
|
+
function productChannelsKey(product) {
|
|
22
|
+
if (!PRODUCT_NAME.test(product))
|
|
23
|
+
throw new Error(`not a product name: ${JSON.stringify(product)}`);
|
|
24
|
+
return `products/${product}/channels.json`;
|
|
25
|
+
}
|
|
26
|
+
/** Public read URL of a product's channels object. */
|
|
27
|
+
export function productChannelsUrl(product) {
|
|
28
|
+
return `${BUCKET_URL}/${productChannelsKey(product)}`;
|
|
29
|
+
}
|
|
30
|
+
/** Write target of a product's channels object. */
|
|
31
|
+
export function productChannelsS3Uri(product) {
|
|
32
|
+
return `s3://${BUCKET}/${productChannelsKey(product)}`;
|
|
33
|
+
}
|
|
34
|
+
export const ProductChannelSchema = z.enum(["nightly", "rc", "latest"]);
|
|
35
|
+
export const ProductChannelRowSchema = z.looseObject({
|
|
36
|
+
/** The immutable dated tag, `<repo>:<YYYY-MM-DD>-<sha8>` -- never a channel tag. */
|
|
37
|
+
image: z.string().min(1),
|
|
38
|
+
/** The product commit the image was built from. */
|
|
39
|
+
sha: z.string().min(7),
|
|
40
|
+
publishedAt: z.string().min(1),
|
|
41
|
+
/** The Norsk and Studio images the product's templates launch (its
|
|
42
|
+
* `norsk-ctl.runtime-images` label). */
|
|
43
|
+
runtimeImages: z.array(z.string()).default([]),
|
|
44
|
+
gate: z.looseObject({
|
|
45
|
+
checks: z.literal("passed"),
|
|
46
|
+
/** `passed`, or skipped by the product's explicit opt-out, with its reason. */
|
|
47
|
+
guides: z.union([z.literal("passed"), z.looseObject({ skipped: z.string().min(1) })]),
|
|
48
|
+
/** The run that gated and published this image. */
|
|
49
|
+
run: z.string().optional(),
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
export const ProductChannelsSchema = z.looseObject({
|
|
53
|
+
schemaVersion: z.literal(PRODUCT_CHANNELS_SCHEMA_VERSION),
|
|
54
|
+
product: z.string().regex(PRODUCT_NAME),
|
|
55
|
+
imageRef: z.string().min(1),
|
|
56
|
+
updated: z.string().min(1),
|
|
57
|
+
channels: z.partialRecord(ProductChannelSchema, ProductChannelRowSchema),
|
|
58
|
+
});
|
|
59
|
+
export function parseProductChannels(raw) {
|
|
60
|
+
const r = ProductChannelsSchema.safeParse(raw);
|
|
61
|
+
return r.success ? { ok: true, value: r.data } : { ok: false, error: z.prettifyError(r.error) };
|
|
62
|
+
}
|
|
63
|
+
/** `current` with `channel` set to `row`; every other channel is kept. */
|
|
64
|
+
export function withChannelRow(current, opts) {
|
|
65
|
+
if (current && (current.product !== opts.product || current.imageRef !== opts.imageRef)) {
|
|
66
|
+
throw new Error(`channels object belongs to ${current.product} (${current.imageRef}), not ${opts.product} (${opts.imageRef})`);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
schemaVersion: PRODUCT_CHANNELS_SCHEMA_VERSION,
|
|
70
|
+
product: opts.product,
|
|
71
|
+
imageRef: opts.imageRef,
|
|
72
|
+
updated: opts.updated,
|
|
73
|
+
channels: { ...(current?.channels ?? {}), [opts.channel]: opts.row },
|
|
74
|
+
};
|
|
75
|
+
}
|