multi script beta

This commit is contained in:
2026-09-12 18:23:56 -04:00
parent 30e59c900c
commit 85bcb05a7d
16 changed files with 1053 additions and 516 deletions
+30 -20
View File
@@ -2,9 +2,9 @@ import { json } from '@sveltejs/kit';
import {
listBetas,
listBetaServers,
listBetaScripts,
openBeta,
BetaError,
type BetaTriggerKind
BetaError
} from '$lib/server/fleet/beta.js';
import { getScript, getVersion } from '$lib/server/fleet/scripts.js';
import { kick } from '$lib/server/pipeline/worker.js';
@@ -17,53 +17,63 @@ export async function GET({ url }: { url: URL }) {
| 'rolled-back'
| null;
const rows = listBetas(stateFilter ?? undefined).map((b) => {
const script = getScript(b.script_id);
const version = getVersion(b.script_version_id);
const scripts = listBetaScripts(b.id).map((bs) => {
const script = getScript(bs.script_id);
const version = getVersion(bs.script_version_id);
return {
id: bs.script_id,
name: script?.name ?? '(deleted)',
target_rel_path: script?.target_rel_path ?? '',
version: version
? { id: version.id, version: version.version, sha256: version.sha256 }
: null
};
});
return {
...b,
script: script ? { id: script.id, name: script.name, target_rel_path: script.target_rel_path } : null,
version: version ? { id: version.id, version: version.version, sha256: version.sha256 } : null,
scripts,
servers: listBetaServers(b.id)
};
});
return json({ betas: rows });
}
// POST body: { scriptId, scriptVersionId, servers[], waitForHealth, waitForMatch, triggerKind, notes? }
// POST body: { scripts: [{ scriptId, scriptVersionId }], servers[], waitForHealth, waitForMatch, notes? }
export async function POST({ request }: { request: Request }) {
const body = (await request.json().catch(() => ({}))) as {
scriptId?: number;
scriptVersionId?: number;
scripts?: Array<{ scriptId?: number; scriptVersionId?: number }>;
servers?: string[];
waitForHealth?: boolean;
waitForMatch?: boolean;
triggerKind?: BetaTriggerKind;
notes?: string;
};
if (
typeof body.scriptId !== 'number' ||
typeof body.scriptVersionId !== 'number' ||
!Array.isArray(body.servers)
) {
if (!Array.isArray(body.scripts) || body.scripts.length === 0 || !Array.isArray(body.servers)) {
return json(
{ ok: false, error: 'scriptId, scriptVersionId, and servers[] are required' },
{ ok: false, error: 'scripts[] (non-empty) and servers[] are required' },
{ status: 400 }
);
}
if (body.servers.length === 0) {
return json({ ok: false, error: 'at least one server is required' }, { status: 400 });
}
const triggerKind: BetaTriggerKind = body.triggerKind ?? 'unscoped';
const normalized: Array<{ scriptId: number; scriptVersionId: number }> = [];
for (const s of body.scripts) {
if (typeof s.scriptId !== 'number' || typeof s.scriptVersionId !== 'number') {
return json(
{ ok: false, error: 'each scripts[] entry needs scriptId and scriptVersionId' },
{ status: 400 }
);
}
normalized.push({ scriptId: s.scriptId, scriptVersionId: s.scriptVersionId });
}
try {
const result = await openBeta({
scriptId: body.scriptId,
scriptVersionId: body.scriptVersionId,
scripts: normalized,
servers: body.servers,
waitForHealth: body.waitForHealth ?? true,
waitForMatch: body.waitForMatch ?? false,
triggerKind,
notes: body.notes
});
void kick();
+14 -4
View File
@@ -1,5 +1,5 @@
import { json } from '@sveltejs/kit';
import { getBeta, listBetaServers } from '$lib/server/fleet/beta.js';
import { getBeta, listBetaServers, listBetaScripts } from '$lib/server/fleet/beta.js';
import { getScript, getVersion } from '$lib/server/fleet/scripts.js';
function parseId(raw: string): number | null {
@@ -13,9 +13,19 @@ export async function GET({ params }: { params: { id: string } }) {
const beta = getBeta(id);
if (!beta) return json({ ok: false, error: 'not found' }, { status: 404 });
const script = getScript(beta.script_id);
const version = getVersion(beta.script_version_id);
const scripts = listBetaScripts(id).map((bs) => {
const script = getScript(bs.script_id);
const version = getVersion(bs.script_version_id);
return {
id: bs.script_id,
name: script?.name ?? '(deleted)',
target_rel_path: script?.target_rel_path ?? '',
version: version
? { id: version.id, version: version.version, sha256: version.sha256 }
: null
};
});
const servers = listBetaServers(id);
return json({ beta, script, version, servers });
return json({ beta, scripts, servers });
}
+28 -1
View File
@@ -57,8 +57,35 @@ export async function GET({ url }: { url: URL }) {
grouped.set(s.deployment_id, arr);
}
const scriptRows = db()
.prepare(
`SELECT ds.deployment_id, ds.script_id, s.name AS script_name,
sv.version AS script_version, pv.version AS prior_version
FROM deployment_scripts ds
LEFT JOIN scripts s ON s.id = ds.script_id
LEFT JOIN script_versions sv ON sv.id = ds.script_version_id
LEFT JOIN script_versions pv ON pv.id = ds.prior_version_id`
)
.all() as Array<{
deployment_id: string;
script_id: number;
script_name: string | null;
script_version: number | null;
prior_version: number | null;
}>;
const scriptsByDep = new Map<string, typeof scriptRows>();
for (const s of scriptRows) {
const arr = scriptsByDep.get(s.deployment_id) ?? [];
arr.push(s);
scriptsByDep.set(s.deployment_id, arr);
}
const rows = deployments
.map((d) => ({ ...d, servers: grouped.get(d.id) ?? [] }))
.map((d) => ({
...d,
servers: grouped.get(d.id) ?? [],
scripts: scriptsByDep.get(d.id) ?? []
}))
.filter((d) => (activeOnly ? d.servers.some((s) => s.restart_state === 'pending' || s.restart_state === 'failed') : true));
return json({ deployments: rows });
+15 -1
View File
@@ -24,5 +24,19 @@ export async function GET({ params }: { params: { id: string } }) {
)
.all(params.id);
return json({ deployment: dep, servers, files });
const scripts = db()
.prepare(
`SELECT ds.script_id, s.name AS script_name, s.target_rel_path,
ds.script_version_id, sv.version AS script_version,
ds.prior_version_id, pv.version AS prior_version
FROM deployment_scripts ds
LEFT JOIN scripts s ON s.id = ds.script_id
LEFT JOIN script_versions sv ON sv.id = ds.script_version_id
LEFT JOIN script_versions pv ON pv.id = ds.prior_version_id
WHERE ds.deployment_id = ?
ORDER BY ds.script_id`
)
.all(params.id);
return json({ deployment: dep, servers, files, scripts });
}