use std::fs; use std::process::Command; use rust_util::XResult; use rust_util::util_cmd; use crate::build_util; use crate::build_util::Builder; const DOCKER_CMD: &str = "docker"; const DCOLER_TEMP_CMD: &str = "temp_sub_cmd"; const DOCKER_WORK_DIR: &str = "/usr/src/app"; // $ docker run --rm --user "$(id -u)":"$(id -g)" \ // -v "$PWD":/usr/src/app -w /usr/src/app rust:1.47 \ // cargo build --release #[derive(Default)] pub struct DockerCmd { docker_name: String, docker_current_dir: Option, docker_run_uid: Option, docker_run_gid: Option, volumns: Vec<(String, String)>, mirror: Option, } impl DockerCmd { pub fn new(docker_name: &str) -> Self { Self { docker_name: docker_name.into(), ..Default::default() } } pub fn current_dir(&mut self, current_dir: &str) -> &mut Self { self.docker_current_dir = Some(current_dir.into()); self } pub fn uid(&mut self, uid: u32) -> &mut Self { self.docker_run_uid = Some(uid); self } pub fn gid(&mut self, gid: u32) -> &mut Self { self.docker_run_gid = Some(gid); self } pub fn mirror(&mut self, mirror: &str) -> &mut Self { self.mirror = Some(mirror.into()); self } pub fn add_volumn(&mut self, outer_vol: &str, docker_vol: &str) -> &mut Self { self.volumns.push((outer_vol.into(), docker_vol.into())); self } pub fn exec(self, cmds: &[String]) -> XResult<()> { let mut cmd = Command::new(DOCKER_CMD); cmd.arg("run"); cmd.arg("--rm"); cmd.arg("--user"); cmd.arg(&format!("{}:{}", self.docker_run_uid.unwrap_or_else(|| users::get_current_uid() as u32), self.docker_run_gid.unwrap_or_else(|| users::get_current_gid() as u32), )); cmd.arg("-v"); cmd.arg(&format!("{}:{}", build_util::get_work_dir(&self.docker_current_dir)?, DOCKER_WORK_DIR)); for (outer_vol, docker_vol) in self.volumns { cmd.arg("-v"); cmd.arg(&format!("{}:{}", outer_vol, docker_vol)); } cmd.args(vec!["-w", DOCKER_WORK_DIR]); cmd.arg(&self.docker_name); let builder = Builder::from(&self.docker_current_dir)?; let builder_name = match builder.get_name() { Some(n) => n, None => { warning!("Unknown builder, use default: cargo"); "cargo".into() // default use cargo? }, }; let mut sub_cmd = String::with_capacity(1024); if let Some(mirror) = self.mirror { sub_cmd.push_str(&make_cmd(&mirror)); sub_cmd.push_str("\n"); } let mut builder_cmd = builder_name; let mut sub_build_cmd = vec![]; let mut cmds_iter = cmds.iter(); if let Some(cmd0) = cmds_iter.next() { if cmd0.starts_with(":") { builder_cmd = cmd0.chars().skip(1).collect::(); } else { sub_build_cmd.push(cmd0.clone()); } } else { warning!("Docker build param is empty!"); } cmds_iter.for_each(|c| { sub_build_cmd.push(c.clone()); }); sub_cmd.push_str(&builder_cmd); sub_cmd.push(' '); sub_cmd.push_str(&sub_build_cmd.iter().map(|arg| escape_arg(arg)).collect::>().join(" ")); fs::write(DCOLER_TEMP_CMD, &sub_cmd).ok(); cmd.args(vec!["bash", DCOLER_TEMP_CMD]); success!("Docker cmd exec: {:?}", cmd); success!("Docker temp sub cmd: {}", sub_cmd); let r = util_cmd::run_command_and_wait(&mut cmd).map_err(|e| e.into()); fs::remove_file(DCOLER_TEMP_CMD).ok(); r } } fn escape_arg(arg: &str) -> String { let mut r = String::with_capacity(arg.len() + 10); r.push('\''); for c in arg.chars() { if c == '\'' { r.push('\\'); } r.push(c); } r.push('\''); r } fn make_cmd(mirror: &str) -> String { let mut s = String::with_capacity(256); s.push_str(&format!(r#"echo '[source.crates-io] registry = "https://github.com/rust-lang/crates.io-index" replace-with = "mirror" [source.mirror] registry = "{}" ' > /usr/local/cargo/config"#, mirror)); s }