Add configurable beta test plans and staged rollout

This commit is contained in:
2026-09-17 21:35:28 -04:00
parent 5b5266da3a
commit a50eae9d21
26 changed files with 1518 additions and 44 deletions
+14 -5
View File
@@ -1,4 +1,6 @@
<script lang="ts">
import BetaTestProgress from "$ui/components/BetaTestProgress.svelte";
import type { BetaTestView } from "$lib/beta-plan.js";
import { onMount, onDestroy } from 'svelte';
import { page } from '$app/stores';
import Card from '$ui/components/primitives/Card.svelte';
@@ -30,6 +32,7 @@
version: { id: number; version: number; sha256: string } | null;
}
let test = $state<BetaTestView | null>(null);
let beta = $state<BetaRow | null>(null);
let scripts = $state<BetaScript[]>([]);
let servers = $state<BetaServerRow[]>([]);
@@ -50,7 +53,7 @@
if (!res.ok) throw new Error(res.status === 404 ? 'Beta not found.' : 'Could not refresh beta.');
const body = await res.json();
if (current !== request || requestedId !== id) return;
beta = body.beta; scripts = body.scripts; servers = body.servers; events = body.events;
test = body.test; beta = body.beta; scripts = body.scripts; servers = body.servers; events = body.events;
loadError = '';
} catch (err) {
if (current === request && requestedId === id) loadError = (err as Error).message;
@@ -79,7 +82,8 @@
const prompt = isReject
? `Reject beta #${id}? This restores original files on every beta server and marks the version as test-failed.`
: `Rollback beta #${id}? This restores original files on every beta server.`;
if (!confirm(prompt)) return;
const restart = test ? test.plan.rollbackRestart === 'when-empty' ? ' Restarts will roll out when servers are empty.' : ' Restart servers manually to load the restored files.' : '';
if (!confirm(prompt + restart)) return;
acting = 'rollback'; flash = null;
try {
const res = await fetch(`/api/betas/${id}/rollback`, { method: 'POST' });
@@ -143,7 +147,7 @@
</div>
</Card>
<OperationHistory steps={betaFlow(b, servers)} {events} active={b.state === 'running' || b.state === 'ready-promote'} />
<OperationHistory steps={betaFlow({ ...b, testPlan: !!test }, servers)} {events} active={b.state === 'running' || b.state === 'ready-promote'} />
<!-- Artifacts -->
<Card padding="lg">
@@ -168,6 +172,9 @@
{/if}
</Card>
{#if test}
<Card padding="lg"><BetaTestProgress {test} betaId={id} closed={b.state === "promoted" || b.state === "rolled-back"} onUpdate={load} /></Card>
{:else}
<!-- Options -->
<Card padding="lg">
<div class="panel-head"><h2>Options</h2></div>
@@ -183,6 +190,8 @@
</div>
</Card>
{/if}
<!-- Server progress -->
<Card padding="lg">
<div class="panel-head">
@@ -206,7 +215,7 @@
{#if bs.error}
<span class="err">{bs.error}</span>
{/if}
{#if bs.stage === 'failed'}
{#if bs.stage === 'failed' && !test}
<Button
variant="secondary"
size="sm"
@@ -231,7 +240,7 @@
onclick={promote}
disabled={b.state !== 'ready-promote' || acting !== null}
loading={acting === 'promote'}
title={b.state !== 'ready-promote' ? 'All servers must reach ready first' : ''}
title={b.state !== 'ready-promote' ? 'The test plan must pass before promotion' : ''}
>
Promote
</Button>