feat: implement --noconfirm

This commit is contained in:
steven-omaha
2023-05-12 16:05:49 +02:00
parent f03805446e
commit 7d9f444ea6
10 changed files with 94 additions and 29 deletions
+16 -3
View File
@@ -1,7 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::{Arg, ArgMatches, Command}; use clap::{Arg, ArgAction, ArgMatches, Command};
use path_absolutize::Absolutize; use path_absolutize::Absolutize;
use crate::action::*; use crate::action::*;
@@ -100,16 +100,22 @@ fn get_group_cmd() -> Command {
fn get_package_cmd() -> Command { fn get_package_cmd() -> Command {
let sync = Command::new(SYNC) let sync = Command::new(SYNC)
.about("install packages from all imported groups") .about("install packages from all imported groups")
.visible_alias("sy"); .visible_alias("sy")
.arg(build_noconfirm_arg());
let clean = Command::new(CLEAN) let clean = Command::new(CLEAN)
.about("remove unmanaged packages") .about("remove unmanaged packages")
.visible_alias("c"); .visible_alias("c")
.arg(build_noconfirm_arg());
let unmanaged = Command::new(UNMANAGED) let unmanaged = Command::new(UNMANAGED)
.about("show explicitly installed packages not managed by pacdef") .about("show explicitly installed packages not managed by pacdef")
.visible_alias("u"); .visible_alias("u");
let review = Command::new(REVIEW) let review = Command::new(REVIEW)
.about("review unmanaged packages") .about("review unmanaged packages")
.visible_alias("r"); .visible_alias("r");
let search = Command::new(SEARCH) let search = Command::new(SEARCH)
.visible_alias("se") .visible_alias("se")
.about("search for packages which match a provided regex") .about("search for packages which match a provided regex")
@@ -128,6 +134,13 @@ fn get_package_cmd() -> Command {
.subcommands([clean, review, unmanaged, search, sync]) .subcommands([clean, review, unmanaged, search, sync])
} }
fn build_noconfirm_arg() -> Arg {
Arg::new("noconfirm")
.long("noconfirm")
.help("do not ask for any confirmation")
.action(ArgAction::SetTrue)
}
/// Get and parse the CLI arguments. /// Get and parse the CLI arguments.
#[must_use] #[must_use]
pub fn get() -> clap::ArgMatches { pub fn get() -> clap::ArgMatches {
+11 -2
View File
@@ -21,6 +21,7 @@ const SECTION: Text = "arch";
const SWITCHES_INFO: Switches = &["--query", "--info"]; const SWITCHES_INFO: Switches = &["--query", "--info"];
const SWITCHES_INSTALL: Switches = &["--sync"]; const SWITCHES_INSTALL: Switches = &["--sync"];
const SWITCHES_MAKE_DEPENDENCY: Switches = &["--database", "--asdeps"]; const SWITCHES_MAKE_DEPENDENCY: Switches = &["--database", "--asdeps"];
const SWITCHES_NOCONFIRM: Switches = &["--noconfirm"];
const SWITCHES_REMOVE: Switches = &["--remove", "--recursive"]; const SWITCHES_REMOVE: Switches = &["--remove", "--recursive"];
const SUPPORTS_AS_DEPENDENCY: bool = true; const SUPPORTS_AS_DEPENDENCY: bool = true;
@@ -49,11 +50,15 @@ impl Backend for Arch {
} }
/// Install the specified packages. /// Install the specified packages.
fn install_packages(&self, packages: &[Package]) -> Result<ExitStatus> { fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<ExitStatus> {
let mut cmd = Command::new(&self.binary); let mut cmd = Command::new(&self.binary);
cmd.args(self.get_switches_install()); cmd.args(self.get_switches_install());
if noconfirm {
cmd.args(self.get_switches_noconfirm());
}
for p in packages { for p in packages {
cmd.arg(format!("{p}")); cmd.arg(format!("{p}"));
} }
@@ -63,7 +68,7 @@ impl Backend for Arch {
} }
/// Remove the specified packages. /// Remove the specified packages.
fn remove_packages(&self, packages: &[Package]) -> Result<ExitStatus> { fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<ExitStatus> {
let mut cmd = Command::new(&self.binary); let mut cmd = Command::new(&self.binary);
cmd.args(self.get_switches_remove()); cmd.args(self.get_switches_remove());
@@ -71,6 +76,10 @@ impl Backend for Arch {
cmd.args(rm_args); cmd.args(rm_args);
} }
if noconfirm {
cmd.args(self.get_switches_noconfirm());
}
for p in packages { for p in packages {
cmd.arg(format!("{p}")); cmd.arg(format!("{p}"));
} }
@@ -20,6 +20,7 @@ const SECTION: Text = "debian";
const SWITCHES_INFO: Switches = &["show"]; const SWITCHES_INFO: Switches = &["show"];
const SWITCHES_INSTALL: Switches = &["install"]; const SWITCHES_INSTALL: Switches = &["install"];
const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; // not needed const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; // not needed
const SWITCHES_NOCONFIRM: Switches = &["--yes"];
const SWITCHES_REMOVE: Switches = &["remove"]; const SWITCHES_REMOVE: Switches = &["remove"];
const SUPPORTS_AS_DEPENDENCY: bool = true; const SUPPORTS_AS_DEPENDENCY: bool = true;
@@ -20,6 +20,7 @@ const SECTION: Text = "python";
const SWITCHES_INFO: Switches = &["show"]; const SWITCHES_INFO: Switches = &["show"];
const SWITCHES_INSTALL: Switches = &["install"]; const SWITCHES_INSTALL: Switches = &["install"];
const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; // not needed const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; // not needed
const SWITCHES_NOCONFIRM: Switches = &[]; // not needed
const SWITCHES_REMOVE: Switches = &["uninstall"]; const SWITCHES_REMOVE: Switches = &["uninstall"];
const SUPPORTS_AS_DEPENDENCY: bool = false; const SUPPORTS_AS_DEPENDENCY: bool = false;
@@ -20,6 +20,7 @@ const SECTION: Text = "rust";
const SWITCHES_INSTALL: Switches = &["install"]; const SWITCHES_INSTALL: Switches = &["install"];
const SWITCHES_INFO: Switches = &["search", "--limit", "1"]; const SWITCHES_INFO: Switches = &["search", "--limit", "1"];
const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; const SWITCHES_MAKE_DEPENDENCY: Switches = &[];
const SWITCHES_NOCONFIRM: Switches = &[]; // not needed
const SWITCHES_REMOVE: Switches = &["uninstall"]; const SWITCHES_REMOVE: Switches = &["uninstall"];
const SUPPORTS_AS_DEPENDENCY: bool = false; const SUPPORTS_AS_DEPENDENCY: bool = false;
@@ -37,6 +37,10 @@ pub trait Backend: Debug {
/// Get CLI switches for the package manager to install packages. /// Get CLI switches for the package manager to install packages.
fn get_switches_install(&self) -> Switches; fn get_switches_install(&self) -> Switches;
/// Get CLI switches for the package manager to perform `sync` and `clean` without
/// confirmation.
fn get_switches_noconfirm(&self) -> Switches;
/// Get CLI switches for the package manager to remove packages. /// Get CLI switches for the package manager to remove packages.
fn get_switches_remove(&self) -> Switches; fn get_switches_remove(&self) -> Switches;
@@ -73,12 +77,18 @@ pub trait Backend: Debug {
} }
/// Install the specified packages. /// Install the specified packages.
fn install_packages(&self, packages: &[Package]) -> Result<ExitStatus> { fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<ExitStatus> {
let mut cmd = Command::new(self.get_binary()); let mut cmd = Command::new(self.get_binary());
cmd.args(self.get_switches_install()); cmd.args(self.get_switches_install());
if noconfirm {
cmd.args(self.get_switches_noconfirm());
}
for p in packages { for p in packages {
cmd.arg(format!("{p}")); cmd.arg(format!("{p}"));
} }
cmd.status() cmd.status()
.with_context(|| format!("running command {cmd:?}")) .with_context(|| format!("running command {cmd:?}"))
} }
@@ -92,20 +102,28 @@ pub trait Backend: Debug {
fn make_dependency(&self, packages: &[Package]) -> Result<ExitStatus> { fn make_dependency(&self, packages: &[Package]) -> Result<ExitStatus> {
let mut cmd = Command::new(self.get_binary()); let mut cmd = Command::new(self.get_binary());
cmd.args(self.get_switches_make_dependency()); cmd.args(self.get_switches_make_dependency());
for p in packages { for p in packages {
cmd.arg(format!("{p}")); cmd.arg(format!("{p}"));
} }
cmd.status() cmd.status()
.with_context(|| format!("running command [{cmd:?}]")) .with_context(|| format!("running command [{cmd:?}]"))
} }
/// Remove the specified packages. /// Remove the specified packages.
fn remove_packages(&self, packages: &[Package]) -> Result<ExitStatus> { fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<ExitStatus> {
let mut cmd = Command::new(self.get_binary()); let mut cmd = Command::new(self.get_binary());
cmd.args(self.get_switches_remove()); cmd.args(self.get_switches_remove());
if noconfirm {
cmd.args(self.get_switches_noconfirm());
}
for p in packages { for p in packages {
cmd.arg(format!("{p}")); cmd.arg(format!("{p}"));
} }
cmd.status() cmd.status()
.with_context(|| format!("running command [{cmd:?}]")) .with_context(|| format!("running command [{cmd:?}]"))
} }
+4
View File
@@ -19,6 +19,10 @@ macro_rules! impl_backend_constants {
SWITCHES_INSTALL SWITCHES_INSTALL
} }
fn get_switches_noconfirm(&self) -> Switches {
SWITCHES_NOCONFIRM
}
fn get_switches_remove(&self) -> Switches { fn get_switches_remove(&self) -> Switches {
SWITCHES_REMOVE SWITCHES_REMOVE
} }
@@ -35,31 +35,37 @@ impl ToDoPerBackend {
self.0.iter().all(|(_, diff)| diff.is_empty()) self.0.iter().all(|(_, diff)| diff.is_empty())
} }
pub(crate) fn install_missing_packages(&self) -> Result<()> { pub(crate) fn install_missing_packages(&self, noconfirm: bool) -> Result<()> {
self.handle_backend_command(Backend::install_packages, "install", "installing") self.handle_backend_command(
Backend::install_packages,
noconfirm,
"install",
"installing",
)
.context("installing packages") .context("installing packages")
} }
pub(crate) fn remove_unmanaged_packages(&self) -> Result<()> { pub(crate) fn remove_unmanaged_packages(&self, noconfirm: bool) -> Result<()> {
self.handle_backend_command(Backend::remove_packages, "remove", "removing") self.handle_backend_command(Backend::remove_packages, noconfirm, "remove", "removing")
.context("removing packages") .context("removing packages")
} }
fn handle_backend_command<'a, F>( fn handle_backend_command<'a, F>(
&'a self, &'a self,
func: F, func: F,
noconfirm: bool,
verb: &'_ str, verb: &'_ str,
verb_continuous: &'_ str, verb_continuous: &'_ str,
) -> Result<()> ) -> Result<()>
where where
F: Fn(&'a dyn Backend, &'a [Package]) -> Result<ExitStatus>, F: Fn(&'a dyn Backend, &'a [Package], bool) -> Result<ExitStatus>,
{ {
for (backend, packages) in &self.0 { for (backend, packages) in &self.0 {
if packages.is_empty() { if packages.is_empty() {
continue; continue;
} }
let exit_status = func(&**backend, packages).with_context(|| { let exit_status = func(&**backend, packages, noconfirm).with_context(|| {
format!("{verb_continuous} packages for {}", backend.get_section()) format!("{verb_continuous} packages for {}", backend.get_section())
})?; })?;
+26 -14
View File
@@ -71,13 +71,13 @@ impl Pacdef {
}, },
Some(("package", args)) => match args.subcommand() { Some(("package", args)) => match args.subcommand() {
Some((CLEAN, _)) => self.clean_packages(), Some((CLEAN, args)) => self.clean_packages(&args.clone()),
Some((REVIEW, _)) => review::review(self.get_unmanaged_packages()?, self.groups) Some((REVIEW, _)) => review::review(self.get_unmanaged_packages()?, self.groups)
.context("review unmanaged packages"), .context("review unmanaged packages"),
Some((SEARCH, args)) => { Some((SEARCH, args)) => {
search::search_packages(args, &self.groups).context("searching packages") search::search_packages(args, &self.groups).context("searching packages")
} }
Some((SYNC, _)) => self.install_packages(), Some((SYNC, args)) => self.install_packages(&args.clone()), // TODO fix cloning
Some((UNMANAGED, _)) => self.show_unmanaged_packages(), Some((UNMANAGED, _)) => self.show_unmanaged_packages(),
Some((_, _)) => panic!("{ACTION_NOT_MATCHED}"), Some((_, _)) => panic!("{ACTION_NOT_MATCHED}"),
@@ -130,7 +130,7 @@ impl Pacdef {
} }
} }
fn install_packages(&mut self) -> Result<()> { fn install_packages(&mut self, args: &ArgMatches) -> Result<()> {
let to_install = self.get_missing_packages()?; let to_install = self.get_missing_packages()?;
if to_install.nothing_to_do_for_all_backends() { if to_install.nothing_to_do_for_all_backends() {
@@ -141,12 +141,18 @@ impl Pacdef {
println!("Would install the following packages:\n"); println!("Would install the following packages:\n");
to_install.show().context("printing things to do")?; to_install.show().context("printing things to do")?;
println!(); let noconfirm = *args
if !get_user_confirmation()? { .get_one::<bool>("noconfirm")
return Ok(()); .expect("has a default value");
};
to_install.install_missing_packages() println!();
if noconfirm {
println!("proceeding without confirmation");
} else if !get_user_confirmation()? {
return Ok(());
}
to_install.install_missing_packages(noconfirm)
} }
fn edit_group_files(&self, arg_matches: &ArgMatches) -> Result<()> { fn edit_group_files(&self, arg_matches: &ArgMatches) -> Result<()> {
@@ -220,7 +226,7 @@ impl Pacdef {
} }
} }
fn clean_packages(mut self) -> Result<()> { fn clean_packages(&mut self, args: &ArgMatches) -> Result<()> {
let to_remove = self.get_unmanaged_packages()?; let to_remove = self.get_unmanaged_packages()?;
if to_remove.nothing_to_do_for_all_backends() { if to_remove.nothing_to_do_for_all_backends() {
@@ -231,12 +237,18 @@ impl Pacdef {
println!("Would remove the following packages:\n"); println!("Would remove the following packages:\n");
to_remove.show().context("printing things to do")?; to_remove.show().context("printing things to do")?;
println!(); let noconfirm = *args
if !get_user_confirmation()? { .get_one::<bool>("noconfirm")
return Ok(()); .expect("has a default value");
};
to_remove.remove_unmanaged_packages() println!();
if noconfirm {
println!("proceeding without confirmation");
} else if !get_user_confirmation()? {
return Ok(());
}
to_remove.remove_unmanaged_packages(noconfirm)
} }
fn show_group_content(&self, groups: &ArgMatches) -> Result<()> { fn show_group_content(&self, groups: &ArgMatches) -> Result<()> {
+1 -1
View File
@@ -30,7 +30,7 @@ impl Strategy {
pub(super) fn execute(self) -> Result<()> { pub(super) fn execute(self) -> Result<()> {
if !self.delete.is_empty() { if !self.delete.is_empty() {
ensure!(self.backend.remove_packages(&self.delete)?.success()); ensure!(self.backend.remove_packages(&self.delete, false)?.success());
} }
if !self.as_dependency.is_empty() { if !self.as_dependency.is_empty() {