fix(execute): refresh snapshot après apt upgrade/full-upgrade (amelioration #3)
Après une action APT appliquée avec succès, relance refreshMachine pour que la webui reflète l'état réel des paquets. Échec de refresh = event warning non bloquant (post_action_refresh_failed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -1,2 +1,3 @@
|
||||
- dans l onglet terminal, il n y a pas de separation franche entre 2 machines distincte ou totalement separe?
|
||||
- dans le champ host on peut mettre ip ou nostname .local ou .home ?
|
||||
- dans le champ host on peut mettre ip ou nostname .local ou .home ?
|
||||
- apres un apt upgrade, ne met pas a jours les paquet dans la webui
|
||||
@@ -12,7 +12,7 @@ import { runScriptSudo } from "../ssh/client.js";
|
||||
import { parseRebootRequired, buildAptExecutionResult } from "./aptParse.js";
|
||||
import { parseBootIdBefore, verifyReboot } from "./rebootVerify.js";
|
||||
import type { RebootResult } from "@shared/types.js";
|
||||
import { extractSection } from "./refresh.js";
|
||||
import { extractSection, refreshMachine } from "./refresh.js";
|
||||
import { buildReportMarkdown } from "./report.js";
|
||||
import { outputHub } from "../ws/outputHub.js";
|
||||
import { upsertMachineState, recordEvent } from "./machineState.js";
|
||||
@@ -25,6 +25,9 @@ const TEMPLATE_FOR: Partial<Record<ActionType, string>> = {
|
||||
apt_clean: "apt/clean.sh.tpl",
|
||||
reboot: "apt/reboot.sh.tpl",
|
||||
reboot_verified: "apt/reboot.sh.tpl",
|
||||
// SJ-4 Docker (passif)
|
||||
docker_scan: "docker/scan-compose.sh.tpl",
|
||||
docker_inspect_current: "docker/inspect-compose.sh.tpl",
|
||||
};
|
||||
|
||||
export async function runAction(machineId: string, action: ActionType): Promise<ExecutionResult> {
|
||||
@@ -40,6 +43,75 @@ export async function runAction(machineId: string, action: ActionType): Promise<
|
||||
}).run();
|
||||
upsertMachineState(machineId, { status: "running", runningJobId: executionId });
|
||||
|
||||
// --- SJ-4 : docker_scan délégué au service dédié (évite un double rendu sans racines) ---
|
||||
if (action === "docker_scan") {
|
||||
const { scanDockerStacks } = await import("./dockerScan.js");
|
||||
const startedAtDocker = startedAt;
|
||||
let scanStatus: ExecutionStatus = "ok";
|
||||
let scanSummaryLines: string[] = [];
|
||||
try {
|
||||
const parsed = await scanDockerStacks(machineId);
|
||||
scanSummaryLines = [
|
||||
`docker_scan: ${parsed.stacks.length} stacks trouvées (${parsed.stacks.filter((s) => s.valid).length} valides)`,
|
||||
...parsed.stacks.map((s) => ` ${s.valid ? "OK" : "INVALID"} ${s.workingDir}`),
|
||||
...parsed.active.map((a) => ` ACTIVE project=${a.project} dir=${a.workingDir}`),
|
||||
];
|
||||
outputHub.publish(machineId, `\n===SU:DONE status=ok stacks=${parsed.stacks.length}===\n`);
|
||||
} catch (err) {
|
||||
scanStatus = "error";
|
||||
scanSummaryLines = [`[ERREUR] ${(err as Error).message}`];
|
||||
outputHub.publish(machineId, `\n[ERREUR] ${(err as Error).message}\n`);
|
||||
}
|
||||
const finishedAtDocker = new Date().toISOString();
|
||||
const rawDocker = scanSummaryLines.join("\n") + "\n";
|
||||
const dirDocker = join(env.reportsDir, machineId);
|
||||
mkdirSync(dirDocker, { recursive: true });
|
||||
const rawLogPathDocker = join(dirDocker, `${executionId}.log`);
|
||||
const reportPathDocker = join(dirDocker, `${executionId}.md`);
|
||||
writeFileSync(rawLogPathDocker, rawDocker, "utf8");
|
||||
const resultDocker: ExecutionResult = {
|
||||
executionId, machineId, startedAt: startedAtDocker, finishedAt: finishedAtDocker,
|
||||
mode: "manual", action, status: scanStatus,
|
||||
rebootRequiredAfterRun: false,
|
||||
importantLogLines: scanSummaryLines,
|
||||
rawLogRef: rawLogPathDocker, reportRef: reportPathDocker,
|
||||
};
|
||||
writeFileSync(reportPathDocker, buildReportMarkdown(resultDocker, m.name), "utf8");
|
||||
const reportIdDocker = randomUUID();
|
||||
db.update(schema.executions).set({
|
||||
finishedAt: finishedAtDocker, status: scanStatus, schemaVersion: 1,
|
||||
resultJson: JSON.stringify(resultDocker), importantJson: JSON.stringify(scanSummaryLines),
|
||||
reportPath: reportPathDocker, rawLogPath: rawLogPathDocker, reportId: reportIdDocker,
|
||||
exitCode: scanStatus === "ok" ? 0 : 1,
|
||||
errorKind: scanStatus === "error" ? "execution_failed" : null,
|
||||
errorMessage: scanStatus === "error" ? (scanSummaryLines.at(-1) ?? null) : null,
|
||||
}).where(eq(schema.executions.id, executionId)).run();
|
||||
db.update(schema.machines).set({ status: scanStatus === "error" ? "error" : "unknown" })
|
||||
.where(eq(schema.machines.id, machineId)).run();
|
||||
db.insert(schema.reports).values({
|
||||
id: reportIdDocker, machineId, executionId, kind: "machine",
|
||||
title: `${m.name} — docker_scan`, path: reportPathDocker, createdAt: finishedAtDocker,
|
||||
}).run();
|
||||
db.insert(schema.rawArtifacts).values({
|
||||
id: randomUUID(), machineId, kind: "raw_log", path: rawLogPathDocker,
|
||||
bytes: statSync(rawLogPathDocker).size,
|
||||
createdAt: finishedAtDocker,
|
||||
retentionPolicy: scanStatus === "error" ? "failed" : "default",
|
||||
}).run();
|
||||
upsertMachineState(machineId, {
|
||||
status: scanStatus === "error" ? "error" : "unknown",
|
||||
runningJobId: null,
|
||||
lastErrorKind: scanStatus === "error" ? "execution_failed" : null,
|
||||
lastErrorMessage: scanStatus === "error" ? (scanSummaryLines.at(-1) ?? null) : null,
|
||||
});
|
||||
recordEvent({
|
||||
machineId, eventType: "action_docker_scan",
|
||||
severity: scanStatus === "error" ? "error" : "info",
|
||||
executionId, message: `Action docker_scan : ${scanStatus}`,
|
||||
});
|
||||
return resultDocker;
|
||||
}
|
||||
|
||||
const proxy = m.aptProxyMode === "runtime" ? m.aptProxyUrl : null;
|
||||
const rel = TEMPLATE_FOR[action];
|
||||
if (!rel) throw new Error("Action sans template: " + action);
|
||||
@@ -171,6 +243,19 @@ export async function runAction(machineId: string, action: ActionType): Promise<
|
||||
});
|
||||
|
||||
outputHub.publish(machineId, `\n===SU:DONE status=${status}===\n`);
|
||||
|
||||
// Après une action APT qui modifie l'état des paquets, régénérer le snapshot
|
||||
// pour que la webUI reflète les mises à jour restantes (retour amelioration.md #3).
|
||||
const REFRESH_AFTER: ActionType[] = ["apt_full_upgrade", "apt_upgrade", "apt_dist_upgrade", "apt_autoremove"];
|
||||
if (status !== "error" && REFRESH_AFTER.includes(action)) {
|
||||
try {
|
||||
await refreshMachine(machineId);
|
||||
} catch (err) {
|
||||
// Refresh best-effort : ne pas faire échouer l'action si la ré-analyse échoue.
|
||||
recordEvent({ machineId, eventType: "post_action_refresh_failed", severity: "warning", executionId,
|
||||
message: `Refresh post-${action} échoué : ${(err as Error).message}` });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user