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:
@@ -13,6 +13,8 @@ export interface ScriptRow {
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export type ScriptVersionState = 'untested' | 'tested' | 'test-failed';
|
||||
|
||||
export interface ScriptVersionRow {
|
||||
id: number;
|
||||
script_id: number;
|
||||
@@ -21,6 +23,7 @@ export interface ScriptVersionRow {
|
||||
size: number;
|
||||
content: Buffer;
|
||||
notes: string | null;
|
||||
state: ScriptVersionState;
|
||||
uploaded_at: string;
|
||||
}
|
||||
|
||||
@@ -233,3 +236,30 @@ export function serversMatchingScope(scopeType: ScopeType | null, scopeValue: st
|
||||
.all(scopeValue) as Array<{ name: string }>;
|
||||
return rows.map((r) => r.name);
|
||||
}
|
||||
|
||||
// Distinct game IDs present in the registered fleet. Powers the scope=game
|
||||
// dropdown so operators can only pick values that will match at least one
|
||||
// server.
|
||||
export function listDistinctGames(): string[] {
|
||||
const rows = db()
|
||||
.prepare(`SELECT DISTINCT game FROM servers WHERE game IS NOT NULL AND game <> '' ORDER BY game`)
|
||||
.all() as Array<{ game: string }>;
|
||||
return rows.map((r) => r.game);
|
||||
}
|
||||
|
||||
// Distinct maps grouped by their game. Scope=map is chained (pick game → pick
|
||||
// map) so the UI can filter map options to those valid under the selected game.
|
||||
export function listMapsByGame(): Record<string, string[]> {
|
||||
const rows = db()
|
||||
.prepare(
|
||||
`SELECT DISTINCT game, map FROM servers
|
||||
WHERE game IS NOT NULL AND game <> '' AND map IS NOT NULL AND map <> ''
|
||||
ORDER BY game, map`
|
||||
)
|
||||
.all() as Array<{ game: string; map: string }>;
|
||||
const out: Record<string, string[]> = {};
|
||||
for (const r of rows) {
|
||||
(out[r.game] ??= []).push(r.map);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user