@norskvideo/ctl-test-harness 0.1.20 → 0.1.22
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/compose-advancing.d.ts +26 -0
- package/compose-advancing.js +61 -0
- package/container-logs.d.ts +48 -0
- package/container-logs.js +101 -0
- package/demo/cli.d.ts +22 -3
- package/demo/cli.js +136 -21
- package/demo/dev-loop-cli.d.ts +2 -0
- package/demo/dev-loop-cli.js +8 -0
- package/demo/dev-loop.d.ts +33 -0
- package/demo/dev-loop.js +205 -0
- package/demo/gates.d.ts +22 -0
- package/demo/gates.js +56 -0
- package/demo/index.d.ts +8 -3
- package/demo/index.js +4 -1
- package/demo/panes.d.ts +81 -0
- package/demo/panes.js +236 -0
- package/demo/run.d.ts +132 -5
- package/demo/run.js +386 -94
- package/demo/spec.d.ts +48 -6
- package/demo/spec.js +37 -11
- package/package.json +11 -2
package/demo/run.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { type ManifestSeed } from "@norskvideo/ctl-sdk/manifest-seed";
|
|
1
2
|
import { ensureRunnerOnNetwork } from "../container-net.js";
|
|
2
3
|
import { type DaemonProcess, type StartDaemonOptions } from "../daemon.js";
|
|
3
4
|
import { type BaseHarnessPorts } from "../harness-config.js";
|
|
4
5
|
import { type SourceHandle, type SrtPumpTarget } from "../source-pump.js";
|
|
5
|
-
import type { DemoIngest, DemoSpec } from "./spec.js";
|
|
6
|
+
import type { DemoDaemonPolicy, DemoIngest, DemoMode, DemoSpec, DemoTemplate } from "./spec.js";
|
|
6
7
|
export interface CliResult {
|
|
7
8
|
stdout: string;
|
|
8
9
|
stderr: string;
|
|
@@ -16,20 +17,38 @@ export interface ProcessHandle {
|
|
|
16
17
|
/** What `up` records so `down` (or a refused second `up`) can find the run. */
|
|
17
18
|
export interface DemoState {
|
|
18
19
|
product: string;
|
|
20
|
+
/** Absent on a demo's record; `dev-loop` on the standalone tier's, which
|
|
21
|
+
* lives beside it under its own file so the two never mistake each other. */
|
|
22
|
+
kind?: DemoStateKind;
|
|
19
23
|
storeDir: string;
|
|
20
24
|
daemonPort: number;
|
|
21
25
|
instanceId?: string;
|
|
22
26
|
devPid?: number;
|
|
27
|
+
/** Absent on records from before the policy existed: private. */
|
|
28
|
+
daemon?: DemoDaemonPolicy;
|
|
29
|
+
/** Extra containers `up` started, for `down` to remove. */
|
|
30
|
+
extras?: string[];
|
|
31
|
+
/** dev-loop: the exported workdir and the template it exported, for `refresh`. */
|
|
32
|
+
workdir?: string;
|
|
33
|
+
templateName?: string;
|
|
23
34
|
}
|
|
35
|
+
export type DemoStateKind = "demo" | "dev-loop";
|
|
24
36
|
export interface DemoStateStore {
|
|
25
|
-
read(product: string): DemoState | null;
|
|
37
|
+
read(product: string, kind?: DemoStateKind): DemoState | null;
|
|
26
38
|
write(state: DemoState): void;
|
|
27
|
-
remove(product: string): void;
|
|
39
|
+
remove(product: string, kind?: DemoStateKind): void;
|
|
28
40
|
}
|
|
29
41
|
export interface DemoDeps {
|
|
30
42
|
licenseFile(): string;
|
|
31
43
|
storeDir(slug: string): string;
|
|
44
|
+
/** The developer's real store, for `--daemon reuse`. */
|
|
45
|
+
realStoreDir(): string;
|
|
32
46
|
writeFile(path: string, contents: string): void;
|
|
47
|
+
/** Remove a file if present (the dev-loop's export stamp). */
|
|
48
|
+
removeFile(path: string): void;
|
|
49
|
+
/** File contents, or null when unreadable (compose.yml, manifest.seed.json,
|
|
50
|
+
* config.yaml, proxy-secret). */
|
|
51
|
+
readFile(path: string): string | null;
|
|
33
52
|
fileExists(path: string): boolean;
|
|
34
53
|
startDaemon(storeDir: string, options: StartDaemonOptions): {
|
|
35
54
|
daemon: DaemonProcess;
|
|
@@ -42,11 +61,14 @@ export interface DemoDeps {
|
|
|
42
61
|
cwd: string;
|
|
43
62
|
env: Record<string, string>;
|
|
44
63
|
}): ProcessHandle;
|
|
64
|
+
/** One `docker <argv>` invocation: extras, and the reuse policy's control-plane container sweep. */
|
|
65
|
+
docker(argv: string[]): CliResult;
|
|
45
66
|
fetch(url: string, init?: RequestInit): Promise<Response>;
|
|
46
67
|
startSources(opts: {
|
|
47
68
|
daemonPort: number;
|
|
48
69
|
instanceId: string;
|
|
49
70
|
targets: readonly SrtPumpTarget[];
|
|
71
|
+
proxySecret?: string;
|
|
50
72
|
timeoutMs?: number;
|
|
51
73
|
}): Promise<SourceHandle[]>;
|
|
52
74
|
stopSources(handles: SourceHandle[]): Promise<void>;
|
|
@@ -83,14 +105,21 @@ export interface DemoTimeouts {
|
|
|
83
105
|
}
|
|
84
106
|
export interface DemoRunOptions {
|
|
85
107
|
action: "up" | "check";
|
|
86
|
-
mode:
|
|
87
|
-
/**
|
|
108
|
+
mode: DemoMode;
|
|
109
|
+
/** Default private. */
|
|
110
|
+
daemon?: DemoDaemonPolicy;
|
|
111
|
+
/** `reuse`: the daemon's port (default NORSK_CTL_PORT, else 8333). */
|
|
112
|
+
daemonPort?: number;
|
|
113
|
+
/** The product repo root: dev command cwd, relative inputs and links, manifest.seed.json. */
|
|
88
114
|
cwd: string;
|
|
89
115
|
/** Default: the product name without its `norsk-` prefix. */
|
|
90
116
|
slug?: string;
|
|
91
117
|
/** Releases an `up` hold. */
|
|
92
118
|
abort?: AbortSignal;
|
|
93
119
|
timeouts?: DemoTimeouts;
|
|
120
|
+
/** The hostname other machines reach this box by: every printed URL uses
|
|
121
|
+
* it, and a private daemon is initialised with it as its publicHost. */
|
|
122
|
+
publicHost?: string;
|
|
94
123
|
}
|
|
95
124
|
export interface DemoRunResult {
|
|
96
125
|
instanceId: string;
|
|
@@ -134,6 +163,23 @@ export interface TemplateParams {
|
|
|
134
163
|
* no parameter carries it and the instance offers it as the conventional
|
|
135
164
|
* default, so the demo follows the template when the template moves. */
|
|
136
165
|
export declare function resolveIngestPort(ingest: DemoIngest, rows: IngestPortRow[], t?: TemplateParams): number;
|
|
166
|
+
/** A stored template's `image:` ref that disagrees with the product's seed. */
|
|
167
|
+
export interface PinMismatch {
|
|
168
|
+
repo: string;
|
|
169
|
+
stored: string;
|
|
170
|
+
seed: string;
|
|
171
|
+
}
|
|
172
|
+
/** Image pins are read, never typed (05-demo s4): for every repo the seed's
|
|
173
|
+
* `latest` names (studio, media), the stored compose must pin the same ref.
|
|
174
|
+
* A repo the compose does not name is not a mismatch — the guard is about a
|
|
175
|
+
* stale snapshot, not template completeness. */
|
|
176
|
+
export declare function checkTemplatePins(composeText: string, seed: ManifestSeed): PinMismatch[];
|
|
177
|
+
/** The two config.yaml scalars the proxy URL needs; a regex, not a YAML
|
|
178
|
+
* parser, because that is all the driver reads from a store it does not own. */
|
|
179
|
+
export declare function parseProxyConfig(configText: string | null): {
|
|
180
|
+
scheme: "http" | "https";
|
|
181
|
+
port: number;
|
|
182
|
+
};
|
|
137
183
|
export declare function findBrokenSymlinks(dir: string): string[];
|
|
138
184
|
/** `<cwd>/test-temp/demo/<product>.json` — beside the store dirs, inside the
|
|
139
185
|
* consumer's repo, where `down` from another shell can find it. */
|
|
@@ -142,6 +188,84 @@ export declare function fileStateStore(cwd: string): DemoStateStore;
|
|
|
142
188
|
* own), falling back to the pid alone if it is not a group leader. */
|
|
143
189
|
export declare function killGroup(pid: number): void;
|
|
144
190
|
export declare function defaultDemoDeps(cwd: string): DemoDeps;
|
|
191
|
+
export interface SessionOptions {
|
|
192
|
+
mode: DemoMode;
|
|
193
|
+
daemon: DemoDaemonPolicy;
|
|
194
|
+
daemonPort?: number;
|
|
195
|
+
timeouts?: DemoTimeouts;
|
|
196
|
+
publicHost?: string;
|
|
197
|
+
/** Private: attach to an existing private store (a recorded run) instead of making one. */
|
|
198
|
+
storeDir?: string;
|
|
199
|
+
}
|
|
200
|
+
/** The pieces of a run that `runDemo`, `runExportCheck` and the dev-loop
|
|
201
|
+
* share: the daemon (private on its band, or the developer's), the dev
|
|
202
|
+
* backend on the driver's port, the registration, and the template (built
|
|
203
|
+
* or chosen). */
|
|
204
|
+
export declare class DemoSession {
|
|
205
|
+
readonly spec: DemoSpec;
|
|
206
|
+
readonly slug: string;
|
|
207
|
+
readonly cwd: string;
|
|
208
|
+
readonly deps: DemoDeps;
|
|
209
|
+
readonly mode: DemoMode;
|
|
210
|
+
readonly policy: DemoDaemonPolicy;
|
|
211
|
+
readonly ports: DemoPorts;
|
|
212
|
+
readonly storeDir: string;
|
|
213
|
+
/** The host every printed URL names. */
|
|
214
|
+
readonly host: string;
|
|
215
|
+
readonly publicHost: string | undefined;
|
|
216
|
+
/** Scheme + authority of the daemon's proxy, for `proxy` URLs. */
|
|
217
|
+
readonly proxyBase: string;
|
|
218
|
+
daemon: DaemonProcess | null;
|
|
219
|
+
dev: ProcessHandle | null;
|
|
220
|
+
extras: string[];
|
|
221
|
+
private readonly timeouts;
|
|
222
|
+
constructor(spec: DemoSpec, slug: string, cwd: string, deps: DemoDeps, opts: SessionOptions);
|
|
223
|
+
get devUrl(): string;
|
|
224
|
+
templateDir(name: string): string;
|
|
225
|
+
/** What the real daemon's proxy demands on `/api/*`; absent on a private daemon. */
|
|
226
|
+
proxySecret(): string | undefined;
|
|
227
|
+
cli: (argv: string[], opts?: {
|
|
228
|
+
output?: "json" | "yaml";
|
|
229
|
+
}) => Promise<CliResult>;
|
|
230
|
+
cliOk: (argv: string[], opts?: {
|
|
231
|
+
output?: "json" | "yaml";
|
|
232
|
+
}) => Promise<CliResult>;
|
|
233
|
+
/** Private: init a virgin store and start a daemon on the band. Reuse: the
|
|
234
|
+
* developer's daemon must already answer. */
|
|
235
|
+
attachDaemon(): Promise<void>;
|
|
236
|
+
startDev(): Promise<void>;
|
|
237
|
+
/** The reuse policy's mandatory sequence: `product add` refuses an existing
|
|
238
|
+
* name and stored templates are immutable, so the instance, every template
|
|
239
|
+
* the product publishes (plus the spec's built one), the product and its
|
|
240
|
+
* control-plane container all go first. Each step tolerates absence. */
|
|
241
|
+
resetRegistration(instanceId: string): Promise<void>;
|
|
242
|
+
register(opts: {
|
|
243
|
+
licence: boolean;
|
|
244
|
+
}): Promise<void>;
|
|
245
|
+
/** The template to launch: built from the spec's input, or a name the
|
|
246
|
+
* product publishes (its first default when the spec names none). */
|
|
247
|
+
resolveTemplate(t?: DemoTemplate | undefined): Promise<string>;
|
|
248
|
+
/** Funke's iterate.sh pin guard for every product: the stored compose must
|
|
249
|
+
* pin what manifest.seed.json declares. Image mode, and any reuse — a
|
|
250
|
+
* private dev-mode daemon renders the template fresh from source, so there
|
|
251
|
+
* is nothing stale to catch there. */
|
|
252
|
+
pinGuard(templateName: string): Promise<void>;
|
|
253
|
+
/** name -> stringified default, from `template show`. */
|
|
254
|
+
declaredParams(templateName: string): Promise<Map<string, string | undefined>>;
|
|
255
|
+
startExtras(instanceId: string): void;
|
|
256
|
+
stopExtras(): void;
|
|
257
|
+
waitRunning(instanceId: string): Promise<void>;
|
|
258
|
+
instanceListed(instanceId: string): Promise<boolean>;
|
|
259
|
+
ingestPorts(instanceId: string): Promise<IngestPortRow[]>;
|
|
260
|
+
gateTimeout(ms: number | undefined): number;
|
|
261
|
+
/** Tear down in reverse: sources, instance, then — private only — the
|
|
262
|
+
* daemon (with its proxy), the dev backend, the store. Reuse leaves the
|
|
263
|
+
* developer's daemon, store and registration as they are. */
|
|
264
|
+
teardown(opts: {
|
|
265
|
+
instances: string[];
|
|
266
|
+
handles: SourceHandle[];
|
|
267
|
+
}): Promise<void>;
|
|
268
|
+
}
|
|
145
269
|
export declare function runDemo(spec: DemoSpec, opts: DemoRunOptions, deps?: DemoDeps): Promise<DemoRunResult>;
|
|
146
270
|
export interface ExportCheckOptions {
|
|
147
271
|
cwd: string;
|
|
@@ -156,5 +280,8 @@ export interface ExportCheckOptions {
|
|
|
156
280
|
export declare function runExportCheck(spec: DemoSpec, opts: ExportCheckOptions, deps?: DemoDeps): Promise<{
|
|
157
281
|
exportDir: string;
|
|
158
282
|
}>;
|
|
283
|
+
/** `template export-workdir` with the spec's live-source links and params,
|
|
284
|
+
* then the check that rotted (05-demo s1): every symlink resolves. */
|
|
285
|
+
export declare function exportStandaloneWorkdir(s: DemoSession, templateName: string, to: string): Promise<void>;
|
|
159
286
|
/** `demo down`: tear down the run `up` recorded, from any shell. */
|
|
160
287
|
export declare function demoDown(product: string, deps?: DemoDeps): Promise<void>;
|