refactor: pass invocation options down instead of reading a global
This commit is contained in:
@@ -3,9 +3,10 @@ use std::{
|
||||
fmt::Display,
|
||||
os::unix::process::CommandExt,
|
||||
process::{Command, ExitStatus, Stdio},
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
pub struct Args(Vec<String>);
|
||||
|
||||
impl From<&str> for Args {
|
||||
@@ -53,8 +54,8 @@ impl CommandBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<Command> {
|
||||
if options().verbose {
|
||||
pub fn build(self, ctx: &Ctx) -> Result<Command> {
|
||||
if ctx.verbose {
|
||||
eprintln!("running `{self}`");
|
||||
}
|
||||
let (first, rest) = self.args.split_first().context("empty args")?;
|
||||
@@ -64,47 +65,47 @@ impl CommandBuilder {
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
pub fn exec_get_stdout(self) -> Result<String> {
|
||||
pub fn exec_get_stdout(self, ctx: &Ctx) -> Result<String> {
|
||||
let shown = self.to_string();
|
||||
let out = self.build()?.output()?;
|
||||
let out = self.build(ctx)?.output()?;
|
||||
|
||||
check(&shown, out.status)?;
|
||||
Ok(String::from_utf8(out.stdout)?)
|
||||
}
|
||||
|
||||
pub fn exec(self) -> Result<()> {
|
||||
if options().dry_run {
|
||||
pub fn exec(self, ctx: &Ctx) -> Result<()> {
|
||||
if ctx.dry_run {
|
||||
eprintln!("would run `{self}`");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let shown = self.to_string();
|
||||
let status = self.build()?.spawn()?.wait()?;
|
||||
let status = self.build(ctx)?.spawn()?.wait()?;
|
||||
|
||||
check(&shown, status)
|
||||
}
|
||||
|
||||
// replaces this process, so the command's exit code and signals become ours
|
||||
pub fn exec_replace(self) -> Result<()> {
|
||||
if options().dry_run {
|
||||
pub fn exec_replace(self, ctx: &Ctx) -> Result<()> {
|
||||
if ctx.dry_run {
|
||||
eprintln!("would run `{self}`");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let shown = self.to_string();
|
||||
let error = self.build()?.exec();
|
||||
let error = self.build(ctx)?.exec();
|
||||
|
||||
Err(error).with_context(|| format!("running `{shown}`"))
|
||||
}
|
||||
|
||||
pub fn exec_redirect_stdout(self, stdio: Stdio) -> Result<()> {
|
||||
if options().dry_run {
|
||||
pub fn exec_redirect_stdout(self, ctx: &Ctx, stdio: Stdio) -> Result<()> {
|
||||
if ctx.dry_run {
|
||||
eprintln!("would run `{self}`");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let shown = self.to_string();
|
||||
let status = self.build()?.stdout(stdio).spawn()?.wait()?;
|
||||
let status = self.build(ctx)?.stdout(stdio).spawn()?.wait()?;
|
||||
|
||||
check(&shown, status)
|
||||
}
|
||||
@@ -120,23 +121,3 @@ fn check(command: &str, status: ExitStatus) -> Result<()> {
|
||||
None => bail!("`{command}` was killed by a signal"),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct Options {
|
||||
pub verbose: bool,
|
||||
pub dry_run: bool,
|
||||
}
|
||||
|
||||
static OPTIONS: OnceLock<Options> = OnceLock::new();
|
||||
|
||||
pub fn set_options(options: Options) {
|
||||
let _ = OPTIONS.set(options);
|
||||
}
|
||||
|
||||
fn options() -> Options {
|
||||
OPTIONS.get().copied().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn is_dry_run() -> bool {
|
||||
options().dry_run
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::{Context, Result, anyhow, bail};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::command_builder::CommandBuilder;
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
const POSTGRES_IMAGES: [&str; 4] = ["postg", "timescale", "pgvector", "citus"];
|
||||
const DJANGO_SETTINGS_MODULE: &str = "DJANGO_SETTINGS_MODULE";
|
||||
@@ -11,10 +12,10 @@ pub struct Compose {
|
||||
}
|
||||
|
||||
impl Compose {
|
||||
pub fn resolve() -> Result<Self> {
|
||||
pub fn resolve(ctx: &Ctx) -> Result<Self> {
|
||||
let out = CommandBuilder::docker_compose()
|
||||
.args("config --format json")
|
||||
.build()?
|
||||
.build(ctx)?
|
||||
.output()
|
||||
.context("running docker compose config")?;
|
||||
|
||||
|
||||
5
src/ctx.rs
Normal file
5
src/ctx.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
// how ahab was invoked, handed to everything that runs commands or writes files
|
||||
pub struct Ctx {
|
||||
pub verbose: bool,
|
||||
pub dry_run: bool,
|
||||
}
|
||||
31
src/main.rs
31
src/main.rs
@@ -3,24 +3,27 @@ use std::process::ExitCode;
|
||||
mod cli;
|
||||
mod command_builder;
|
||||
mod compose;
|
||||
mod ctx;
|
||||
mod scripts;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = cli::Ahab::parse();
|
||||
|
||||
command_builder::set_options(command_builder::Options {
|
||||
let ctx = Ctx {
|
||||
verbose: args.verbose,
|
||||
dry_run: args.dry_run,
|
||||
});
|
||||
};
|
||||
|
||||
if args.dry_run {
|
||||
if ctx.dry_run {
|
||||
cli::reject_unsupported_dry_run(&args.command);
|
||||
}
|
||||
|
||||
match run(args.command) {
|
||||
match run(&ctx, args.command) {
|
||||
Ok(code) => code,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e:#}");
|
||||
@@ -29,31 +32,31 @@ fn main() -> ExitCode {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(command: cli::Commands) -> Result<ExitCode> {
|
||||
fn run(ctx: &Ctx, command: cli::Commands) -> Result<ExitCode> {
|
||||
let done = ExitCode::SUCCESS;
|
||||
|
||||
match command {
|
||||
cli::Commands::Django { command } => {
|
||||
match command {
|
||||
cli::Django::Bash => scripts::django::bash(),
|
||||
cli::Django::Run { rest } => scripts::django::run(&rest),
|
||||
cli::Django::Bash => scripts::django::bash(ctx),
|
||||
cli::Django::Run { rest } => scripts::django::run(ctx, &rest),
|
||||
cli::Django::MakeCommand { app, name } => {
|
||||
scripts::django::make_command(&app, &name)
|
||||
}
|
||||
cli::Django::Makemigrations => scripts::django::makemigrations(),
|
||||
cli::Django::Manage { rest } => scripts::django::manage(&rest),
|
||||
cli::Django::Migrate { rest } => scripts::django::migrate(&rest),
|
||||
cli::Django::Shell => scripts::django::shell(),
|
||||
cli::Django::Test => scripts::django::test(),
|
||||
cli::Django::Makemigrations => scripts::django::makemigrations(ctx),
|
||||
cli::Django::Manage { rest } => scripts::django::manage(ctx, &rest),
|
||||
cli::Django::Migrate { rest } => scripts::django::migrate(ctx, &rest),
|
||||
cli::Django::Shell => scripts::django::shell(ctx),
|
||||
cli::Django::Test => scripts::django::test(ctx),
|
||||
}?;
|
||||
|
||||
Ok(done)
|
||||
}
|
||||
cli::Commands::Postgres { command } => {
|
||||
match command {
|
||||
cli::Postgres::Import { path } => scripts::postgres::import(&path),
|
||||
cli::Postgres::Import { path } => scripts::postgres::import(ctx, &path),
|
||||
cli::Postgres::Dump { path, format, gzip } => {
|
||||
scripts::postgres::dump(&path, format, gzip)
|
||||
scripts::postgres::dump(ctx, &path, format, gzip)
|
||||
}
|
||||
}?;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
use crate::compose::Compose;
|
||||
use crate::ctx::Ctx;
|
||||
use crate::scripts::docker_compose;
|
||||
|
||||
const DEBUG_TEMPLATE: &str = r#"from django.core.management.base import BaseCommand
|
||||
@@ -53,40 +54,40 @@ pub fn make_command(app: &PathBuf, name: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn bash() -> Result<()> {
|
||||
run(&["bash".to_string()])
|
||||
pub fn bash(ctx: &Ctx) -> Result<()> {
|
||||
run(ctx, &["bash".to_string()])
|
||||
}
|
||||
|
||||
pub fn run(rest: &[String]) -> Result<()> {
|
||||
let service = Compose::resolve()?.django()?;
|
||||
docker_compose::run(&service, rest)
|
||||
pub fn run(ctx: &Ctx, rest: &[String]) -> Result<()> {
|
||||
let service = Compose::resolve(ctx)?.django()?;
|
||||
docker_compose::run(ctx, &service, rest)
|
||||
}
|
||||
|
||||
pub fn manage(rest: &[String]) -> Result<()> {
|
||||
pub fn manage(ctx: &Ctx, rest: &[String]) -> Result<()> {
|
||||
let mut args = vec!["python".to_string(), "manage.py".to_string()];
|
||||
args.extend_from_slice(rest);
|
||||
|
||||
run(&args)
|
||||
run(ctx, &args)
|
||||
}
|
||||
|
||||
// shortcuts
|
||||
pub fn makemigrations() -> Result<()> {
|
||||
manage(&["makemigrations".to_string()])
|
||||
pub fn makemigrations(ctx: &Ctx) -> Result<()> {
|
||||
manage(ctx, &["makemigrations".to_string()])
|
||||
}
|
||||
|
||||
pub fn migrate(rest: &[String]) -> Result<()> {
|
||||
pub fn migrate(ctx: &Ctx, rest: &[String]) -> Result<()> {
|
||||
let mut full_rest = vec!["migrate".to_string()];
|
||||
full_rest.extend_from_slice(rest);
|
||||
|
||||
manage(&full_rest)
|
||||
manage(ctx, &full_rest)
|
||||
}
|
||||
|
||||
pub fn shell() -> Result<()> {
|
||||
manage(&["shell".to_string()])
|
||||
pub fn shell(ctx: &Ctx) -> Result<()> {
|
||||
manage(ctx, &["shell".to_string()])
|
||||
}
|
||||
|
||||
pub fn test() -> Result<()> {
|
||||
manage(&["test".to_string()])
|
||||
pub fn test(ctx: &Ctx) -> Result<()> {
|
||||
manage(ctx, &["test".to_string()])
|
||||
}
|
||||
|
||||
fn safe_create_file(path: PathBuf) -> Result<File, std::io::Error> {
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::command_builder::CommandBuilder;
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
pub fn run(service: &str, rest: &[String]) -> Result<()> {
|
||||
pub fn run(ctx: &Ctx, service: &str, rest: &[String]) -> Result<()> {
|
||||
CommandBuilder::docker_compose()
|
||||
.args("run --rm")
|
||||
.arg(service)
|
||||
.args(rest)
|
||||
.exec_replace()
|
||||
.exec_replace(ctx)
|
||||
}
|
||||
|
||||
pub fn start(service: Option<&str>) -> Result<()> {
|
||||
pub fn start(ctx: &Ctx, service: Option<&str>) -> Result<()> {
|
||||
let mut command = CommandBuilder::docker_compose().args("start");
|
||||
if let Some(service) = service {
|
||||
command = command.arg(service);
|
||||
}
|
||||
|
||||
command.exec()
|
||||
command.exec(ctx)
|
||||
}
|
||||
|
||||
pub fn stop() -> Result<()> {
|
||||
CommandBuilder::docker_compose().args("stop").exec()
|
||||
pub fn stop(ctx: &Ctx) -> Result<()> {
|
||||
CommandBuilder::docker_compose().args("stop").exec(ctx)
|
||||
}
|
||||
|
||||
pub fn up() -> Result<()> {
|
||||
CommandBuilder::docker_compose().args("up -d").exec()
|
||||
pub fn up(ctx: &Ctx) -> Result<()> {
|
||||
CommandBuilder::docker_compose().args("up -d").exec(ctx)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::{
|
||||
|
||||
use super::docker_compose;
|
||||
use crate::cli::Format;
|
||||
use crate::command_builder;
|
||||
use crate::ctx::Ctx;
|
||||
use crate::{command_builder::CommandBuilder, compose::Compose};
|
||||
|
||||
const CUSTOM_MAGIC: &[u8] = b"PGDMP";
|
||||
@@ -37,15 +37,15 @@ struct Database {
|
||||
}
|
||||
|
||||
impl Database {
|
||||
fn resolve() -> Result<Self> {
|
||||
let compose = Compose::resolve()?;
|
||||
fn resolve(ctx: &Ctx) -> Result<Self> {
|
||||
let compose = Compose::resolve(ctx)?;
|
||||
let service = compose.postgres()?;
|
||||
let (user, name) = compose.postgres_credentials(&service);
|
||||
|
||||
let container = CommandBuilder::docker_compose()
|
||||
.args("ps -q")
|
||||
.arg(&service)
|
||||
.exec_get_stdout()?
|
||||
.exec_get_stdout(ctx)?
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
@@ -132,8 +132,10 @@ fn is_existing_role_error(line: &str) -> bool {
|
||||
line.starts_with("ERROR:") && line.contains("role \"") && line.ends_with("already exists")
|
||||
}
|
||||
|
||||
fn restore_cluster(db: &Database, script: &str, file: &Path) -> Result<()> {
|
||||
let out = piped(db, script, file)?.output().context("running psql")?;
|
||||
fn restore_cluster(ctx: &Ctx, db: &Database, script: &str, file: &Path) -> Result<()> {
|
||||
let out = piped(ctx, db, script, file)?
|
||||
.output()
|
||||
.context("running psql")?;
|
||||
|
||||
io::stdout().write_all(&out.stdout).ok();
|
||||
|
||||
@@ -187,21 +189,21 @@ fn pipefail(script: &str) -> String {
|
||||
format!("set -o pipefail; {script}")
|
||||
}
|
||||
|
||||
fn piped(db: &Database, script: &str, input: &Path) -> Result<std::process::Command> {
|
||||
fn piped(ctx: &Ctx, db: &Database, script: &str, input: &Path) -> Result<std::process::Command> {
|
||||
let file = File::open(input).with_context(|| format!("opening {}", input.display()))?;
|
||||
let mut command = CommandBuilder::docker()
|
||||
.args("exec -i")
|
||||
.arg(&db.container)
|
||||
.args("sh -c")
|
||||
.arg(script)
|
||||
.build()?;
|
||||
.build(ctx)?;
|
||||
|
||||
command.stdin(Stdio::from(file));
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
fn wait_until_ready(db: &Database) -> Result<()> {
|
||||
if command_builder::is_dry_run() {
|
||||
fn wait_until_ready(ctx: &Ctx, db: &Database) -> Result<()> {
|
||||
if ctx.dry_run {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -215,7 +217,7 @@ fn wait_until_ready(db: &Database) -> Result<()> {
|
||||
.arg(&db.user)
|
||||
.args("-d")
|
||||
.arg(&db.name)
|
||||
.build()?
|
||||
.build(ctx)?
|
||||
.stdout(Stdio::null())
|
||||
.spawn()?
|
||||
.wait()?
|
||||
@@ -237,30 +239,31 @@ fn wait_until_ready(db: &Database) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
fn when_ready(db: &Database, command: CommandBuilder) -> Result<()> {
|
||||
wait_until_ready(db)?;
|
||||
command.exec()
|
||||
fn when_ready(ctx: &Ctx, db: &Database, command: CommandBuilder) -> Result<()> {
|
||||
wait_until_ready(ctx, db)?;
|
||||
command.exec(ctx)
|
||||
}
|
||||
|
||||
fn in_container(db: &Database) -> CommandBuilder {
|
||||
CommandBuilder::docker().args("exec").arg(&db.container)
|
||||
}
|
||||
|
||||
pub fn import(file: &Path) -> Result<()> {
|
||||
pub fn import(ctx: &Ctx, file: &Path) -> Result<()> {
|
||||
let dump = Dump::of(file)?;
|
||||
let db = Database::resolve()?;
|
||||
let db = Database::resolve(ctx)?;
|
||||
|
||||
eprintln!("stopping all containers");
|
||||
docker_compose::stop()?;
|
||||
docker_compose::stop(ctx)?;
|
||||
|
||||
eprintln!("starting db container");
|
||||
docker_compose::start(Some(&db.service))?;
|
||||
docker_compose::start(ctx, Some(&db.service))?;
|
||||
|
||||
let remote = remote_dump();
|
||||
|
||||
// a directory cannot be streamed, so it is the one shape that gets copied in
|
||||
if matches!(dump, Dump::Directory) {
|
||||
when_ready(
|
||||
ctx,
|
||||
&db,
|
||||
CommandBuilder::docker()
|
||||
.args("cp -L")
|
||||
@@ -277,8 +280,8 @@ pub fn import(file: &Path) -> Result<()> {
|
||||
(kind, Some(db.restore_with(kind)))
|
||||
}
|
||||
Dump::Gzip => {
|
||||
wait_until_ready(&db)?;
|
||||
let out = piped(&db, &format!("gunzip -c | head -c {HEADER_LEN}"), file)?
|
||||
wait_until_ready(ctx, &db)?;
|
||||
let out = piped(ctx, &db, &format!("gunzip -c | head -c {HEADER_LEN}"), file)?
|
||||
.output()
|
||||
.context("reading the compressed dump's header")?;
|
||||
|
||||
@@ -312,6 +315,7 @@ pub fn import(file: &Path) -> Result<()> {
|
||||
eprintln!("restoring database with {tool}");
|
||||
|
||||
when_ready(
|
||||
ctx,
|
||||
&db,
|
||||
in_container(&db)
|
||||
.args("dropdb -U")
|
||||
@@ -323,6 +327,7 @@ pub fn import(file: &Path) -> Result<()> {
|
||||
// is already there
|
||||
if kind != Kind::Cluster {
|
||||
when_ready(
|
||||
ctx,
|
||||
&db,
|
||||
in_container(&db)
|
||||
.args("createdb -U")
|
||||
@@ -332,23 +337,23 @@ pub fn import(file: &Path) -> Result<()> {
|
||||
)?;
|
||||
}
|
||||
|
||||
wait_until_ready(&db)?;
|
||||
if command_builder::is_dry_run() {
|
||||
wait_until_ready(ctx, &db)?;
|
||||
if ctx.dry_run {
|
||||
eprintln!("would restore with {tool}");
|
||||
} else {
|
||||
match (kind, restore.as_deref()) {
|
||||
// psql's output is read rather than streamed here, to keep the expected
|
||||
// role errors out of the way
|
||||
(Kind::Cluster, Some(script)) => restore_cluster(&db, script, file)?,
|
||||
(Kind::Cluster, Some(script)) => restore_cluster(ctx, &db, script, file)?,
|
||||
(_, restore) => {
|
||||
let status = match restore {
|
||||
Some(script) => piped(&db, script, file)?.spawn()?.wait()?,
|
||||
Some(script) => piped(ctx, &db, script, file)?.spawn()?.wait()?,
|
||||
None => in_container(&db)
|
||||
.args("pg_restore -U")
|
||||
.arg(&db.user)
|
||||
.arg(format!("--dbname={}", db.name))
|
||||
.arg(&remote)
|
||||
.build()?
|
||||
.build(ctx)?
|
||||
.spawn()?
|
||||
.wait()?,
|
||||
};
|
||||
@@ -361,25 +366,25 @@ pub fn import(file: &Path) -> Result<()> {
|
||||
}
|
||||
|
||||
if matches!(dump, Dump::Directory) {
|
||||
let _ = in_container(&db).args("rm -rf").arg(&remote).exec();
|
||||
let _ = in_container(&db).args("rm -rf").arg(&remote).exec(ctx);
|
||||
}
|
||||
|
||||
eprintln!("restarting containers");
|
||||
docker_compose::stop()?;
|
||||
docker_compose::up()?;
|
||||
docker_compose::stop(ctx)?;
|
||||
docker_compose::up(ctx)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dump(file: &PathBuf, format: Format, gzip: bool) -> Result<()> {
|
||||
let db = Database::resolve()?;
|
||||
pub fn dump(ctx: &Ctx, file: &PathBuf, format: Format, gzip: bool) -> Result<()> {
|
||||
let db = Database::resolve(ctx)?;
|
||||
|
||||
if format == Format::Directory {
|
||||
if gzip {
|
||||
bail!("a directory dump is a directory of already compressed files, not a stream");
|
||||
}
|
||||
|
||||
return dump_directory(&db, file);
|
||||
return dump_directory(ctx, &db, file);
|
||||
}
|
||||
|
||||
eprintln!("dumping to local file {}", file.to_string_lossy());
|
||||
@@ -388,7 +393,7 @@ pub fn dump(file: &PathBuf, format: Format, gzip: bool) -> Result<()> {
|
||||
// cannot destroy the dump that is already there
|
||||
let partial = suffixed(file, ".partial");
|
||||
// a dry run produces no dump, so it must not lay a hand on the target either
|
||||
let stdout = if command_builder::is_dry_run() {
|
||||
let stdout = if ctx.dry_run {
|
||||
Stdio::null()
|
||||
} else {
|
||||
Stdio::from(
|
||||
@@ -403,7 +408,7 @@ pub fn dump(file: &PathBuf, format: Format, gzip: bool) -> Result<()> {
|
||||
.arg(&db.container)
|
||||
.args("sh -c")
|
||||
.arg(pipefail(&format!("{} | gzip", dump_command(&db, format))))
|
||||
.exec_redirect_stdout(stdout)
|
||||
.exec_redirect_stdout(ctx, stdout)
|
||||
} else {
|
||||
let dumping = match format.flag() {
|
||||
Some(flag) => in_container(&db)
|
||||
@@ -414,7 +419,7 @@ pub fn dump(file: &PathBuf, format: Format, gzip: bool) -> Result<()> {
|
||||
None => in_container(&db).args("pg_dumpall -U").arg(&db.user),
|
||||
};
|
||||
|
||||
dumping.exec_redirect_stdout(stdout)
|
||||
dumping.exec_redirect_stdout(ctx, stdout)
|
||||
};
|
||||
|
||||
if let Err(e) = dumped {
|
||||
@@ -422,7 +427,7 @@ pub fn dump(file: &PathBuf, format: Format, gzip: bool) -> Result<()> {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
if command_builder::is_dry_run() {
|
||||
if ctx.dry_run {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -442,7 +447,7 @@ fn dump_command(db: &Database, format: Format) -> String {
|
||||
|
||||
// pg_dump writes a directory format dump itself rather than to stdout, so it lands
|
||||
// in the container and comes back with docker cp
|
||||
fn dump_directory(db: &Database, target: &Path) -> Result<()> {
|
||||
fn dump_directory(ctx: &Ctx, db: &Database, target: &Path) -> Result<()> {
|
||||
if target.exists() {
|
||||
bail!(
|
||||
"{} already exists; a directory dump will not be written over it",
|
||||
@@ -459,15 +464,15 @@ fn dump_directory(db: &Database, target: &Path) -> Result<()> {
|
||||
.args("--format=d -f")
|
||||
.arg(&remote)
|
||||
.arg(&db.name)
|
||||
.exec()?;
|
||||
.exec(ctx)?;
|
||||
|
||||
let copied = CommandBuilder::docker()
|
||||
.args("cp")
|
||||
.arg(format!("{}:{remote}", db.container))
|
||||
.arg(target.to_string_lossy())
|
||||
.exec();
|
||||
.exec(ctx);
|
||||
|
||||
let _ = in_container(db).args("rm -rf").arg(&remote).exec();
|
||||
let _ = in_container(db).args("rm -rf").arg(&remote).exec(ctx);
|
||||
|
||||
copied
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user