/**
 * Alpine Linux image builder orchestration.
 */
import fs from "fs";
import path from "path";
import { copyExecutable, createInitramfs, createRootfsImage, downloadFile, findMke2fs, generateImageEnvScript, injectBeforeSandboxdExec, writeExecutable, } from "../alpine/utils.js";
import { extractTarGz } from "../alpine/tar.js";
import { assertSafeWritePath, ensureRootfsShell, hardenExtractedRootfs, } from "../alpine/rootfs.js";
import { exportOciRootfs } from "../alpine/oci.js";
import { installPackages, runPostBuildCommands } from "../alpine/packages.js";
import { applyPostBuildCopies } from "../alpine/post-build-copy.js";
import { syncKernelModules } from "../alpine/kernel-modules.js";
import { INITRAMFS_INIT_SCRIPT, ROOTFS_INIT_SCRIPT, } from "../alpine/init-scripts.js";
/** Build Alpine rootfs and initramfs images */
export async function buildAlpineImages(opts) {
    const { arch, alpineVersion, alpineBranch, rootfsPackages, initramfsPackages, postBuildCopy = [], postBuildCommands = [], sandboxdBin, sandboxfsBin, sandboxsshBin, sandboxingressBin, rootfsLabel, rootfsSizeMb, workDir, cacheDir, log, } = opts;
    const rootfsDir = path.join(workDir, "rootfs");
    const initramfsDir = path.join(workDir, "initramfs-root");
    const rootfsImage = path.join(workDir, "rootfs.ext4");
    const initramfsOut = path.join(workDir, "initramfs.cpio.lz4");
    const mkfsCmd = findMke2fs();
    fs.mkdirSync(cacheDir, { recursive: true });
    fs.mkdirSync(workDir, { recursive: true });
    const tarball = `alpine-minirootfs-${alpineVersion}-${arch}.tar.gz`;
    const tarballPath = path.join(cacheDir, tarball);
    const alpineUrl = opts.alpineUrl ??
        `https://dl-cdn.alpinelinux.org/alpine/${alpineBranch}/releases/${arch}/${tarball}`;
    if (!fs.existsSync(tarballPath)) {
        log(`Downloading ${alpineUrl}`);
        await downloadFile(alpineUrl, tarballPath);
    }
    fs.rmSync(rootfsDir, { recursive: true, force: true });
    fs.rmSync(initramfsDir, { recursive: true, force: true });
    fs.mkdirSync(rootfsDir, { recursive: true });
    fs.mkdirSync(initramfsDir, { recursive: true });
    let ociSource;
    let ociOwnershipEntries = [];
    if (opts.ociRootfs) {
        const runtimeLabel = opts.ociRootfs.runtime ?? "auto-detect";
        log(`Extracting OCI rootfs from ${opts.ociRootfs.image} (${runtimeLabel})...`);
        ociSource = exportOciRootfs({
            arch,
            workDir,
            targetDir: rootfsDir,
            log,
            ownershipSink: (entries) => {
                ociOwnershipEntries = entries;
            },
            ...opts.ociRootfs,
        });
        hardenExtractedRootfs(rootfsDir);
    }
    else {
        log("Extracting Alpine minirootfs for rootfs...");
        await extractTarGz(tarballPath, rootfsDir);
    }
    log("Extracting Alpine minirootfs for initramfs...");
    await extractTarGz(tarballPath, initramfsDir);
    if (rootfsPackages.length > 0) {
        log(`Installing rootfs packages: ${rootfsPackages.join(" ")}`);
        await installPackages(rootfsDir, rootfsPackages, arch, cacheDir, log);
    }
    if (initramfsPackages.length > 0) {
        log(`Installing initramfs packages: ${initramfsPackages.join(" ")}`);
        await installPackages(initramfsDir, initramfsPackages, arch, cacheDir, log);
    }
    if (opts.ociRootfs) {
        ensureRootfsShell(rootfsDir, opts.ociRootfs.image, initramfsDir, log);
    }
    if (postBuildCopy.length > 0) {
        applyPostBuildCopies(rootfsDir, postBuildCopy, log);
    }
    if (postBuildCommands.length > 0) {
        runPostBuildCommands(rootfsDir, postBuildCommands, arch, log);
    }
    copyExecutable(sandboxdBin, path.join(rootfsDir, "usr/bin/sandboxd"), rootfsDir);
    copyExecutable(sandboxfsBin, path.join(rootfsDir, "usr/bin/sandboxfs"), rootfsDir);
    copyExecutable(sandboxsshBin, path.join(rootfsDir, "usr/bin/sandboxssh"), rootfsDir);
    copyExecutable(sandboxingressBin, path.join(rootfsDir, "usr/bin/sandboxingress"), rootfsDir);
    let rootfsInitContent = opts.rootfsInit ?? ROOTFS_INIT_SCRIPT;
    const imageEnvScript = opts.defaultEnv
        ? generateImageEnvScript(opts.defaultEnv)
        : null;
    if (imageEnvScript) {
        const envPath = path.join(rootfsDir, "etc/profile.d/gondolin-image-env.sh");
        assertSafeWritePath(envPath, rootfsDir);
        fs.mkdirSync(path.dirname(envPath), { recursive: true });
        fs.writeFileSync(envPath, imageEnvScript, { mode: 0o644 });
        rootfsInitContent = injectBeforeSandboxdExec(rootfsInitContent, "# Load image default environment (generated by gondolin build)\n" +
            "if [ -r /etc/profile.d/gondolin-image-env.sh ]; then\n" +
            "  . /etc/profile.d/gondolin-image-env.sh\n" +
            "fi\n");
    }
    if (opts.rootfsInitExtra) {
        rootfsInitContent = injectBeforeSandboxdExec(rootfsInitContent, opts.rootfsInitExtra);
    }
    const initramfsInitContent = opts.initramfsInit ?? INITRAMFS_INIT_SCRIPT;
    writeExecutable(path.join(rootfsDir, "init"), rootfsInitContent, rootfsDir);
    writeExecutable(path.join(initramfsDir, "init"), initramfsInitContent);
    const python3 = path.join(rootfsDir, "usr/bin/python3");
    const python = path.join(rootfsDir, "usr/bin/python");
    if (fs.existsSync(python3) && !fs.existsSync(python)) {
        assertSafeWritePath(python, rootfsDir);
        fs.symlinkSync("python3", python);
    }
    ensureRuntimeDirs(rootfsDir);
    ensureRuntimeDirs(initramfsDir);
    syncKernelModules(rootfsDir, initramfsDir, log, {
        copyRootfsToInitramfs: !opts.ociRootfs,
    });
    fs.rmSync(path.join(rootfsDir, "boot"), { recursive: true, force: true });
    log("Creating rootfs ext4 image...");
    createRootfsImage(mkfsCmd, rootfsImage, rootfsDir, rootfsLabel, rootfsSizeMb, ociOwnershipEntries);
    log("Creating initramfs...");
    createInitramfs(initramfsDir, initramfsOut);
    log(`Rootfs image written to ${rootfsImage}`);
    log(`Initramfs written to ${initramfsOut}`);
    return {
        rootfsImage,
        initramfs: initramfsOut,
        ociSource,
    };
}
function ensureRuntimeDirs(rootDir) {
    for (const sub of ["proc", "sys", "dev", "run"]) {
        const targetDir = path.join(rootDir, sub);
        assertSafeWritePath(targetDir, rootDir);
        fs.mkdirSync(targetDir, { recursive: true });
    }
}
//# sourceMappingURL=alpine.js.map