feat: socle BDD (tâche 1.9 Phase 1-2) + moteur APT (tâche 2 SJ-0→3) + WIP capabilities/auth/Rust
Checkpoint multi-chantiers (arbre vert : tsc 0 erreur, 70 tests, build OK). - tâche 1.9 Phase 1 : schéma socle (machine_state/events/reports/raw_artifacts/ hardware/metrics + colonnes étendues) + wiring refresh/execute. Migration 0002. - tâche 1.9 Phase 2 : machine_credentials + machine_host_keys (non destructif, dual-read + backfill). Migration 0003. Fix séquence journal de migration. - tâche 2 : SJ-0 (types étendus rétro-compatibles, réducteur Docker, resolveTemplate), SJ-1 (update-analyze enrichi), SJ-2 (apply + diff dpkg + timeout inactivité SSH), SJ-3 (reboot vérifié boot_id). - WIP parallèle inclus : /api/capabilities, auth/apiTokens/apiClients, system metrics, scaffold app_rust, ajustements frontend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// server/templates/aptReduce.test.ts
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { reduceAptLines } from "./aptReduce.js";
|
||||
import { reduceAptLines, reduceLines } from "./aptReduce.js";
|
||||
|
||||
describe("reduceAptLines", () => {
|
||||
it("ne garde que les lignes utiles", () => {
|
||||
@@ -26,4 +26,22 @@ describe("reduceAptLines", () => {
|
||||
it("retourne un tableau vide si rien d'utile", () => {
|
||||
expect(reduceAptLines("Reading package lists...\nDone")).toEqual([]);
|
||||
});
|
||||
|
||||
it("garde aussi les lignes Docker utiles", () => {
|
||||
const raw = [
|
||||
"Pulling jellyfin ...",
|
||||
"Status: Downloaded newer image for jellyfin/jellyfin:latest",
|
||||
"Recreating jellyfin ...",
|
||||
"Started jellyfin",
|
||||
"blabla inutile",
|
||||
"Total reclaimed space: 1.2GB",
|
||||
].join("\n");
|
||||
expect(reduceLines(raw)).toEqual([
|
||||
"Pulling jellyfin ...",
|
||||
"Status: Downloaded newer image for jellyfin/jellyfin:latest",
|
||||
"Recreating jellyfin ...",
|
||||
"Started jellyfin",
|
||||
"Total reclaimed space: 1.2GB",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
// server/templates/aptReduce.ts
|
||||
const PREFIXES = ["Inst ", "Conf ", "Remv ", "Err ", "E:", "W:", "dpkg:"];
|
||||
const CONTAINS = ["reboot-required", "REBOOT_REQUIRED"];
|
||||
const PREFIXES = [
|
||||
// APT / dpkg (jalon 1)
|
||||
"Inst ", "Conf ", "Remv ", "Err ", "E:", "W:", "dpkg:",
|
||||
// Docker (SJ-0)
|
||||
"Pulling", "Digest", "Status", "Downloaded newer image", "Recreating", "Started", "Error",
|
||||
];
|
||||
const CONTAINS = [
|
||||
"reboot-required", "REBOOT_REQUIRED",
|
||||
"deleted", "Total reclaimed space",
|
||||
];
|
||||
|
||||
/** Garde uniquement les lignes informatives d'une sortie APT brute. */
|
||||
export function reduceAptLines(raw: string): string[] {
|
||||
/** Garde uniquement les lignes informatives (APT + Docker) d'une sortie brute. */
|
||||
export function reduceLines(raw: string): string[] {
|
||||
return raw
|
||||
.split("\n")
|
||||
.map((l) => l.trimEnd())
|
||||
.filter((l) => PREFIXES.some((p) => l.startsWith(p)) || CONTAINS.some((c) => l.includes(c)));
|
||||
}
|
||||
|
||||
/** Alias rétro-compatible (jalon 1) : même comportement, conserve les imports existants. */
|
||||
export const reduceAptLines = reduceLines;
|
||||
|
||||
@@ -14,4 +14,10 @@ describe("renderTemplate", () => {
|
||||
const out = renderTemplate("apt/check.sh.tpl", { aptProxy: "http://cache:3142" });
|
||||
expect(out).toContain('http_proxy="http://cache:3142"');
|
||||
});
|
||||
|
||||
it("rend update-analyze.sh.tpl avec les sections attendues", () => {
|
||||
const out = renderTemplate("apt/update-analyze.sh.tpl", { aptProxy: null });
|
||||
expect(out).toContain("===SU:APT_SIM_DISTUPGRADE===");
|
||||
expect(out).toContain("apt-mark showhold");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// server/templates/render.ts
|
||||
import Mustache from "mustache";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { readFileSync, existsSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const TEMPLATES_ROOT = resolve(process.cwd(), "templates");
|
||||
@@ -14,3 +14,23 @@ export function renderTemplate(relPath: string, vars: TemplateVars): string {
|
||||
// Mustache échappe le HTML par défaut; on désactive (ce sont des scripts shell).
|
||||
return Mustache.render(tpl, vars, {}, { escape: (s) => s });
|
||||
}
|
||||
|
||||
/** Existence par défaut d'un template relatif à templates/. */
|
||||
function defaultExists(rel: string): boolean {
|
||||
return existsSync(resolve(TEMPLATES_ROOT, rel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Résout le chemin de template le plus spécifique pour (action, OS) :
|
||||
* `<osFamily>/<action>.sh.tpl` s'il existe, sinon fallback base `apt/<action>.sh.tpl`.
|
||||
* `exists` est injectable pour les tests.
|
||||
*/
|
||||
export function resolveTemplate(
|
||||
action: string,
|
||||
osFamily: string,
|
||||
exists: (rel: string) => boolean = defaultExists,
|
||||
): string {
|
||||
const specific = `${osFamily}/${action}.sh.tpl`;
|
||||
if (osFamily !== "unknown" && osFamily !== "apt" && exists(specific)) return specific;
|
||||
return `apt/${action}.sh.tpl`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { resolveTemplate } from "./render.js";
|
||||
|
||||
describe("resolveTemplate", () => {
|
||||
it("retombe sur apt/ quand aucun dossier OS spécifique n'existe (fonction exists fournie)", () => {
|
||||
const noneExist = () => false;
|
||||
expect(resolveTemplate("full-upgrade", "proxmox", noneExist)).toBe("apt/full-upgrade.sh.tpl");
|
||||
expect(resolveTemplate("update-analyze", "debian", noneExist)).toBe("apt/update-analyze.sh.tpl");
|
||||
});
|
||||
|
||||
it("choisit le template OS spécifique quand il existe", () => {
|
||||
const proxmoxExists = (rel: string) => rel === "proxmox/full-upgrade.sh.tpl";
|
||||
expect(resolveTemplate("full-upgrade", "proxmox", proxmoxExists)).toBe("proxmox/full-upgrade.sh.tpl");
|
||||
});
|
||||
|
||||
it("unknown retombe toujours sur apt/", () => {
|
||||
const all = () => true;
|
||||
expect(resolveTemplate("clean", "unknown", all)).toBe("apt/clean.sh.tpl");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user