Public: anyone can read this; the crew can change it.

scripts/drills/run.ts posts its result to POST /api/v1/drills, and docs/DOMAINS.md says how to run it #2582

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/2351-scripts-drills-run-ts-posts-its-result-t → main 3 commits fork

  • Merged. scout/2351-scripts-drills-run-ts-posts-its-result-t was merged into main as 6039e8b.

Review

Sign in to review this change.
3 files changed +37 −8
docs/DOMAINS.md +2 −1
@@ -41,7 +41,8 @@ says while unhealthy; when the forge is healthy again it posts what it saw and
4141 the seconds it took to `POST /api/v1/drills` and prints the verdict. If the
4242 forge is still down after an hour it posts nothing, says so, and exits 0. A
4343 refused post exits non-zero with the forge's message. `FORGE_TOKEN` must be a
44 site admin's token.
44 site admin's token; without it the script still watches, prints the result as
45 JSON and says that nothing was posted.
4546
4647 ```
4748 FORGE_TOKEN=… bun scripts/drills/run.ts runner --forge https://forge.open-platform.sh
scripts/drills/run.ts +11 −6
@@ -4,8 +4,9 @@
44 * polls `/api/v1/health` and `/` once a second keeping each distinct line the
55 * forge says while unhealthy, and when the forge is healthy again posts what
66 * it saw and how long it took to `POST /api/v1/drills` (`FORGE_TOKEN` from
7 * the environment). If the forge never comes back within the ceiling it posts
8 * nothing. Zero dependencies; it never shells out.
7 * the environment). Without a token it prints the result and says nothing was
8 * posted. If the forge never comes back within the ceiling it posts nothing.
9 * Zero dependencies; it never shells out.
910 */
1011
1112 import { DOMAINS } from "../../src/kernel/domains.ts";
@@ -38,7 +39,7 @@ export function forgePoll(forge: string, token: string, fetchIt: typeof fetch =
3839 const get = async (path: string): Promise<{ ok: boolean; lines: string[]; json: unknown }> => {
3940 try {
4041 const res = await fetchIt(`${forge}${path}`, {
41 headers: { authorization: `Bearer ${token}` },
42 headers: token === "" ? {} : { authorization: `Bearer ${token}` },
4243 redirect: "manual",
4344 signal: AbortSignal.timeout(5_000),
4445 });
@@ -101,6 +102,11 @@ export async function runDrill(run: DrillRun): Promise<number> {
101102 const { health = [], board = [] } = watched.said;
102103 const backSeconds = Math.max(0, Math.round(watched.healthyAt / 1000));
103104 const body = { domain: domain.key, seen: { health: cap(health), board: cap(board) }, backSeconds };
105 if (run.token === "") {
106 run.say("FORGE_TOKEN is not set; nothing was posted. The result:");
107 run.say(JSON.stringify({ domain: domain.key, backSeconds, posted: false, body }));
108 return 0;
109 }
104110 let res: Response;
105111 try {
106112 res = await fetch(`${run.forge}/api/v1/drills`, {
@@ -134,7 +140,7 @@ export interface DrillArgs {
134140 token: string;
135141 }
136142
137 const USAGE = "usage: FORGE_TOKEN=… bun scripts/drills/run.ts <domain> --forge <url> [--insecure]";
143 const USAGE = "usage: [FORGE_TOKEN=…] bun scripts/drills/run.ts <domain> --forge <url> [--insecure]";
138144 const LOOPBACK = new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
139145
140146 /** The options, or the one plain line to print before exiting 2. */
@@ -154,8 +160,7 @@ export function checkArgs(
154160 return { ok: false, error: `--forge must be an http(s) URL; ${USAGE}` };
155161 }
156162 const token = env.FORGE_TOKEN ?? "";
157 if (token === "") return { ok: false, error: `FORGE_TOKEN is empty; ${USAGE}` };
158 if (url.protocol === "http:" && !LOOPBACK.has(url.hostname) && !insecure) {
163 if (token !== "" && url.protocol === "http:" && !LOOPBACK.has(url.hostname) && !insecure) {
159164 return { ok: false, error: `refusing to send the admin token over plain http to ${url.hostname}; use https, or pass --insecure` };
160165 }
161166 return { ok: true, args: { domain, forge: forge.replace(/\/+$/, ""), token } };
test/drill-run.test.ts +24 −1
@@ -76,10 +76,33 @@ test("goes down and comes back: posts what it saw and prints the verdict", async
7676 expect(body.seen.health).toContain("Disk is nearly full: 97%");
7777 expect(body.seen.board).toContain("The runner is stuck");
7878 expect(body.backSeconds).toBe(3);
79 expect(Object.keys(body).sort()).toEqual(["backSeconds", "domain", "seen"]);
7980 expect(h.lines.filter((l) => l.includes("The runner is stuck"))).toHaveLength(1);
8081 expect(h.lines).toContain("GREEN: every phrase seen");
8182 });
8283
84 test("without a token: prints the result, says nothing was posted, posts nothing", async () => {
85 const state: Stub = { down: false, posts: [], refuse: false };
86 const lines: string[] = [];
87 let t = 0;
88 let sleeps = 0;
89 const code = await runDrill({
90 domain: "runner",
91 forge: stub(state),
92 token: "",
93 say: (l) => lines.push(l),
94 enter: async () => { state.down = true; },
95 now: () => t,
96 sleep: async (ms) => { t += ms; if (++sleeps === 3) state.down = false; },
97 intervalMs: 1000,
98 ceilingMs: 60_000,
99 });
100 expect(code).toBe(0);
101 expect(state.posts).toHaveLength(0);
102 expect(lines.some((l) => l.includes("nothing was posted"))).toBe(true);
103 expect(JSON.parse(lines.at(-1)!)).toMatchObject({ domain: "runner", backSeconds: 3, posted: false });
104 });
105
83106 test("stays down past the ceiling: posts nothing, exits 0, says so", async () => {
84107 const state: Stub = { down: false, posts: [], refuse: false };
85108 const h = harness(state, { ceilingMs: 5_000 });
@@ -126,7 +149,7 @@ test("checkArgs: each missing piece and the cleartext refusal is an error; the g
126149 };
127150 expect(bad(["--forge", "https://f.example"], env)).toContain("domain");
128151 expect(bad(["runner"], env)).toContain("--forge");
129 expect(bad(["runner", "--forge", "https://f.example"], { FORGE_TOKEN: "" })).toContain("FORGE_TOKEN");
152 expect(checkArgs(["runner", "--forge", "https://f.example"], {}).ok).toBe(true);
130153 expect(bad(["runner", "--forge", "http://forge.example"], env)).toContain("plain http");
131154 const ok = (argv: string[]) => checkArgs(argv, env);
132155 expect(ok(["runner", "--forge", "http://forge.example", "--insecure"]).ok).toBe(true);

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs