zoi-package 1.24.0

Advanced Package Manager & Environment Orchestrator
Documentation
use anyhow::{Result, anyhow};
use colored::*;
use std::path::{Path, PathBuf};
use std::process::Command;
use zoi_core::utils;

pub fn run(
    package_file: &Path,
    build_type: Option<&str>,
    platforms: &[String],
    sign_key: Option<String>,
    output_dir: Option<&Path>,
    version_override: Option<&str>,
    sub_packages: Option<Vec<String>>,
    image: &str,
    fakeroot: bool,
    install_deps: bool,
    test: bool,
) -> Result<()> {
    println!("{} Building package using Docker...", "::".bold().blue());
    println!("Image: {}", image.cyan());

    if !utils::command_exists("docker") {
        return Err(anyhow!(
            "Docker is not installed or not in PATH. Please install Docker to use this method."
        ));
    }

    let abs_package_file = package_file.canonicalize()?;
    let package_dir = abs_package_file
        .parent()
        .ok_or_else(|| anyhow!("Could not get parent directory of package file"))?;

    let abs_output_dir = if let Some(dir) = output_dir {
        if !dir.exists() {
            std::fs::create_dir_all(dir)?;
        }
        dir.canonicalize()?
    } else {
        package_dir.to_path_buf()
    };

    let container_workdir = "/work";
    let container_output_dir = "/output";

    let mut docker_args = vec![
        "run".to_string(),
        "--rm".to_string(),
        "-v".to_string(),
        format!("{}:{}", package_dir.display(), container_workdir),
        "-v".to_string(),
        format!("{}:{}", abs_output_dir.display(), container_output_dir),
        "-w".to_string(),
        container_workdir.to_string(),
    ];

    if let Ok(user_id) = Command::new("id").arg("-u").output() {
        let uid = String::from_utf8_lossy(&user_id.stdout).trim().to_string();
        if let Ok(group_id) = Command::new("id").arg("-g").output() {
            let gid = String::from_utf8_lossy(&group_id.stdout).trim().to_string();
            docker_args.push("--user".to_string());
            docker_args.push(format!("{}:{}", uid, gid));
        }
    }

    if sign_key.is_some() {
        let host_gpg_home = std::env::var("GNUPGHOME")
            .map(PathBuf::from)
            .unwrap_or_else(|_| {
                utils::get_user_home()
                    .map(|h| h.join(".gnupg"))
                    .unwrap_or_default()
            });

        if host_gpg_home.exists() {
            let container_gpg_home = "/gpg_home";
            docker_args.push("-v".to_string());
            docker_args.push(format!(
                "{}:{}",
                host_gpg_home.display(),
                container_gpg_home
            ));
            docker_args.push("-e".to_string());
            docker_args.push(format!("GNUPGHOME={}", container_gpg_home));
        }
    }

    if let Ok(password) = std::env::var("GPG_PASSWORD") {
        docker_args.push("-e".to_string());
        docker_args.push(format!("GPG_PASSWORD={}", password));
    }

    docker_args.push(image.to_string());

    let package_filename = abs_package_file
        .file_name()
        .ok_or_else(|| anyhow!("Invalid package file name"))?
        .to_string_lossy();

    let mut inner_cmd = format!(
        "if ! command -v sudo >/dev/null 2>&1 && [ \"$(id -u)\" -eq 0 ]; then \
            if command -v pacman >/dev/null 2>&1; then pacman -Sy --noconfirm sudo gnupg; \
            elif command -v apt-get >/dev/null 2>&1; then apt-get update && apt-get install -y sudo gnupg; \
            elif command -v dnf >/dev/null 2>&1; then dnf install -y sudo gnupg; \
            elif command -v apk >/dev/null 2>&1; then apk add --update sudo gnupg; fi; \
         fi && \
         if command -v pacman >/dev/null 2>&1; then pacman -Sy --noconfirm base-devel git; \
         elif command -v apt-get >/dev/null 2>&1; then apt-get update && apt-get install -y build-essential git; \
         elif command -v dnf >/dev/null 2>&1; then dnf install -y @development-tools git; \
         elif command -v apk >/dev/null 2>&1; then apk add --update build-base git; fi && \
         curl -fsSL https://zillowe.pages.dev/scripts/zoi/install.sh | bash && \
         export PATH=\"$HOME/.local/bin:$PATH\" && \
         zoi sync && \
         zoi package build {} --output-dir {}",
        package_filename, container_output_dir
    );

    if let Some(bt) = build_type {
        inner_cmd.push_str(&format!(" --type {}", bt));
    }

    for p in platforms {
        inner_cmd.push_str(&format!(" --platform {}", p));
    }

    if let Some(sk) = sign_key {
        inner_cmd.push_str(&format!(" --sign {}", sk));
    }

    if let Some(v) = version_override {
        inner_cmd.push_str(&format!(" --version-override {}", v));
    }

    if let Some(subs) = sub_packages {
        for s in subs {
            inner_cmd.push_str(&format!(" --sub {}", s));
        }
    }

    if fakeroot {
        inner_cmd.push_str(" --fakeroot");
    }

    if install_deps {
        inner_cmd.push_str(" --install-deps");
    }

    if test {
        inner_cmd.push_str(" --test");
    }

    docker_args.push("bash".to_string());
    docker_args.push("-c".to_string());
    docker_args.push(inner_cmd);

    println!("Running docker command: {}", "docker".cyan());
    let status = Command::new("docker").args(&docker_args).status()?;

    if !status.success() {
        return Err(anyhow!(
            "Docker build failed with exit code {:?}",
            status.code()
        ));
    }

    println!("{}", "Docker build successful!".green());

    Ok(())
}