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

Sentinel: red:01m23vygyffx5hg2f5vm8r74sr was degraded 120 minutes today: trevato/forge: main is red at ca12f0f — "a fork that follows its parent > the terminal's About card says the same sente… #2101

Merged opened this

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

Merged

scout/forge:scout/2075-sentinel-red-01m23vygyffx5hg2f5vm8r74sr → main 5 commits fork 1 thread · 1 unresolved

  • Merged. scout/2075-sentinel-red-01m23vygyffx5hg2f5vm8r74sr was merged into main as 8c724a2.

Review

Sign in to review this change.
3 files changed +54 −9
src/core/parent_releases.ts +11
@@ -146,6 +146,17 @@ async function fetchDoc(url: string, f: typeof fetch): Promise<unknown> {
146146
147147 const refreshing = new Map<string, Promise<void>>();
148148
149 /** Resolves when no refresh of this cache is in flight (at once if none is). */

Nit: the doc comment could say this is for tests and shutdown. Otherwise it reads as dead production code.

150 export async function refreshIdle(dataDir: string): Promise<void> {
151 const file = join(dataDir, "parent-releases.json");
152 for (
153 let job = refreshing.get(file);
154 job !== undefined;
155 job = refreshing.get(file)
156 )
157 await job;
158 }
159
149160 /** Reads the parent now and stores the answer; one refresh at a time per cache. */
150161 export function refreshParentReleases(
151162 dataDir: string,
test/following.test.ts +3 −9
@@ -8,7 +8,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
88 import { mkdtemp, rm } from "node:fs/promises";
99 import { tmpdir } from "node:os";
1010 import { join } from "node:path";
11 import { parentUrlProblem } from "../src/core/parent_releases.ts";
11 import { parentUrlProblem, refreshIdle } from "../src/core/parent_releases.ts";
1212 import { Session } from "../src/tui/session.ts";
1313 import { releasesRow } from "../src/tui/screens/about.ts";
1414 import { createApp, type App } from "../src/app.ts";
@@ -967,15 +967,9 @@ describe("a landed pull moves the forks, like the push it amounts to", () => {
967967 });
968968 });
969969
970 /** Waits for a background refresh to rewrite the cache (or 600 ms for none). */
970 /** Waits for any background refresh of the cache to finish. */
971971 async function settled(dir: string): Promise<void> {
972 const file = Bun.file(join(dir, "parent-releases.json"));
973 const text = async () => ((await file.exists()) ? await file.text() : "");
974 const before = await text();
975 for (let i = 0; i < 24; i++) {
976 await Bun.sleep(25);
977 if ((await text()) !== before) return;
978 }
972 await refreshIdle(dir);
979973 }
980974
981975 describe("a parent URL", () => {
test/parent-releases-idle.test.ts added +40
@@ -0,0 +1,40 @@
1 import { expect, test } from "bun:test";
2 import { mkdtemp, rm } from "node:fs/promises";
3 import { tmpdir } from "node:os";
4 import { join } from "node:path";
5 import {
6 refreshIdle,
7 refreshParentReleases,
8 } from "../src/core/parent_releases.ts";
9
10 test("refreshIdle resolves at once with nothing in flight, and waits for a slow refresh", async () => {
11 const dir = await mkdtemp(join(tmpdir(), "forge-idle-"));
12 try {
13 await refreshIdle(dir);
14 let release!: () => void;
15 const gate = new Promise<void>((r) => (release = r));
16 const slow = (async () => {
17 await gate;
18 return Response.json({
19 releases: [{ name: "1.0.0", url: "http://127.0.0.1/r" }],
20 });
21 }) as unknown as typeof fetch;
22 void refreshParentReleases(dir, "http://127.0.0.1:1", {
23 loopback: true,
24 fetch: slow,
25 });
26 let idle = false;
27 const waiting = refreshIdle(dir).then(() => (idle = true));
28 await Bun.sleep(30);
29 expect(idle).toBe(false);
30 release();
31 await waiting;
32 expect(idle).toBe(true);
33 const stored = JSON.parse(
34 await Bun.file(join(dir, "parent-releases.json")).text(),
35 );
36 expect(stored.ok).toBe(true);
37 } finally {
38 await rm(dir, { recursive: true, force: true });
39 }
40 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs