use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, clap::Args)]
pub struct BuildArgs {
#[clap(long, short = 't')]
pub tag: String,
#[clap(long, short = 'f')]
pub file: Option<String>,
#[clap(long, default_value = "auto")]
pub network: String,
#[clap(long)]
pub no_cache: bool,
#[clap(long = "dns-backend", value_name = "BACKEND")]
pub dns_backend: Option<String>,
#[clap(long = "build-arg")]
pub build_arg: Vec<String>,
#[clap(default_value = ".")]
pub context: String,
}
pub fn cmd_build(args: BuildArgs) -> Result<(), Box<dyn std::error::Error>> {
use pelagos::build;
use pelagos::network::NetworkMode;
if let Some(ref backend) = args.dns_backend {
unsafe { std::env::set_var("PELAGOS_DNS_BACKEND", backend) };
}
let context_dir = PathBuf::from(&args.context)
.canonicalize()
.map_err(|e| format!("cannot access build context '{}': {}", args.context, e))?;
let remfile_path = if let Some(ref f) = args.file {
PathBuf::from(f)
} else {
context_dir.join("Remfile")
};
if !remfile_path.is_file() {
return Err(format!(
"Remfile not found: {} (use -f to specify a different path)",
remfile_path.display()
)
.into());
}
let content = std::fs::read_to_string(&remfile_path)?;
let instructions = build::parse_remfile(&content)?;
if instructions.is_empty() {
return Err("Remfile is empty".into());
}
let network_mode = match args.network.as_str() {
"bridge" => NetworkMode::Bridge,
"pasta" => NetworkMode::Pasta,
"none" => NetworkMode::Loopback,
"auto" => {
if pelagos::paths::is_rootless() {
NetworkMode::Pasta
} else {
NetworkMode::Bridge
}
}
name => {
let config = pelagos::paths::network_config_dir(name).join("config.json");
if config.exists() {
NetworkMode::BridgeNamed(name.to_string())
} else {
return Err(format!(
"unknown network '{}' — use a mode (none, bridge, pasta, auto) \
or create it first: pelagos network create {} --subnet CIDR",
name, name
)
.into());
}
}
};
let mut build_args_map = HashMap::new();
for arg in &args.build_arg {
if let Some((k, v)) = arg.split_once('=') {
build_args_map.insert(k.to_string(), v.to_string());
} else {
build_args_map.insert(arg.clone(), String::new());
}
}
eprintln!("Building {} from {}", args.tag, remfile_path.display());
let manifest = build::execute_build(
&instructions,
&context_dir,
&args.tag,
network_mode,
!args.no_cache,
&build_args_map,
)?;
eprintln!(
"Successfully built {} ({} layers)",
args.tag,
manifest.layers.len()
);
Ok(())
}