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