config and plugin

This commit is contained in:
2026-09-12 19:11:44 -04:00
parent 85bcb05a7d
commit 8c40bbeff1
19 changed files with 1685 additions and 86 deletions
+32 -8
View File
@@ -1,7 +1,7 @@
import { mkdir, rm, writeFile } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { destOf } from '../config.js';
import { destOfKind, type ArtifactKind } from '../config.js';
import { db } from '../db/index.js';
import { emit } from '../pipeline/bus.js';
import { recordScript, removeScript } from './inventory.js';
@@ -33,6 +33,7 @@ interface DepScriptRow {
script_id: number;
script_version_id: number;
prior_version_id: number | null;
kind: ArtifactKind;
}
interface FileRow {
@@ -49,6 +50,7 @@ interface FileRow {
interface ScriptTargetRow {
id: number;
target_rel_path: string;
kind: ArtifactKind;
}
function nowIso(): string {
@@ -91,7 +93,8 @@ export async function rollbackPromoteDeployment(promoteId: string): Promise<Roll
deployment_id: promoteId,
script_id: dep.script_id,
script_version_id: dep.script_version_id,
prior_version_id: dep.prior_version_id
prior_version_id: dep.prior_version_id,
kind: 'script'
}];
}
@@ -105,18 +108,32 @@ export async function rollbackPromoteDeployment(promoteId: string): Promise<Roll
// Map rel_path → scriptId (and prior_version_id) so we can restore the
// inventory row's script_version_id correctly during file restore.
const scriptByTarget = new Map<string, DepScriptRow>();
const kindByScriptId = new Map<number, ArtifactKind>();
if (scripts.length > 0) {
const placeholders = scripts.map(() => '?').join(',');
const targetRows = db()
.prepare(`SELECT id, target_rel_path FROM scripts WHERE id IN (${placeholders})`)
.prepare(`SELECT id, target_rel_path, kind FROM scripts WHERE id IN (${placeholders})`)
.all(...scripts.map((s) => s.script_id)) as ScriptTargetRow[];
const targetById = new Map<number, string>();
for (const t of targetRows) targetById.set(t.id, t.target_rel_path);
for (const t of targetRows) {
targetById.set(t.id, t.target_rel_path);
kindByScriptId.set(t.id, t.kind);
}
for (const s of scripts) {
const t = targetById.get(s.script_id);
if (t) scriptByTarget.set(t, s);
}
}
const kindForRelPath = (relPath: string): ArtifactKind => {
const owner = scriptByTarget.get(relPath);
if (owner) return owner.kind ?? kindByScriptId.get(owner.script_id) ?? 'script';
// Config/baseline aggregate rows have no per-artifact target_rel_path
// mapping — the deployment_files row is keyed on the shared cfg name.
// Any script/baseline in this deployment tagged as config/baseline
// means the rollback for that path lives under the config destination.
for (const s of scripts) if (s.kind === 'config' || s.kind === 'baseline') return 'config';
return 'script';
};
const newDepId = rollbackDeploymentId(promoteId);
const startedAt = nowIso();
@@ -133,9 +150,10 @@ export async function rollbackPromoteDeployment(promoteId: string): Promise<Roll
const perServerFailures = new Map<string, string>();
for (const [server, files] of perServer) {
const dest = destOf(server);
try {
for (const f of files) {
const kind = kindForRelPath(f.rel_path);
const dest = destOfKind(server, kind);
const abs = join(dest, f.rel_path);
await mkdir(join(dest, dirname(f.rel_path)), { recursive: true });
const owningScript = scriptByTarget.get(f.rel_path);
@@ -173,8 +191,8 @@ export async function rollbackPromoteDeployment(promoteId: string): Promise<Roll
VALUES (?, 'rollback', ?, 'ui', NULL, NULL, NULL, NULL)`
);
const insertDepScript = db().prepare(
`INSERT INTO deployment_scripts (deployment_id, script_id, script_version_id, prior_version_id)
VALUES (?, ?, ?, ?)`
`INSERT INTO deployment_scripts (deployment_id, script_id, script_version_id, prior_version_id, kind)
VALUES (?, ?, ?, ?, ?)`
);
const insertDepServer = db().prepare(
`INSERT INTO deployment_servers
@@ -193,7 +211,13 @@ export async function rollbackPromoteDeployment(promoteId: string): Promise<Roll
// after rollback is the promote's prior; the "prior" of the rollback
// is what was current up until this call (i.e. the promote's version).
for (const s of scripts) {
insertDepScript.run(newDepId, s.script_id, s.prior_version_id ?? s.script_version_id, s.script_version_id);
insertDepScript.run(
newDepId,
s.script_id,
s.prior_version_id ?? s.script_version_id,
s.script_version_id,
s.kind ?? 'script'
);
}
for (const [server, files] of perServer) {
if (perServerFailures.has(server)) {