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
+17 -3
View File
@@ -6,9 +6,15 @@ export async function GET({ url }: { url: URL }) {
const deployments = db()
.prepare(
`SELECT id, action, started_at, finished_at, triggered_by
FROM deployments
ORDER BY started_at DESC
`SELECT d.id, d.action, d.started_at, d.finished_at, d.triggered_by,
d.script_id, d.script_version_id, d.prior_version_id, d.beta_id, d.rolled_back_by,
s.name AS script_name, sv.version AS script_version,
pv.version AS prior_version
FROM deployments d
LEFT JOIN scripts s ON s.id = d.script_id
LEFT JOIN script_versions sv ON sv.id = d.script_version_id
LEFT JOIN script_versions pv ON pv.id = d.prior_version_id
ORDER BY d.started_at DESC
LIMIT 500`
)
.all() as Array<{
@@ -17,6 +23,14 @@ export async function GET({ url }: { url: URL }) {
started_at: string;
finished_at: string | null;
triggered_by: string | null;
script_id: number | null;
script_version_id: number | null;
prior_version_id: number | null;
beta_id: number | null;
rolled_back_by: string | null;
script_name: string | null;
script_version: number | null;
prior_version: number | null;
}>;
const serversByDep = db()
@@ -0,0 +1,14 @@
import { json } from '@sveltejs/kit';
import { rollbackPromoteDeployment, RollbackError } from '$lib/server/fleet/rollback.js';
export async function POST({ params }: { params: { id: string } }) {
try {
const result = await rollbackPromoteDeployment(params.id);
return json({ ok: true, result });
} catch (err) {
if (err instanceof RollbackError) {
return json({ ok: false, error: err.message, kind: err.kind }, { status: 409 });
}
throw err;
}
}