node-app-build 6.4.1

Mini app developer CLI: scaffold, validate, package node-app-* Debian packages
//! Docker fallback for `node-app package`.
//!
//! When the host doesn't have `dpkg-deb` (macOS, Windows, or stripped-down
//! Linux), we run the dpkg-deb step inside a `debian:stable-slim` container.
//! The staging tree is bind-mounted read-only at `/staging`, the output
//! directory is bind-mounted writable at `/out`, and the container
//! installs `dpkg-dev`, copies the staged tree into a writable scratch
//! directory (so dpkg-deb can adjust ownership without complaining about
//! the read-only mount), then runs `dpkg-deb --build`.
//!
//! Phase 4c of econ-v1/node#868 — restores the Docker path that the
//! pre-PR-#874 implementation had but that #874's bun-only refactor
//! dropped.

use anyhow::{bail, Context, Result};
use std::path::Path;
use std::process::Command;

const DOCKER_IMAGE: &str = "debian:stable-slim";

pub fn run_in_container(staging: &Path, deb_path: &Path) -> Result<()> {
    let stage_abs = staging
        .canonicalize()
        .with_context(|| format!("canonicalize staging dir {}", staging.display()))?;
    let deb_parent_raw = deb_path
        .parent()
        .ok_or_else(|| anyhow::anyhow!("deb output path has no parent: {}", deb_path.display()))?;
    let deb_parent = deb_parent_raw
        .canonicalize()
        .with_context(|| format!("canonicalize output dir {}", deb_parent_raw.display()))?;
    let deb_name = deb_path
        .file_name()
        .ok_or_else(|| anyhow::anyhow!("deb output path has no filename"))?
        .to_string_lossy()
        .to_string();

    let staging_mount = format!("{}:/staging:ro", stage_abs.display());
    let out_mount = format!("{}:/out", deb_parent.display());
    let inner = format!(
        "set -e; \
         apt-get update -qq >/dev/null && \
         apt-get install -y -qq dpkg-dev >/dev/null && \
         cp -r /staging /tmp/build && \
         dpkg-deb --root-owner-group --build /tmp/build /out/{}",
        deb_name
    );

    println!("→ dpkg-deb (docker, {})", DOCKER_IMAGE);
    let status = Command::new("docker")
        .args([
            "run",
            "--rm",
            "-v",
            &staging_mount,
            "-v",
            &out_mount,
            DOCKER_IMAGE,
            "bash",
            "-c",
            &inner,
        ])
        .status()
        .with_context(|| {
            "failed to run `docker run` — is Docker installed and running? \
             (On macOS: `colima start` or open Docker Desktop.)"
        })?;

    if !status.success() {
        bail!(
            "Docker-fallback dpkg-deb build failed (container exit: {})",
            status
        );
    }
    Ok(())
}