Created
May 13, 2021 10:31
-
-
Save andreypopp/f704a5daacfd01604e7e95452e36c79d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use colored::*; | |
use exec; | |
use std::env; | |
use std::fs; | |
use std::io::{BufRead, BufReader}; | |
use std::path; | |
use std::process; | |
use std::process::{Command, Stdio}; | |
use std::sync::{Arc, Mutex}; | |
use std::thread; | |
use std::vec::Vec; | |
struct Workspace { | |
name: String, | |
path: path::PathBuf, | |
cwd: path::PathBuf, | |
} | |
fn current_workspace() -> Workspace { | |
let mut vars = env::vars(); | |
let path = vars.find_map( | |
|(name, value)| { | |
if name == "w__root" { | |
Some(value) | |
} else { | |
None | |
} | |
}, | |
); | |
let path = match path { | |
None => { | |
log_error("$w__root is not set"); | |
process::exit(1) | |
} | |
Some(path) => path::PathBuf::from(path), | |
}; | |
let cwd = fs::canonicalize(env::current_dir().unwrap()).unwrap(); | |
let name = cwd.file_name().unwrap().to_owned().into_string().unwrap(); | |
Workspace { name, path, cwd } | |
} | |
impl Workspace { | |
fn build(&self) { | |
let mut cmd = Command::new("podman"); | |
cmd.arg("build") | |
.arg(self.path.clone()) | |
.arg("-t") | |
.arg(self.name.clone()); | |
run_progress( | |
&mut cmd, | |
|line| println!("{}", line.white()), | |
|line| println!("{}", line.red()), | |
); | |
} | |
fn up(&self) { | |
let mut volume = String::new(); | |
volume.push_str(self.path.to_str().unwrap()); | |
volume.push_str(":"); | |
volume.push_str(self.path.to_str().unwrap()); | |
let mut cmd = Command::new("podman"); | |
cmd.arg("run") | |
.arg("--detach") | |
.arg("--replace") | |
.arg("--name") | |
.arg(self.name.clone()) | |
.arg("--volume") | |
.arg(volume) | |
.arg(self.name.clone()) | |
.arg("sleep") | |
.arg("infinity"); | |
run(&mut cmd); | |
} | |
fn exec(&self, args: &[String]) -> ! { | |
let mut cmd = exec::Command::new("podman"); | |
cmd.arg("exec") | |
.arg("-w") | |
.arg(self.cwd.as_path()) | |
.arg("-i") | |
.arg(self.name.clone()); | |
cmd.args(args); | |
let _ = cmd.exec(); | |
process::exit(1) | |
} | |
} | |
fn run_progress<OnStdout, OnStderr>(cmd: &mut Command, on_stdout: OnStdout, on_stderr: OnStderr) | |
where | |
OnStdout: Fn(String) + Send + 'static, | |
OnStderr: Fn(String) + Send + 'static, | |
{ | |
cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); | |
let mut c = cmd.spawn().expect("failed to start command"); | |
let out_reader = read_lines(c.stdout.take().unwrap(), on_stdout); | |
let err_reader = read_lines(c.stderr.take().unwrap(), on_stderr); | |
out_reader.join().expect("failed to read stdout"); | |
err_reader.join().expect("failed to read stderr"); | |
let exit_status = c.wait().expect("failed to execute command"); | |
if !exit_status.success() { | |
log_error("command failed:"); | |
process::exit(1) | |
} | |
} | |
/** | |
* Run command till completion, print stdout/stderr on log_error. | |
*/ | |
fn run(cmd: &mut Command) { | |
cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); | |
let mut c = cmd.spawn().expect("failed to start command"); | |
// Keep vector with collected stdout/stderr within a mutex and then refcounted. | |
let out = Arc::new(Mutex::new(Vec::new())); | |
// Start reading stdout in a separate thread. | |
let out_reader = { | |
let out = out.clone(); | |
read_lines( | |
c.stdout.take().expect("unable to get process stdout"), | |
move |line| { | |
out.lock().unwrap().push(line); | |
}, | |
) | |
}; | |
// Start reading stderr in a separate thread. | |
let err_reader = { | |
let out = out.clone(); | |
read_lines(c.stderr.take().unwrap(), move |line| { | |
out.lock().unwrap().push(line); | |
}) | |
}; | |
// Wait for both readers to finish. | |
out_reader.join().expect("failed to read stdout"); | |
err_reader.join().expect("failed to read stderr"); | |
let exit_status = c.wait().expect("failed to execute command"); | |
if !exit_status.success() { | |
log_error("command failed:"); | |
out.lock().unwrap().iter().for_each(|line| { | |
println!("{}", line.red()); | |
}); | |
process::exit(1) | |
} | |
} | |
fn read_lines<R, F>(oc: R, mut on_line: F) -> thread::JoinHandle<()> | |
where | |
R: std::io::Read + Send + 'static, | |
F: FnMut(String) + Send + 'static, | |
{ | |
let out_reader = BufReader::new(oc); | |
return thread::spawn(move || { | |
out_reader.lines().for_each(|line| { | |
let line = line.unwrap(); | |
on_line(line); | |
}) | |
}); | |
} | |
fn log_info(msg: &str) { | |
println!("{} {}", " [INFO]".cyan().bold(), msg.cyan().bold()); | |
} | |
fn log_error(msg: &str) { | |
println!("{} {}", "[ERROR]".red().bold(), msg.red().bold()); | |
} | |
fn main() { | |
let ws = current_workspace(); | |
let args = env::args(); | |
let args: Vec<_> = args.collect(); | |
match args[1].as_str() { | |
"build" => { | |
if !ws.path.join("Dockerfile").exists() && !ws.path.join("Containerfile").exists() { | |
log_error("no Dockefile/Containerfile found at the project root"); | |
process::exit(1); | |
} | |
log_info("building..."); | |
ws.build() | |
} | |
"exec" => { | |
ws.exec(&args[2..]); | |
} | |
"up" => { | |
log_info("staring development environment..."); | |
ws.up(); | |
} | |
_ => { | |
ws.exec(&args[1..]); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment