@norskvideo/ctl-dev-kit 0.1.74 → 0.1.75
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/conventions/smoke.yml +18 -0
- package/licence/check-entitlement.ts +78 -0
- package/package.json +2 -1
package/conventions/smoke.yml
CHANGED
|
@@ -172,6 +172,24 @@ jobs:
|
|
|
172
172
|
# runner the first pull overruns the per-test timeout. Pre-pull them (tags
|
|
173
173
|
# from manifest.seed.json), plus whatever the repo lists in the optional
|
|
174
174
|
# scripts/smoke/prepull-extra.sh hook (a driver image, a sidecar).
|
|
175
|
+
# Before anything is built or launched: does this licence actually name
|
|
176
|
+
# this product? `product add` refuses one it does not, and that refusal
|
|
177
|
+
# reaches the log as a 60-second readiness timeout indistinguishable from
|
|
178
|
+
# a product that failed to start. Turnkeys make this routine — each ships
|
|
179
|
+
# under a licence naming its own product, so a newly created one whose
|
|
180
|
+
# licence was never minted should say so in seconds, by name.
|
|
181
|
+
#
|
|
182
|
+
# The product name is the repo's own PRODUCT_NAME, not the short board key
|
|
183
|
+
# above (`funke` vs `funke-pegasus`): the licence names the product.
|
|
184
|
+
- name: Check the licence entitles this product
|
|
185
|
+
run: |
|
|
186
|
+
set -euo pipefail
|
|
187
|
+
product="$(sed -nE 's/.*PRODUCT_NAME[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/p' shared/src/version.ts | head -1)"
|
|
188
|
+
[ -n "$product" ] || { echo "::error::could not read PRODUCT_NAME from shared/src/version.ts"; exit 1; }
|
|
189
|
+
nix develop .#build --command bun \
|
|
190
|
+
node_modules/@norskvideo/ctl-dev-kit/licence/check-entitlement.ts \
|
|
191
|
+
"$RUNNER_TEMP/norsk-license.json" "$product"
|
|
192
|
+
|
|
175
193
|
- name: Pre-pull the media + studio images
|
|
176
194
|
run: |
|
|
177
195
|
set -euo pipefail
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Does this licence entitle this product? Asked before anything is built or
|
|
2
|
+
// launched, because the failure it prevents is otherwise invisible.
|
|
3
|
+
//
|
|
4
|
+
// A V2 licence names the products it covers, and `product add` refuses one it
|
|
5
|
+
// does not name. Discovered late, that surfaces as
|
|
6
|
+
// `product did not become ready within 60000ms` — the same 60 seconds of
|
|
7
|
+
// silence as a product that could not start, or one on an address the daemon
|
|
8
|
+
// could not dial. Reuters' smoke tier sat behind exactly that ambiguity.
|
|
9
|
+
//
|
|
10
|
+
// Turnkeys make this routine rather than rare: each ships to one customer under
|
|
11
|
+
// a licence naming their product, so every new turnkey needs its own, and the
|
|
12
|
+
// moment one is missing should be a sentence rather than a timeout.
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
14
|
+
|
|
15
|
+
interface LicensedProduct {
|
|
16
|
+
product?: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The product names a licence covers, or null when the shape is unreadable —
|
|
20
|
+
* deliberately distinct from "covers nothing", which is a real answer. */
|
|
21
|
+
export function licensedProducts(licenceJson: string): string[] | null {
|
|
22
|
+
let payload: string;
|
|
23
|
+
try {
|
|
24
|
+
const outer = JSON.parse(licenceJson) as { payload?: unknown };
|
|
25
|
+
if (typeof outer.payload !== "string") return null;
|
|
26
|
+
payload = Buffer.from(outer.payload, "base64").toString("utf8");
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const inner = JSON.parse(payload) as { products?: unknown };
|
|
32
|
+
if (!Array.isArray(inner.products)) return null;
|
|
33
|
+
return inner.products.map((p) => (p as LicensedProduct)?.product).filter((p): p is string => typeof p === "string");
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The message to fail with, or null to proceed. Null covers two very different
|
|
40
|
+
* cases on purpose: the licence entitles the product, or the licence is in a
|
|
41
|
+
* shape this cannot read. Refusing on the second would assert something never
|
|
42
|
+
* determined, and would break the fleet on the first licence format change. */
|
|
43
|
+
export function entitlementProblem(licenceJson: string, product: string): string | null {
|
|
44
|
+
if (licenceJson.trim() === "") {
|
|
45
|
+
return `licence is empty — the NORSK_LICENSE_V2 secret is unset or unreadable, so ${product} cannot be launched`;
|
|
46
|
+
}
|
|
47
|
+
const products = licensedProducts(licenceJson);
|
|
48
|
+
if (products === null) return null;
|
|
49
|
+
if (products.includes(product)) return null;
|
|
50
|
+
const covers = products.length ? products.join(", ") : "nothing";
|
|
51
|
+
return `licence does not entitle ${product} (covers: ${covers})`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (import.meta.main) {
|
|
55
|
+
const [file, product] = process.argv.slice(2);
|
|
56
|
+
if (!file || !product) {
|
|
57
|
+
console.error("usage: check-entitlement.ts <licence.json> <product-name>");
|
|
58
|
+
process.exit(2);
|
|
59
|
+
}
|
|
60
|
+
let contents: string;
|
|
61
|
+
try {
|
|
62
|
+
contents = readFileSync(file, "utf8");
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.error(`licence-check: cannot read ${file}: ${String(e)}`);
|
|
65
|
+
process.exit(2);
|
|
66
|
+
}
|
|
67
|
+
const problem = entitlementProblem(contents, product);
|
|
68
|
+
if (problem) {
|
|
69
|
+
console.error(`licence-check: ${problem}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const products = licensedProducts(contents);
|
|
73
|
+
console.log(
|
|
74
|
+
products === null
|
|
75
|
+
? `licence-check: licence shape not recognised; entitlement for ${product} not asserted`
|
|
76
|
+
: `licence-check: ${product} is entitled (licence covers: ${products.join(", ")})`,
|
|
77
|
+
);
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@norskvideo/ctl-dev-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.75",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./create-product": "./create-product/create-product.ts",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"./doc-guide/prose-bundle": "./doc-guide/prose-bundle.js",
|
|
14
14
|
"./docs/path-existence": "./docs/path-existence.ts",
|
|
15
15
|
"./docs/splice-generated": "./docs/splice-generated.ts",
|
|
16
|
+
"./licence/check-entitlement": "./licence/check-entitlement.ts",
|
|
16
17
|
"./licence/check-product-name": "./licence/check-product-name.ts",
|
|
17
18
|
"./licence/check-template": "./licence/check-template.ts",
|
|
18
19
|
"./package.json": "./package.json",
|