use anyhow::{Result, anyhow};
use std::path::Path;
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>>,
fakeroot: bool,
install_deps: bool,
) -> Result<()> {
#[cfg(not(target_os = "linux"))]
{
let _ = (
package_file,
build_type,
platforms,
sign_key,
output_dir,
version_override,
sub_packages,
fakeroot,
install_deps,
);
return Err(anyhow!("Bubblewrap ('bwrap') is only supported on Linux."));
}
#[cfg(target_os = "linux")]
{
use colored::*;
use std::process::Command;
use zoi_core::utils;
println!(
"{} Building package using Bubblewrap sandbox...",
"::".bold().blue()
);
if !utils::command_exists("bwrap") {
return Err(anyhow!(
"Bubblewrap ('bwrap') is not installed or not in PATH. Please install it 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 zoi_exe = std::env::current_exe()?;
let zoi_exe_dir = zoi_exe
.parent()
.ok_or_else(|| anyhow!("Could not get zoi executable directory"))?;
let home_dir = home::home_dir().ok_or_else(|| anyhow!("Could not get home directory"))?;
let zoi_home = home_dir.join(".zoi");
let mut bwrap_args = vec![
"--ro-bind".to_string(),
"/usr".to_string(),
"/usr".to_string(),
"--symlink".to_string(),
"/usr/bin".to_string(),
"/bin".to_string(),
"--symlink".to_string(),
"/usr/lib".to_string(),
"/lib".to_string(),
"--symlink".to_string(),
"/usr/lib64".to_string(),
"/lib64".to_string(),
"--symlink".to_string(),
"/usr/sbin".to_string(),
"/sbin".to_string(),
"--ro-bind".to_string(),
"/etc".to_string(),
"/etc".to_string(),
"--dev".to_string(),
"/dev".to_string(),
"--proc".to_string(),
"/proc".to_string(),
"--tmpfs".to_string(),
"/tmp".to_string(),
"--bind".to_string(),
package_dir.display().to_string(),
container_workdir.to_string(),
"--bind".to_string(),
abs_output_dir.display().to_string(),
container_output_dir.to_string(),
"--ro-bind".to_string(),
zoi_exe_dir.display().to_string(),
"/zoi_bin".to_string(),
"--chdir".to_string(),
container_workdir.to_string(),
"--unshare-all".to_string(),
"--share-net".to_string(),
"--hostname".to_string(),
"zoi-build".to_string(),
"--setenv".to_string(),
"PATH".to_string(),
"/zoi_bin:/usr/bin:/bin:/usr/sbin:/sbin".to_string(),
];
if zoi_home.exists() {
bwrap_args.push("--ro-bind".to_string());
bwrap_args.push(zoi_home.display().to_string());
bwrap_args.push(zoi_home.display().to_string());
}
let system_zoi = Path::new("/var/lib/zoi");
if system_zoi.exists() {
bwrap_args.push("--ro-bind".to_string());
bwrap_args.push(system_zoi.display().to_string());
bwrap_args.push(system_zoi.display().to_string());
}
if fakeroot {
bwrap_args.push("--uid".to_string());
bwrap_args.push("0".to_string());
bwrap_args.push("--gid".to_string());
bwrap_args.push("0".to_string());
} else {
let uid = nix::unistd::getuid().as_raw();
let gid = nix::unistd::getgid().as_raw();
bwrap_args.push("--uid".to_string());
bwrap_args.push(uid.to_string());
bwrap_args.push("--gid".to_string());
bwrap_args.push(gid.to_string());
}
bwrap_args.push("--setenv".to_string());
bwrap_args.push("HOME".to_string());
bwrap_args.push(home_dir.display().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!(
"zoi package build {} --output-dir {} --method native",
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");
}
bwrap_args.push("bash".to_string());
bwrap_args.push("-c".to_string());
bwrap_args.push(inner_cmd);
if !utils::is_admin() && fakeroot {
println!(
"{} Running with fakeroot via user namespaces...",
"::".bold().yellow()
);
}
let status = Command::new("bwrap").args(&bwrap_args).status()?;
if !status.success() {
return Err(anyhow!(
"Bubblewrap build failed with exit code {:?}",
status.code()
));
}
println!("{}", "Bubblewrap build successful!".green());
Ok(())
}
}