@norskvideo/ctl-dev-kit 0.2.8 → 0.2.10
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/check-drift.ts +45 -1
- package/conventions/integration.yml +13 -0
- package/conventions/smoke.yml +11 -3
- package/create-product/turnkey.ts +11 -0
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// (conventions/* + build/*), resolved package-relative so they work both
|
|
19
19
|
// workspace-symlinked and installed from the published tarball.
|
|
20
20
|
import { execFileSync } from "node:child_process";
|
|
21
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
21
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
22
22
|
import { join } from "node:path";
|
|
23
23
|
import { parseManifestSeed } from "@norskvideo/ctl-sdk/manifest-seed";
|
|
24
24
|
import { checkTheme } from "../docs-theme/sync.ts";
|
|
@@ -340,6 +340,49 @@ export function demoProblems(repoRoot: string, canonicalShim: string): string[]
|
|
|
340
340
|
return problems;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
// Product logs (norsk-mgr docs/internal/design/product-logs.md): every compose
|
|
344
|
+
// service a product template declares must say how it logs, or the node's log
|
|
345
|
+
// agent ships nothing (or garbage) for it. The schema's
|
|
346
|
+
// productTemplateLogsProblems says what is wrong with one template; what this
|
|
347
|
+
// gate pins is that the repo asks it. A repo that packs a product template
|
|
348
|
+
// must have a test that calls it — the packer itself cannot refuse, since
|
|
349
|
+
// products also pack at runtime and a missed entry must not become a 500.
|
|
350
|
+
const PACKS_TEMPLATE = /\bpackProductTemplate\b/;
|
|
351
|
+
const CHECKS_LOGS = /\bproductTemplateLogsProblems\b/;
|
|
352
|
+
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", "lib", "coverage", ".wireit"]);
|
|
353
|
+
|
|
354
|
+
function sourceFiles(dir: string, out: string[] = []): string[] {
|
|
355
|
+
let names: string[];
|
|
356
|
+
try {
|
|
357
|
+
names = readdirSync(dir);
|
|
358
|
+
} catch {
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
for (const name of names) {
|
|
362
|
+
if (SKIP_DIRS.has(name)) continue;
|
|
363
|
+
const path = join(dir, name);
|
|
364
|
+
const st = statSync(path);
|
|
365
|
+
if (st.isDirectory()) sourceFiles(path, out);
|
|
366
|
+
else if (/\.(ts|tsx)$/.test(name)) out.push(path);
|
|
367
|
+
}
|
|
368
|
+
return out;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export function productLogsProblems(repoRoot: string): string[] {
|
|
372
|
+
const files = sourceFiles(repoRoot);
|
|
373
|
+
const read = (f: string) => readFileSync(f, "utf8");
|
|
374
|
+
if (!files.some((f) => PACKS_TEMPLATE.test(read(f)))) return [];
|
|
375
|
+
const tested = files.some((f) => /\.test\.tsx?$/.test(f) && CHECKS_LOGS.test(read(f)));
|
|
376
|
+
return tested
|
|
377
|
+
? []
|
|
378
|
+
: [
|
|
379
|
+
"this repo packs a product template but no test calls productTemplateLogsProblems " +
|
|
380
|
+
"(@norskvideo/ctl-product-template-schema). Add one asserting it returns [] for every template " +
|
|
381
|
+
"the product builds, and give the manifest a `logs:` entry per compose service " +
|
|
382
|
+
"(norsk-mgr docs/internal/design/product-logs.md).",
|
|
383
|
+
];
|
|
384
|
+
}
|
|
385
|
+
|
|
343
386
|
// A file that must be a verbatim, byte-for-byte copy of its dev-kit canonical.
|
|
344
387
|
function verbatimProblem(
|
|
345
388
|
repoRoot: string,
|
|
@@ -737,6 +780,7 @@ export function checkDrift(repoRoot: string, canonical: CanonicalBytes, opts: Ch
|
|
|
737
780
|
for (const p of docsThemeProblems(repoRoot, opts.themeCanonical)) push(p);
|
|
738
781
|
|
|
739
782
|
for (const p of demoProblems(repoRoot, canonical.demoShim)) push(p);
|
|
783
|
+
for (const p of productLogsProblems(repoRoot)) push(p);
|
|
740
784
|
|
|
741
785
|
return { ok: problems.length === 0, problems };
|
|
742
786
|
}
|
|
@@ -283,6 +283,18 @@ jobs:
|
|
|
283
283
|
NORSK_CTL_BINARY: ${{ runner.temp }}/norsk-ctl
|
|
284
284
|
NORSK_LICENSE_FILE: ${{ runner.temp }}/norsk-license.json
|
|
285
285
|
NORSK_LAUNCHED_MANIFEST: ${{ runner.temp }}/norsk-launched-instances
|
|
286
|
+
# Where the harness copies each instance's engine logs and — since
|
|
287
|
+
# ctl-test-harness 0.1.60 — its CONTAINER logs, before teardown removes
|
|
288
|
+
# the containers. The studio container's stdout is the only place
|
|
289
|
+
# "Refusing to run workflow" and the runner's own startup appear, and
|
|
290
|
+
# every gate failure that pointed at `docker logs <instance>-studio-1`
|
|
291
|
+
# was pointing at something already destroyed.
|
|
292
|
+
#
|
|
293
|
+
# Keyed on run id + attempt because these runners are SHARED between
|
|
294
|
+
# repos: a bare $RUNNER_TEMP/norsk-logs is one directory nothing clears,
|
|
295
|
+
# and the first artifact built that way carried ten of another product's
|
|
296
|
+
# instances alongside its own three. Same shape the test-temp base uses.
|
|
297
|
+
NORSK_CI_LOG_DIR: ${{ runner.temp }}/norsk-logs-${{ github.run_id }}-${{ github.run_attempt }}
|
|
286
298
|
run: |
|
|
287
299
|
set -euo pipefail
|
|
288
300
|
suite() {
|
|
@@ -321,6 +333,7 @@ jobs:
|
|
|
321
333
|
path: |
|
|
322
334
|
${{ runner.temp }}/integration.log
|
|
323
335
|
test-results/
|
|
336
|
+
${{ runner.temp }}/norsk-logs-${{ github.run_id }}-${{ github.run_attempt }}
|
|
324
337
|
retention-days: 7
|
|
325
338
|
if-no-files-found: warn
|
|
326
339
|
|
package/conventions/smoke.yml
CHANGED
|
@@ -265,10 +265,18 @@ jobs:
|
|
|
265
265
|
NORSK_LICENSE_FILE: ${{ runner.temp }}/norsk-license.json
|
|
266
266
|
# Where the harness copies each instance's full engine logs
|
|
267
267
|
# (/var/log/norsk -> debug.json, host-mounted under the store) before
|
|
268
|
-
# teardown nukes them
|
|
268
|
+
# teardown nukes them, plus — since ctl-test-harness 0.1.60 — each
|
|
269
|
+
# instance's CONTAINER logs, which is the only copy of the studio
|
|
270
|
+
# container's stdout. Uploaded below. `docker logs` only sees the
|
|
269
271
|
# notice+ stdout subset and a decode-error flood can drown its tail;
|
|
270
272
|
# this is the complete structured log at every level.
|
|
271
|
-
|
|
273
|
+
#
|
|
274
|
+
# Keyed on run id + attempt because these runners are SHARED between
|
|
275
|
+
# repos: a bare $RUNNER_TEMP/norsk-logs is one directory nothing clears,
|
|
276
|
+
# so a job uploaded whatever earlier jobs had left in it (observed: one
|
|
277
|
+
# product's artifact carrying ten of another's instances). Same shape the
|
|
278
|
+
# test-temp base uses, for the same reason.
|
|
279
|
+
NORSK_CI_LOG_DIR: ${{ runner.temp }}/norsk-logs-${{ github.run_id }}-${{ github.run_attempt }}
|
|
272
280
|
run: |
|
|
273
281
|
# Outer pipefail so the step reports the journey's exit, not tee's --
|
|
274
282
|
# otherwise a failed smoke run over the `| tee` below scores green.
|
|
@@ -315,7 +323,7 @@ jobs:
|
|
|
315
323
|
uses: actions/upload-artifact@v4
|
|
316
324
|
with:
|
|
317
325
|
name: norsk-engine-logs-attempt-${{ github.run_attempt }}
|
|
318
|
-
path: ${{ runner.temp }}/norsk-logs
|
|
326
|
+
path: ${{ runner.temp }}/norsk-logs-${{ github.run_id }}-${{ github.run_attempt }}
|
|
319
327
|
retention-days: 7
|
|
320
328
|
if-no-files-found: ignore
|
|
321
329
|
|
|
@@ -1104,6 +1104,7 @@ import {
|
|
|
1104
1104
|
type ProductTemplateMaterials,
|
|
1105
1105
|
type ProductTemplateParameter,
|
|
1106
1106
|
STANDARD_ADVANCED_OVERRIDES,
|
|
1107
|
+
STANDARD_LOGS,
|
|
1107
1108
|
} from "@norskvideo/ctl-product-template-schema";
|
|
1108
1109
|
import type { ComponentLibrary } from "@norskvideo/norsk-studio-builder";
|
|
1109
1110
|
import { stringify as yamlStringify } from "yaml";
|
|
@@ -1155,6 +1156,10 @@ ${runtimeScreen} proxy: {
|
|
|
1155
1156
|
expose: [${proxyExpose}],
|
|
1156
1157
|
},
|
|
1157
1158
|
advanced: { ...STANDARD_ADVANCED_OVERRIDES, ...opts.advanced },
|
|
1159
|
+
// How each compose service logs, for the node's log agent. A service
|
|
1160
|
+
// added to renderCompose needs its own entry here — the drift gate
|
|
1161
|
+
// requires a test (product-template.test.ts) that says so.
|
|
1162
|
+
logs: STANDARD_LOGS,
|
|
1158
1163
|
};
|
|
1159
1164
|
|
|
1160
1165
|
return {
|
|
@@ -1256,6 +1261,7 @@ import {
|
|
|
1256
1261
|
ParametersFileSchema,
|
|
1257
1262
|
ProductTemplateManifestSchema,
|
|
1258
1263
|
ProductTemplateMaterialsSchema,
|
|
1264
|
+
productTemplateLogsProblems,
|
|
1259
1265
|
} from "@norskvideo/ctl-product-template-schema";
|
|
1260
1266
|
import { packProductTemplate } from "@norskvideo/ctl-product-template-schema/pack";
|
|
1261
1267
|
import type { ComponentLibrary } from "@norskvideo/norsk-studio-builder";
|
|
@@ -1277,6 +1283,11 @@ describe("product-template materials", () => {
|
|
|
1277
1283
|
expect(ProductTemplateMaterialsSchema.safeParse(materials).success).toBe(true);
|
|
1278
1284
|
});
|
|
1279
1285
|
|
|
1286
|
+
it("declare how every compose service logs", () => {
|
|
1287
|
+
const materials = buildProductTemplateMaterials(ProductConfigSchema.parse(example), { library });
|
|
1288
|
+
expect(productTemplateLogsProblems(materials)).toEqual([]);
|
|
1289
|
+
});
|
|
1290
|
+
|
|
1280
1291
|
it("pack to a tar whose entries round-trip through the schema package", () => {
|
|
1281
1292
|
const materials = buildProductTemplateMaterials(ProductConfigSchema.parse(example), { library });
|
|
1282
1293
|
const tar = packProductTemplate(materials);
|