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

Sentinel: release was degraded 50 minutes today: Release 0.7.479 is unproven: https://hello--forge-trevato-apps.open-platform.sh/ as desktop TimeoutError: The operation… #10190

Merged opened this

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

Merged

scout/forge:scout/10189-sentinel-release-was-degraded-50-minutes → main 2 commits fork

  • Merged. scout/10189-sentinel-release-was-degraded-50-minutes was merged into main as 1bda42c.

Review

Sign in to review this change.
2 files changed +36 −6
src/release/smoke.ts +13 −6
@@ -313,12 +313,19 @@ export async function smoke(base: string, token = "", options: Options = {}): Pr
313313 }
314314 if (options.appHost) {
315315 const url = new URL("/", options.appHost).href;
316 try {
317 const res = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(10_000), headers: { "user-agent": AGENTS["desktop"]! } });
318 const body = await res.text();
319 if (/<title>\s*forge\s*<\/title>/i.test(body)) fail(url, "desktop", res, "an app host answered with the forge's page");
320 } catch (error) {
321 failures.push({ url, agent: "desktop", status: "error", contentType: "", why: String(error) });
316 // This check asks one thing: does an app host answer as the forge (#6726)? An app that is
317 // asleep, stopped or broken and gives no answer at all says nothing about that: it is the
318 // app's health, not the release's, so it is asked once more and then left out of the verdict
319 // (#10189: hello's host timing out held release 0.7.479 unproven for hours).
320 for (let attempt = 1; attempt <= 2; attempt++) {
321 try {
322 const res = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(10_000), headers: { "user-agent": AGENTS["desktop"]! } });
323 const body = await res.text();
324 if (/<title>\s*forge\s*<\/title>/i.test(body)) fail(url, "desktop", res, "an app host answered with the forge's page");
325 break;
326 } catch (error) {
327 console.warn(`smoke: app host ${url} gave no answer (${error}); ${attempt === 1 ? "asking again" : "the app's health, not the release's"}`);
328 }
322329 }
323330 }
324331 if (options.heavy && failures.length === 0) {
test/smoke.test.ts +23
@@ -199,3 +199,26 @@ test("--app-host fails when an app host answers with the forge's page", async ()
199199 appPage.stop(true);
200200 }
201201 });
202
203 test("an app host that gives no answer is asked again, then left out of the release verdict (#10189)", async () => {
204 const base = stub("text/css");
205 const timeout = () => Object.assign(new Error("The operation timed out."), { name: "TimeoutError" });
206 const make = (fails: number, body = "<title>notes</title>") => {
207 let hostCalls = 0;
208 const f = (async (input: string | URL, init?: RequestInit) => {
209 if (String(input).startsWith("http://app.invalid")) {
210 if (++hostCalls <= fails) throw timeout();
211 return new Response(body);
212 }
213 return fetch(input, init);
214 }) as unknown as typeof fetch;
215 return { f, calls: () => hostCalls };
216 };
217 const once = make(1, "<title>forge</title>");
218 // A wake-up timeout is asked again, and the second answer is still judged.
219 expect((await smoke(base, "t", { appHost: "http://app.invalid/", fetch: once.f })).map((f) => f.why)).toEqual(["an app host answered with the forge's page"]);
220 expect(once.calls()).toBe(2);
221 const silent = make(99);
222 expect(await smoke(base, "t", { appHost: "http://app.invalid/", fetch: silent.f })).toEqual([]);
223 expect(silent.calls()).toBe(2);
224 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs