feat: build commands from arguments instead of strings
This commit is contained in:
@@ -14,12 +14,6 @@ impl From<&str> for Args {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for Args {
|
||||
fn from(value: &String) -> Self {
|
||||
Self(Vec::from_iter(value.split_whitespace().map(String::from)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[String]> for Args {
|
||||
fn from(value: &[String]) -> Self {
|
||||
Self(value.to_vec())
|
||||
@@ -46,8 +40,8 @@ impl CommandBuilder {
|
||||
Self::default().args("docker compose")
|
||||
}
|
||||
|
||||
pub fn arg(mut self, arg: impl Into<String>) -> Self {
|
||||
self.args.push(arg.into());
|
||||
pub fn arg(mut self, arg: impl AsRef<str>) -> Self {
|
||||
self.args.push(arg.as_ref().to_string());
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
use crate::command_builder::CommandBuilder;
|
||||
use crate::compose::Compose;
|
||||
use crate::scripts::docker_compose;
|
||||
use crate::{create_file, safe_create_file};
|
||||
@@ -65,10 +64,10 @@ pub fn run(rest: &[String]) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn manage(rest: &[String]) -> Result<()> {
|
||||
let container = Compose::resolve()?.django()?;
|
||||
let joined = rest.join(" ");
|
||||
let command = format!("run --rm {container} python manage.py {joined}");
|
||||
CommandBuilder::docker_compose().args(&command).exec()
|
||||
let mut args = vec!["python".to_string(), "manage.py".to_string()];
|
||||
args.extend_from_slice(rest);
|
||||
|
||||
run(&args)
|
||||
}
|
||||
|
||||
// shortcuts
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn down() -> Result<()> {
|
||||
pub fn run(service: &str, rest: &[String]) -> Result<()> {
|
||||
CommandBuilder::docker_compose()
|
||||
.args("run --rm")
|
||||
.args(service)
|
||||
.arg(service)
|
||||
.args(rest)
|
||||
.exec()
|
||||
}
|
||||
@@ -21,7 +21,7 @@ pub fn run(service: &str, rest: &[String]) -> Result<()> {
|
||||
pub fn exec(service: &str, rest: &[String]) -> Result<()> {
|
||||
CommandBuilder::docker_compose()
|
||||
.args("exec")
|
||||
.args(service)
|
||||
.arg(service)
|
||||
.args(rest)
|
||||
.exec()
|
||||
}
|
||||
@@ -30,9 +30,13 @@ pub fn ps() -> Result<()> {
|
||||
CommandBuilder::docker_compose().args("ps").exec()
|
||||
}
|
||||
|
||||
pub fn start(containers: Option<&str>) -> Result<()> {
|
||||
let args = format!("start {}", containers.unwrap_or(""));
|
||||
CommandBuilder::docker_compose().args(&args).exec()
|
||||
pub fn start(service: Option<&str>) -> Result<()> {
|
||||
let mut command = CommandBuilder::docker_compose().args("start");
|
||||
if let Some(service) = service {
|
||||
command = command.arg(service);
|
||||
}
|
||||
|
||||
command.exec()
|
||||
}
|
||||
|
||||
pub fn stop() -> Result<()> {
|
||||
|
||||
@@ -40,7 +40,7 @@ impl Database {
|
||||
|
||||
let container = CommandBuilder::docker_compose()
|
||||
.args("ps -q")
|
||||
.args(&service)
|
||||
.arg(&service)
|
||||
.exec_get_stdout()?
|
||||
.trim()
|
||||
.to_string();
|
||||
@@ -173,7 +173,7 @@ fn piped(db: &Database, script: &str, input: &Path) -> Result<std::process::Comm
|
||||
let file = File::open(input).with_context(|| format!("opening {}", input.display()))?;
|
||||
let mut command = CommandBuilder::docker()
|
||||
.args("exec -i")
|
||||
.args(&db.container)
|
||||
.arg(&db.container)
|
||||
.args("sh -c")
|
||||
.arg(script)
|
||||
.build()?;
|
||||
@@ -185,10 +185,12 @@ fn piped(db: &Database, script: &str, input: &Path) -> Result<std::process::Comm
|
||||
fn wait_until_ready(db: &Database) -> Result<()> {
|
||||
debug_eprintln!("waiting until pg_isready");
|
||||
while !CommandBuilder::docker()
|
||||
.args(&format!(
|
||||
"exec {} pg_isready -U {} -d {}",
|
||||
db.container, db.user, db.name
|
||||
))
|
||||
.args("exec")
|
||||
.arg(&db.container)
|
||||
.args("pg_isready -U")
|
||||
.arg(&db.user)
|
||||
.args("-d")
|
||||
.arg(&db.name)
|
||||
.build()?
|
||||
.stdout(Stdio::null())
|
||||
.spawn()?
|
||||
@@ -201,9 +203,13 @@ fn wait_until_ready(db: &Database) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_when_ready(db: &Database, command: &str) -> Result<()> {
|
||||
fn when_ready(db: &Database, command: CommandBuilder) -> Result<()> {
|
||||
wait_until_ready(db)?;
|
||||
CommandBuilder::docker().args(command).exec()
|
||||
command.exec()
|
||||
}
|
||||
|
||||
fn in_container(db: &Database) -> CommandBuilder {
|
||||
CommandBuilder::docker().args("exec").arg(&db.container)
|
||||
}
|
||||
|
||||
pub fn import(file: &Path) -> Result<()> {
|
||||
@@ -220,9 +226,12 @@ pub fn import(file: &Path) -> Result<()> {
|
||||
|
||||
// a directory cannot be streamed, so it is the one shape that gets copied in
|
||||
if matches!(dump, Dump::Directory) {
|
||||
run_when_ready(
|
||||
when_ready(
|
||||
&db,
|
||||
&format!("cp -L {} {}:{remote}", file.display(), db.container),
|
||||
CommandBuilder::docker()
|
||||
.args("cp -L")
|
||||
.arg(file.to_string_lossy())
|
||||
.arg(format!("{}:{remote}", db.container)),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -264,20 +273,25 @@ pub fn import(file: &Path) -> Result<()> {
|
||||
|
||||
let tool = kind.tool();
|
||||
eprintln!("restoring database with {tool}");
|
||||
run_when_ready(
|
||||
|
||||
when_ready(
|
||||
&db,
|
||||
&format!("exec {} dropdb -U {} {}", db.container, db.user, db.name),
|
||||
in_container(&db)
|
||||
.args("dropdb -U")
|
||||
.arg(&db.user)
|
||||
.arg(&db.name),
|
||||
)?;
|
||||
|
||||
// a cluster dump creates the database itself, and would trip over one that
|
||||
// is already there
|
||||
if kind != Kind::Cluster {
|
||||
run_when_ready(
|
||||
when_ready(
|
||||
&db,
|
||||
&format!(
|
||||
"exec {} createdb -U {} -E utf8 -T template0 {}",
|
||||
db.container, db.user, db.name
|
||||
),
|
||||
in_container(&db)
|
||||
.args("createdb -U")
|
||||
.arg(&db.user)
|
||||
.args("-E utf8 -T template0")
|
||||
.arg(&db.name),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -289,11 +303,11 @@ pub fn import(file: &Path) -> Result<()> {
|
||||
(_, restore) => {
|
||||
let status = match restore {
|
||||
Some(script) => piped(&db, script, file)?.spawn()?.wait()?,
|
||||
None => CommandBuilder::docker()
|
||||
.args(&format!(
|
||||
"exec {} pg_restore -U {} --dbname={} {remote}",
|
||||
db.container, db.user, db.name
|
||||
))
|
||||
None => in_container(&db)
|
||||
.args("pg_restore -U")
|
||||
.arg(&db.user)
|
||||
.arg(format!("--dbname={}", db.name))
|
||||
.arg(&remote)
|
||||
.build()?
|
||||
.spawn()?
|
||||
.wait()?,
|
||||
@@ -306,9 +320,7 @@ pub fn import(file: &Path) -> Result<()> {
|
||||
}
|
||||
|
||||
if matches!(dump, Dump::Directory) {
|
||||
let _ = CommandBuilder::docker()
|
||||
.args(&format!("exec {} rm -rf {remote}", db.container))
|
||||
.exec();
|
||||
let _ = in_container(&db).args("rm -rf").arg(&remote).exec();
|
||||
}
|
||||
|
||||
eprintln!("restarting containers");
|
||||
@@ -332,19 +344,25 @@ pub fn dump(file: &PathBuf, format: Format, gzip: bool) -> Result<()> {
|
||||
eprintln!("dumping to local file {}", file.to_string_lossy());
|
||||
|
||||
let stdout = Stdio::from(File::create(file)?);
|
||||
let dumping = dump_command(&db, format);
|
||||
|
||||
if gzip {
|
||||
// the whole pipeline has to arrive as one shell argument
|
||||
CommandBuilder::docker()
|
||||
.args("exec")
|
||||
.args(&db.container)
|
||||
.arg(&db.container)
|
||||
.args("sh -c")
|
||||
.arg(format!("{dumping} | gzip"))
|
||||
.arg(format!("{} | gzip", dump_command(&db, format)))
|
||||
.exec_redirect_stdout(stdout)?;
|
||||
} else {
|
||||
CommandBuilder::docker()
|
||||
.args(&format!("exec {} {dumping}", db.container))
|
||||
.exec_redirect_stdout(stdout)?;
|
||||
let dumping = match format.flag() {
|
||||
Some(flag) => in_container(&db)
|
||||
.args("pg_dump -U")
|
||||
.arg(&db.user)
|
||||
.arg(format!("--format={flag}"))
|
||||
.arg(&db.name),
|
||||
None => in_container(&db).args("pg_dumpall -U").arg(&db.user),
|
||||
};
|
||||
|
||||
dumping.exec_redirect_stdout(stdout)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -371,24 +389,21 @@ fn dump_directory(db: &Database, target: &Path) -> Result<()> {
|
||||
eprintln!("dumping to local directory {}", target.display());
|
||||
let remote = remote_dump();
|
||||
|
||||
CommandBuilder::docker()
|
||||
.args(&format!(
|
||||
"exec {} pg_dump -U {} --format=d -f {remote} {}",
|
||||
db.container, db.user, db.name
|
||||
))
|
||||
in_container(db)
|
||||
.args("pg_dump -U")
|
||||
.arg(&db.user)
|
||||
.args("--format=d -f")
|
||||
.arg(&remote)
|
||||
.arg(&db.name)
|
||||
.exec()?;
|
||||
|
||||
let copied = CommandBuilder::docker()
|
||||
.args(&format!(
|
||||
"cp {}:{remote} {}",
|
||||
db.container,
|
||||
target.display()
|
||||
))
|
||||
.args("cp")
|
||||
.arg(format!("{}:{remote}", db.container))
|
||||
.arg(target.to_string_lossy())
|
||||
.exec();
|
||||
|
||||
let _ = CommandBuilder::docker()
|
||||
.args(&format!("exec {} rm -rf {remote}", db.container))
|
||||
.exec();
|
||||
let _ = in_container(db).args("rm -rf").arg(&remote).exec();
|
||||
|
||||
copied
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user