Public: anyone can read this; the crew can change it.
Sentinel: red:01m23vygyffx5hg2f5vm8r74sr is past its budget: trevato/forge: main is red at 960a50f — "a deployed preview's findings are stored under its run, and the run still pass… #4862
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/4830-sentinel-red-01m23vygyffx5hg2f5vm8r74sr → main 1 commit fork 1 thread · 1 unresolved
2 files
changed +33
−1
src/apps/preview_spec.ts +19 −1
| @@ -16,11 +16,29 @@ export const PREVIEW_SPEC_TIMEOUT_MS = 30_000; | ||
| 16 | 16 | |
| 17 | 17 | const SECTION = "https://specification.website/"; |
| 18 | 18 | |
| 19 | /** `webspec()` over `paths` at `base`, with a clock; never throws. */ | |
| 19 | /** A finding that says the page never answered, not that it answered badly. */ | |
| 20 | const unanswered = (f: Finding) => f.check === "fetch" || f.check === "webspec"; | |
| 21 | ||
| 22 | /** | |
| 23 | * `webspec()` over `paths` at `base`, with a clock; never throws. A page that | |
| 24 | * did not answer is asked once more: a preview just swapped in on a busy | |
| 25 | * machine can miss its first request, and that is not a finding about it. | |
| 26 | */ | |
| 20 | 27 | export async function previewSpec( |
| 21 | 28 | base: string, |
| 22 | 29 | paths: readonly string[], |
| 23 | 30 | timeoutMs = PREVIEW_SPEC_TIMEOUT_MS, |
| 31 | ): Promise<Finding[]> { | |
| 32 | const first = await previewSpecOnce(base, paths, timeoutMs); | |
| 33 | if (!first.some(unanswered)) return first; | |
| 34 | const again = await previewSpecOnce(base, paths, timeoutMs); | |
| 35 | return again.filter(unanswered).length <= first.filter(unanswered).length ? again : first; | |
| 36 | } | |
| 37 | ||
| 38 | async function previewSpecOnce( | |
| 39 | base: string, | |
| 40 | paths: readonly string[], | |
| 41 | timeoutMs = PREVIEW_SPEC_TIMEOUT_MS, | |
| 24 | 42 | ): Promise<Finding[]> { |
| 25 | 43 | const stop = new AbortController(); |
| 26 | 44 | let timer: ReturnType<typeof setTimeout> | undefined; |
test/preview-spec.test.ts +14
| @@ -118,6 +118,20 @@ test("a webspec that cannot finish is one finding, and is stored", async () => { | ||
| 118 | 118 | expect(slow[0]!.message).toContain("timed out"); |
| 119 | 119 | }); |
| 120 | 120 | |
| 121 | test("a page that misses its first request is asked once more", async () => { | |
| 122 | let calls = 0; | |
| 123 | const flaky = Bun.serve({ | |
| 124 | port: 0, | |
| 125 | fetch: () => (++calls === 1 | |
| 126 | ? new Response("busy", { status: 503 }) | |
| 127 | : new Response("<!doctype html><html lang=en><title>t</title><p>hi", { headers: { "content-type": "text/html" } })), | |
| 128 | }); | |
| 129 | const found = await previewSpec(`http://127.0.0.1:${flaky.port}`, ["/"], 5_000); | |
| 130 | flaky.stop(true); | |
| 131 | expect(found.some((f) => f.check === "fetch")).toBe(false); | |
| 132 | expect(found.some((f) => f.check === "etag")).toBe(true); | |
| 133 | }); | |
| 134 | ||
| 121 | 135 | test("the terminal's checks row carries the count, edge findings apart", () => { |
| 122 | 136 | const found = [ |
| 123 | 137 | { path: "/", check: "etag", message: "m", section: "https://specification.website/#caching" }, |
new above
offline
The retry reruns the full timeout; a hung preview now holds the deploy check for 2x30s. Give the second run only the remaining budget.