Promote as a real deployment; version state and scope dropdowns

Turns promotion into a first-class deployment (action='promote') so it lands
in the deployments UI with before/after content, per-server restart tracking,
and one-click rollback. Non-beta matching-scope servers get restart_state=
pending so the existing sweep drains and restarts them the same way drift does.

Also adds a per-version state (untested/tested/test-failed) so the upload
flow can visibly signal that a beta must land before promotion, and swaps
the scope-value text box for game/map dropdowns populated from the fleet.
Beta rollback at ready-promote is now labelled Reject and marks the version
test-failed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-09-12 14:24:35 -04:00
co-authored by Claude Opus 4.7
parent 3f934fd579
commit 1afdfddc87
16 changed files with 1014 additions and 41 deletions
+12 -2
View File
@@ -75,7 +75,11 @@
} finally { acting = null; }
}
async function rollback(): Promise<void> {
if (!confirm('Rollback this beta?')) return;
const isReject = beta?.state === 'ready-promote';
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;
acting = 'rollback';
flash = null;
try {
@@ -171,7 +175,13 @@
onclick={rollback}
disabled={beta.state === 'promoted' || beta.state === 'rolled-back' || acting !== null}
>
{acting === 'rollback' ? 'rolling back…' : 'rollback'}
{#if acting === 'rollback'}
{beta.state === 'ready-promote' ? 'rejecting…' : 'rolling back…'}
{:else if beta.state === 'ready-promote'}
reject
{:else}
rollback
{/if}
</button>
</div>
</section>