Files
proxdeploy/tests/engine/shellcheck.test.ts
florian.wildeandClaude Opus 5.5 01d22aac3a feat(engine): Adapter debian und Profil-Engine
Profile sind deklarative Schritte je Distributionsfamilie; der Adapter
übersetzt sie in ein idempotentes Shell-Skript. Ein neuer Adapter (rhel,
alpine) ist eine Datei und ein Eintrag in der Registry.

Variablen werden typisiert geprüft, in run-Schritten jeder Wert für die
Shell gequotet. SSH-Härtung läuft zuletzt, damit sich eine Provisionierung
per SSH nicht selbst aussperrt.

Geprüft mit shellcheck und echt in Containern (Debian 12/13, Ubuntu
22.04/24.04/26.04) inklusive zweitem Lauf. Dabei aufgefallen: frische
Images haben keine Paketlisten – der Vorspann macht jetzt apt-get update.

Closes #005

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:53:38 +02:00

56 lines
2.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { execFileSync } from "node:child_process";
import { mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { debianAdapter } from "@/engine/adapter/debian";
import { katalogLaden } from "@/engine/katalog/laden";
import { profileAufloesen } from "@/engine/profile/aufloesen";
import { profileRendern } from "@/engine/profile/rendern";
/** shellcheck über Docker – läuft nur, wenn Docker und das Image da sind, und sagt das sonst. */
function shellcheckVerfuegbar(): boolean {
try {
execFileSync("docker", ["image", "inspect", "koalaman/shellcheck:stable"], { stdio: "ignore" });
return true;
} catch {
console.warn("shellcheck übersprungen: docker oder koalaman/shellcheck:stable fehlt");
return false;
}
}
describe.runIf(shellcheckVerfuegbar())("shellcheck", () => {
it("findet in den gerenderten Katalogprofilen nichts", async () => {
const katalog = await katalogLaden("catalog");
const profile = new Map(katalog.profile.map((p) => [p.eintrag.id, p.eintrag]));
const ordner = mkdtempSync(join(tmpdir(), "pd-sc-"));
for (const { eintrag: distro } of katalog.distributionen) {
const aufgeloest = profileAufloesen(["docker", "zabbix-agent"], profile, "debian", {
base: { fail2ban: true },
"zabbix-agent": { server: "zbx" },
});
if (!aufgeloest.ok) throw new Error("Auflösung fehlgeschlagen");
const { skript } = profileRendern(
aufgeloest.profile,
debianAdapter,
{ os: distro.os, gast: { name: "g", benutzer: "u" } },
[debianAdapter.benutzerAnlegen({ name: "u", sshSchluessel: ["ssh-ed25519 AAAA u"], sudo: true })],
);
writeFileSync(join(ordner, `${distro.id}.sh`), skript);
}
const ausgabe = (() => {
try {
execFileSync(
"bash",
["-c", `docker run --rm -v ${ordner}:/s:ro koalaman/shellcheck:stable -s bash /s/*.sh`],
{ stdio: "pipe" },
);
return "";
} catch (fehler) {
return String((fehler as { stdout?: Buffer }).stdout ?? fehler);
}
})();
expect(ausgabe).toBe("");
}, 60_000);
});