Public: anyone can read this; the crew can change it.
Archive, part 3b: Wake restarts an archived app's production from the default branch on the same data #2477
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/2457-archive-part-3b-wake-restarts-an-archive → main 1 commit fork 1 thread · 1 unresolved
2 files
changed +26
−2
src/actions/repos.ts +9
| @@ -8,6 +8,7 @@ import { requireVerified } from "./verify.ts"; | ||
| 8 | 8 | import { requireAbility } from "./roles.ts"; |
| 9 | 9 | import type { App } from "../app.ts"; |
| 10 | 10 | import { appsRuntime } from "../apps/runtime.ts"; |
| 11 | import { getAppByRepo } from "../core/apps.ts"; | |
| 11 | 12 | import { canSeeSealed } from "../core/sealed.ts"; |
| 12 | 13 | import { capacityValue } from "../core/capacity.ts"; |
| 13 | 14 | import { NEW_FOR_MS } from "./accounts.ts"; |
| @@ -317,6 +318,14 @@ export async function updateRepoSettings( | ||
| 317 | 318 | const runtime = appsRuntime(app); |
| 318 | 319 | if (runtime !== undefined) await runtime.stopRepo(repo.id); |
| 319 | 320 | } |
| 321 | // Waking brings production back from the default branch on the same data | |
| 322 | // directory; a preview returns on its pull's next push. | |
| 323 | if (updated.archivedAt === null && repo.archivedAt !== null) { | |
| 324 | const runtime = appsRuntime(app); | |
| 325 | const record = getAppByRepo(app.db, repo.id); | |
| 326 | if (runtime !== undefined && record !== undefined) | |
| 327 | await runtime.reconcileApp(record, updated, "wake", "production"); | |
| 328 | } | |
| 320 | 329 | const changed: string[] = ( |
| 321 | 330 | [ |
| 322 | 331 | "visibility", |
test/apps.test.ts +17 −2
| @@ -11,7 +11,7 @@ | ||
| 11 | 11 | */ |
| 12 | 12 | |
| 13 | 13 | import { afterAll, beforeAll, describe, expect, test } from "bun:test"; |
| 14 | import { existsSync, writeFileSync } from "node:fs"; | |
| 14 | import { existsSync, readFileSync, writeFileSync } from "node:fs"; | |
| 15 | 15 | import { mkdtemp, rm } from "node:fs/promises"; |
| 16 | 16 | import { tmpdir } from "node:os"; |
| 17 | 17 | import { join } from "node:path"; |
| @@ -460,7 +460,22 @@ describe("apps: production, previews, data, gates", () => { | ||
| 460 | 460 | // A visit does not wake it. |
| 461 | 461 | expect((await viaHost(`hello.${DOMAIN}`)).status).toBe(503); |
| 462 | 462 | expect(runtime.envStatus(record, "production")?.state).not.toBe("running"); |
| 463 | }, 60_000); | |
| 463 | ||
| 464 | // Waking restarts production on the same data directory. | |
| 465 | const woke = await updateRepoSettings(app, alice, repo, { | |
| 466 | archived: false, | |
| 467 | }); | |
| 468 | expect(woke.ok).toBe(true); | |
| 469 | for ( | |
| 470 | let i = 0; | |
| 471 | i < 600 && runtime.envStatus(record, "production")?.state !== "running"; | |
| 472 | i++ | |
| 473 | ) | |
| 474 | await Bun.sleep(100); | |
| 475 | expect(runtime.envStatus(record, "production")?.state).toBe("running"); | |
| 476 | expect(readFileSync(join(data, "archive-marker"), "utf8")).toBe("kept"); | |
| 477 | expect((await viaHost(`hello.${DOMAIN}`)).status).toBe(200); | |
| 478 | }, 120_000); | |
| 464 | 479 | }); |
| 465 | 480 | |
| 466 | 481 | describe("a preview whose copied data the build cannot read starts fresh (issue #36)", () => { |
new above
offline
Fine as written. Wake goes through the same admin and agent gate as Archive. Consider asserting in a test that an agent cannot wake, since that path is now live.