drift update

This commit is contained in:
2026-09-12 23:15:14 -04:00
parent bb1e5c6a95
commit f10f0cefef
4 changed files with 437 additions and 39 deletions
+51 -12
View File
@@ -33,7 +33,13 @@
scripts: DepScript[];
}
interface DriftFile { relPath: string; size: number; sha256: string }
interface DriftFile {
relPath: string;
size: number;
sha256: string;
kind: 'script' | 'config' | 'plugin' | 'baseline';
driftKind: 'untracked' | 'missing';
}
interface DriftServer { server: string; files: DriftFile[] }
interface DriftPreview { totalDrift: number; servers: DriftServer[] }
@@ -61,9 +67,21 @@
}
}
let driftCounts = $derived.by(() => {
if (!drift) return { untracked: 0, missing: 0 };
let u = 0, m = 0;
for (const s of drift.servers) for (const f of s.files) {
if (f.driftKind === 'untracked') u++; else m++;
}
return { untracked: u, missing: m };
});
async function runDrift(): Promise<void> {
if (!drift || drift.totalDrift === 0) return;
if (!confirm(`Remove ${drift.totalDrift} untracked file(s) across ${drift.servers.length} server(s)?`)) return;
const parts: string[] = [];
if (driftCounts.untracked > 0) parts.push(`delete ${driftCounts.untracked} untracked file(s)`);
if (driftCounts.missing > 0) parts.push(`restore ${driftCounts.missing} missing artifact(s)`);
if (!confirm(`Reconcile drift on ${drift.servers.length} server(s)? Will ${parts.join(' and ')}.`)) return;
running = true;
flash = null;
try {
@@ -75,10 +93,20 @@
const body = (await res.json()) as {
ok: boolean;
error?: string;
result?: { deploymentId: string | null; totalRemoved: number; totalServers: number };
result?: {
deploymentId: string | null;
totalRemoved: number;
totalRestored?: number;
totalServers: number;
};
};
if (!body.ok) { flash = body.error ?? 'drift run failed'; return; }
flash = `removed ${body.result?.totalRemoved ?? 0} files across ${body.result?.totalServers ?? 0} servers`;
const removed = body.result?.totalRemoved ?? 0;
const restored = body.result?.totalRestored ?? 0;
const parts2: string[] = [];
if (removed > 0) parts2.push(`removed ${removed}`);
if (restored > 0) parts2.push(`restored ${restored}`);
flash = `${parts2.join(', ') || 'no'} file(s) across ${body.result?.totalServers ?? 0} server(s)`;
await Promise.all([loadDeployments(), scanDrift()]);
} finally {
running = false;
@@ -149,11 +177,11 @@
{:else}
<section class="drift-panel">
<div class="drift-head">
<h2>Drift{drift ? ` · ${drift.totalDrift} untracked file${drift.totalDrift === 1 ? '' : 's'}` : ''}</h2>
<h2>Drift{drift ? ` · ${driftCounts.untracked} untracked, ${driftCounts.missing} missing` : ''}</h2>
<div class="drift-actions">
<button onclick={scanDrift} disabled={scanning || running}>{scanning ? 'scanning…' : 'rescan'}</button>
<button class="primary" onclick={runDrift} disabled={running || scanning || !drift || drift.totalDrift === 0}>
{running ? 'removing…' : 'remove drift'}
{running ? 'reconciling…' : 'reconcile drift'}
</button>
</div>
</div>
@@ -164,10 +192,13 @@
{:else}
{#each drift.servers as s (s.server)}
<div class="drift-server">
<div class="drift-server-head mono">{s.server} · {s.files.length} file{s.files.length === 1 ? '' : 's'}</div>
<div class="drift-server-head mono">{s.server} · {s.files.length} entr{s.files.length === 1 ? 'y' : 'ies'}</div>
<div class="drift-files">
{#each s.files as f (f.relPath)}
<span class="drift-file mono" title="{f.size}B · {f.sha256.slice(0, 12)}">{f.relPath}</span>
{#each s.files as f (f.driftKind + ':' + f.kind + ':' + f.relPath)}
<span
class="drift-file mono {f.driftKind}"
title="[{f.kind}] {f.driftKind} · {f.size}B · {f.sha256.slice(0, 12)}"
>[{f.driftKind === 'missing' ? '+' : '−'} {f.kind}] {f.relPath}</span>
{/each}
</div>
</div>
@@ -363,12 +394,20 @@
.drift-server-head { color: var(--cream); font-size: var(--text-mono); margin-bottom: 6px; }
.drift-files { display: flex; flex-wrap: wrap; gap: 6px; }
.drift-file {
background: rgba(255, 48, 80, 0.08);
border: 1px solid rgba(255, 48, 80, 0.25);
color: var(--danger-500);
padding: 3px 8px;
border-radius: var(--r-sm);
font-size: var(--text-mono-sm);
border: 1px solid transparent;
}
.drift-file.untracked {
background: rgba(255, 48, 80, 0.08);
border-color: rgba(255, 48, 80, 0.25);
color: var(--danger-500);
}
.drift-file.missing {
background: rgba(255, 176, 32, 0.08);
border-color: rgba(255, 176, 32, 0.3);
color: var(--amber-500);
}
.flash { color: var(--spark-400); font-family: var(--font-mono); font-size: var(--text-mono); margin-top: 12px; }
.mono { font-family: var(--font-mono); }