feat: silence progress with --quiet
This commit is contained in:
@@ -16,6 +16,10 @@ pub struct Ahab {
|
|||||||
#[arg(short, long, global = true)]
|
#[arg(short, long, global = true)]
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
|
|
||||||
|
/// Print only what was asked for, not the progress along the way
|
||||||
|
#[arg(short, long, global = true, conflicts_with = "verbose")]
|
||||||
|
pub quiet: bool,
|
||||||
|
|
||||||
/// Print the docker commands that would run, without running them
|
/// Print the docker commands that would run, without running them
|
||||||
#[arg(long, global = true)]
|
#[arg(long, global = true)]
|
||||||
pub dry_run: bool,
|
pub dry_run: bool,
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
use super::{Argv, Cmd};
|
use super::{Argv, Cmd};
|
||||||
|
|
||||||
fn compose() -> Argv {
|
// compose prints its own progress, which --quiet has to ask it to stop
|
||||||
Argv::new("docker").arg("compose")
|
fn compose(quiet: bool) -> Argv {
|
||||||
|
let argv = Argv::new("docker").arg("compose");
|
||||||
|
|
||||||
|
match quiet {
|
||||||
|
true => argv.flag("--progress", "quiet"),
|
||||||
|
false => argv,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// docker compose run --rm, which runs the image's entrypoint and so fixes up the
|
// docker compose run --rm, which runs the image's entrypoint and so fixes up the
|
||||||
@@ -22,7 +28,7 @@ impl Run {
|
|||||||
|
|
||||||
impl Cmd for Run {
|
impl Cmd for Run {
|
||||||
fn argv(&self) -> Argv {
|
fn argv(&self) -> Argv {
|
||||||
compose()
|
compose(false)
|
||||||
.arg("run")
|
.arg("run")
|
||||||
.arg("--rm")
|
.arg("--rm")
|
||||||
.arg(&self.service)
|
.arg(&self.service)
|
||||||
@@ -35,7 +41,7 @@ pub struct Config;
|
|||||||
|
|
||||||
impl Cmd for Config {
|
impl Cmd for Config {
|
||||||
fn argv(&self) -> Argv {
|
fn argv(&self) -> Argv {
|
||||||
compose().arg("config").flag("--format", "json")
|
compose(false).arg("config").flag("--format", "json")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,39 +60,37 @@ impl Ps {
|
|||||||
|
|
||||||
impl Cmd for Ps {
|
impl Cmd for Ps {
|
||||||
fn argv(&self) -> Argv {
|
fn argv(&self) -> Argv {
|
||||||
compose().arg("ps").arg("--quiet").arg(&self.service)
|
compose(false).arg("ps").arg("--quiet").arg(&self.service)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Up;
|
pub struct Up {
|
||||||
|
pub quiet: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl Cmd for Up {
|
impl Cmd for Up {
|
||||||
fn argv(&self) -> Argv {
|
fn argv(&self) -> Argv {
|
||||||
compose().arg("up").arg("--detach")
|
compose(self.quiet).arg("up").arg("--detach")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Start {
|
pub struct Start<'a> {
|
||||||
service: String,
|
pub service: &'a str,
|
||||||
|
pub quiet: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Start {
|
impl Cmd for Start<'_> {
|
||||||
pub fn service(service: &str) -> Self {
|
|
||||||
Self {
|
|
||||||
service: service.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cmd for Start {
|
|
||||||
fn argv(&self) -> Argv {
|
fn argv(&self) -> Argv {
|
||||||
compose().arg("start").arg(&self.service)
|
compose(self.quiet).arg("start").arg(self.service)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct Stop;
|
|
||||||
|
pub struct Stop {
|
||||||
|
pub quiet: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl Cmd for Stop {
|
impl Cmd for Stop {
|
||||||
fn argv(&self) -> Argv {
|
fn argv(&self) -> Argv {
|
||||||
compose().arg("stop")
|
compose(self.quiet).arg("stop")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub fn make_command(ctx: &Ctx, app: &PathBuf, name: &str) -> Result<()> {
|
|||||||
return Err(anyhow!("directory {app_name} does not exist"));
|
return Err(anyhow!("directory {app_name} does not exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
note!("found app {app_name}");
|
note!(ctx, "found app {app_name}");
|
||||||
|
|
||||||
let management_dir = app_dir.join("management");
|
let management_dir = app_dir.join("management");
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ pub fn make_command(ctx: &Ctx, app: &PathBuf, name: &str) -> Result<()> {
|
|||||||
create_dir(ctx, &management_dir)?;
|
create_dir(ctx, &management_dir)?;
|
||||||
touch(ctx, &management_dir.join("__init__.py"))?;
|
touch(ctx, &management_dir.join("__init__.py"))?;
|
||||||
|
|
||||||
note!("created module {app_name}.management")
|
note!(ctx, "created module {app_name}.management")
|
||||||
};
|
};
|
||||||
|
|
||||||
let commands_dir = management_dir.join("commands");
|
let commands_dir = management_dir.join("commands");
|
||||||
@@ -41,7 +41,7 @@ pub fn make_command(ctx: &Ctx, app: &PathBuf, name: &str) -> Result<()> {
|
|||||||
create_dir(ctx, &commands_dir)?;
|
create_dir(ctx, &commands_dir)?;
|
||||||
touch(ctx, &commands_dir.join("__init__.py"))?;
|
touch(ctx, &commands_dir.join("__init__.py"))?;
|
||||||
|
|
||||||
note!("created module {app_name}.management.commands")
|
note!(ctx, "created module {app_name}.management.commands")
|
||||||
};
|
};
|
||||||
|
|
||||||
write_new(
|
write_new(
|
||||||
@@ -50,7 +50,7 @@ pub fn make_command(ctx: &Ctx, app: &PathBuf, name: &str) -> Result<()> {
|
|||||||
DEBUG_TEMPLATE.as_bytes(),
|
DEBUG_TEMPLATE.as_bytes(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
note!("created command {app_name}.management.commands.{name}");
|
note!(ctx, "created command {app_name}.management.commands.{name}");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ pub fn add(ctx: &Ctx, paths: &[PathBuf], force: bool, store: Option<&Path>) -> R
|
|||||||
let mut failed = 0;
|
let mut failed = 0;
|
||||||
for path in paths {
|
for path in paths {
|
||||||
if let Err(e) = link_one(ctx, &repo, path, force, &report) {
|
if let Err(e) = link_one(ctx, &repo, path, force, &report) {
|
||||||
note!("error: {e:#}");
|
note!(ctx, "error: {e:#}");
|
||||||
failed += 1;
|
failed += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ pub fn restore(ctx: &Ctx, paths: &[PathBuf], all: bool, store: Option<&Path>) ->
|
|||||||
};
|
};
|
||||||
|
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
note!("nothing in the store for this repository");
|
note!(ctx, "nothing in the store for this repository");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ pub fn restore(ctx: &Ctx, paths: &[PathBuf], all: bool, store: Option<&Path>) ->
|
|||||||
let mut failed = 0;
|
let mut failed = 0;
|
||||||
for path in &paths {
|
for path in &paths {
|
||||||
if let Err(e) = restore_one(ctx, &repo, path, &report) {
|
if let Err(e) = restore_one(ctx, &repo, path, &report) {
|
||||||
note!("error: {e:#}");
|
note!(ctx, "error: {e:#}");
|
||||||
failed += 1;
|
failed += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,10 @@ fn repo_components(ctx: &Ctx, root: &Path) -> Result<PathBuf> {
|
|||||||
if let Some(components) = components_from_remote(&url) {
|
if let Some(components) = components_from_remote(&url) {
|
||||||
return Ok(components);
|
return Ok(components);
|
||||||
}
|
}
|
||||||
note!("could not parse git remote `{url}`, falling back to the checkout name");
|
note!(
|
||||||
|
ctx,
|
||||||
|
"could not parse git remote `{url}`, falling back to the checkout name"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = root
|
let name = root
|
||||||
|
|||||||
@@ -45,11 +45,12 @@ fn restore_cluster(ctx: &Ctx, db: &Database, restore: &dyn Cmd, file: &Path) ->
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
note!("{line}");
|
note!(ctx, "{line}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if existing > 0 {
|
if existing > 0 {
|
||||||
note!(
|
note!(
|
||||||
|
ctx,
|
||||||
"left {existing} existing role{} alone",
|
"left {existing} existing role{} alone",
|
||||||
if existing == 1 { "" } else { "s" }
|
if existing == 1 { "" } else { "s" }
|
||||||
);
|
);
|
||||||
@@ -66,11 +67,15 @@ pub fn import(ctx: &Ctx, file: &Path) -> Result<()> {
|
|||||||
let dump = Dump::of(file)?;
|
let dump = Dump::of(file)?;
|
||||||
let db = Database::resolve(ctx)?;
|
let db = Database::resolve(ctx)?;
|
||||||
|
|
||||||
note!("stopping all containers");
|
note!(ctx, "stopping all containers");
|
||||||
Stop.run(ctx)?;
|
Stop { quiet: ctx.quiet }.run(ctx)?;
|
||||||
|
|
||||||
note!("starting db container");
|
note!(ctx, "starting db container");
|
||||||
Start::service(&db.service).run(ctx)?;
|
Start {
|
||||||
|
service: &db.service,
|
||||||
|
quiet: ctx.quiet,
|
||||||
|
}
|
||||||
|
.run(ctx)?;
|
||||||
|
|
||||||
let remote = remote_dump();
|
let remote = remote_dump();
|
||||||
|
|
||||||
@@ -121,7 +126,7 @@ pub fn import(ctx: &Ctx, file: &Path) -> Result<()> {
|
|||||||
let restore = db.restore_with(kind);
|
let restore = db.restore_with(kind);
|
||||||
// the name of what actually runs, so the message cannot drift from it
|
// the name of what actually runs, so the message cannot drift from it
|
||||||
let tool = restore.argv().program().to_string();
|
let tool = restore.argv().program().to_string();
|
||||||
note!("restoring database with {tool}");
|
note!(ctx, "restoring database with {tool}");
|
||||||
|
|
||||||
when_ready(
|
when_ready(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -149,7 +154,7 @@ pub fn import(ctx: &Ctx, file: &Path) -> Result<()> {
|
|||||||
|
|
||||||
wait_until_ready(ctx, &db)?;
|
wait_until_ready(ctx, &db)?;
|
||||||
if ctx.dry_run {
|
if ctx.dry_run {
|
||||||
note!("would restore with {tool}");
|
note!(ctx, "would restore with {tool}");
|
||||||
} else {
|
} else {
|
||||||
// a directory dump was copied in whole, so pg_restore reads it from the
|
// a directory dump was copied in whole, so pg_restore reads it from the
|
||||||
// container; every other shape is fed in on stdin, through gunzip when it
|
// container; every other shape is fed in on stdin, through gunzip when it
|
||||||
@@ -190,9 +195,9 @@ pub fn import(ctx: &Ctx, file: &Path) -> Result<()> {
|
|||||||
let _ = Rm::recursive(&remote).in_container(&db.container).run(ctx);
|
let _ = Rm::recursive(&remote).in_container(&db.container).run(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
note!("restarting containers");
|
note!(ctx, "restarting containers");
|
||||||
Stop.run(ctx)?;
|
Stop { quiet: ctx.quiet }.run(ctx)?;
|
||||||
Up.run(ctx)?;
|
Up { quiet: ctx.quiet }.run(ctx)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -208,7 +213,7 @@ pub fn dump(ctx: &Ctx, file: &Path, format: Format, gzip: bool) -> Result<()> {
|
|||||||
return dump_directory(ctx, &db, file);
|
return dump_directory(ctx, &db, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
note!("dumping to local file {}", file.to_string_lossy());
|
note!(ctx, "dumping to local file {}", file.to_string_lossy());
|
||||||
|
|
||||||
// written beside the target and renamed once the dump succeeds, so a failure
|
// written beside the target and renamed once the dump succeeds, so a failure
|
||||||
// cannot destroy the dump that is already there
|
// cannot destroy the dump that is already there
|
||||||
@@ -259,7 +264,7 @@ fn dump_directory(ctx: &Ctx, db: &Database, target: &Path) -> Result<()> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
note!("dumping to local directory {}", target.display());
|
note!(ctx, "dumping to local directory {}", target.display());
|
||||||
let remote = remote_dump();
|
let remote = remote_dump();
|
||||||
|
|
||||||
PgDump::new(&db.user, &db.name, "d")
|
PgDump::new(&db.user, &db.name, "d")
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
pub struct Ctx {
|
pub struct Ctx {
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
pub dry_run: bool,
|
pub dry_run: bool,
|
||||||
|
pub quiet: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ fn main() -> ExitCode {
|
|||||||
let ctx = Ctx {
|
let ctx = Ctx {
|
||||||
verbose: args.verbose,
|
verbose: args.verbose,
|
||||||
dry_run: args.dry_run,
|
dry_run: args.dry_run,
|
||||||
|
quiet: args.quiet,
|
||||||
};
|
};
|
||||||
|
|
||||||
// said once here rather than by each command, so every line that follows
|
// said once here rather than by each command, so every line that follows
|
||||||
// reads as the plan it is
|
// reads as the plan it is
|
||||||
if ctx.dry_run {
|
if ctx.dry_run {
|
||||||
note!("dry run, nothing will be changed");
|
note!(ctx, "dry run, nothing will be changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
match run(&ctx, args.command) {
|
match run(&ctx, args.command) {
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
// progress and warnings go to stderr, so what a caller pipes is only ever the
|
// progress, on stderr and only when it was asked for
|
||||||
// data a command was asked for
|
|
||||||
macro_rules! note {
|
macro_rules! note {
|
||||||
($($arg:tt)*) => { eprintln!($($arg)*) };
|
($ctx:expr, $($arg:tt)*) => {
|
||||||
|
if !$ctx.quiet {
|
||||||
|
eprintln!($($arg)*)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// not progress: it speaks about the result, so --quiet keeps it
|
||||||
macro_rules! warning {
|
macro_rules! warning {
|
||||||
($($arg:tt)*) => { eprintln!("warning: {}", format_args!($($arg)*)) };
|
($($arg:tt)*) => { eprintln!("warning: {}", format_args!($($arg)*)) };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user