Skip to content

Commit d256d07

Browse files
committed
feat: add notification support to --ask flags
Change-Id: I6a6a69642ac3443525df583fd200827414801d41
1 parent 399db38 commit d256d07

File tree

9 files changed

+163
-53
lines changed

9 files changed

+163
-53
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ hostname = "0.4.1"
3838
humantime = "2.3.0"
3939
inquire = { default-features = false, version = "0.9.1", features = [ "crossterm" ] }
4040
nix = { default-features = false, features = [ "fs", "user" ], version = "0.30.1" }
41-
regex = "1.11.3"
4241
notify-rust = "4.11.7"
42+
regex = "1.11.3"
4343
reqwest = { default-features = false, features = [
4444
"rustls-tls-native-roots",
4545
"blocking",

src/clean.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ use yansi::{Color, Paint};
2020
use crate::{
2121
Result,
2222
commands::{Command, ElevationStrategy},
23-
interface,
23+
interface::{self, NotifyAskMode},
2424
nh_info,
25+
notify::NotificationSender,
2526
};
2627

2728
// Nix impl:
@@ -250,7 +251,7 @@ impl interface::CleanMode {
250251
Err(err) => {
251252
warn!(?err, ?now, "Failed to compare time!");
252253
},
253-
Ok(val) if val <= args.keep_since.into() => {
254+
Ok(val) if val <= std::time::Duration::from(args.keep_since) => {
254255
gcroots_tagged.insert(dst, false);
255256
},
256257
Ok(_) => {
@@ -334,12 +335,26 @@ impl interface::CleanMode {
334335
}
335336

336337
// Clean the paths
337-
if args.ask
338-
&& !Confirm::new("Confirm the cleanup plan?")
339-
.with_default(false)
340-
.prompt()?
341-
{
342-
bail!("User rejected the cleanup plan");
338+
if let Some(ask) = &args.ask {
339+
let confirmation = match ask {
340+
NotifyAskMode::Prompt => {
341+
Confirm::new("Confirm the cleanup plan?")
342+
.with_default(false)
343+
.prompt()?
344+
},
345+
#[cfg(all(unix, not(target_os = "macos")))]
346+
NotifyAskMode::Notify => {
347+
NotificationSender::new(
348+
"nh clean",
349+
"Do you want to confirm the cleanup plan?",
350+
)
351+
.ask()
352+
},
353+
};
354+
355+
if !confirmation {
356+
bail!("User rejected the cleanup plan");
357+
}
343358
}
344359

345360
if !args.dry {
@@ -489,7 +504,7 @@ fn cleanable_generations(
489504
Err(err) => {
490505
warn!(?err, ?now, ?generation, "Failed to compare time!");
491506
},
492-
Ok(val) if val <= keep_since.into() => {
507+
Ok(val) if val <= std::time::Duration::from(keep_since) => {
493508
*tbr = false;
494509
},
495510
Ok(_) => {},

src/darwin.rs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{env, path::PathBuf};
1+
use std::{env, fmt, path::PathBuf};
22

33
use color_eyre::eyre::{Context, bail, eyre};
44
use tracing::{debug, warn};
@@ -14,8 +14,10 @@ use crate::{
1414
DarwinReplArgs,
1515
DarwinSubcommand,
1616
DiffType,
17+
NotifyAskMode,
1718
},
1819
nixos::toplevel_for,
20+
notify::NotificationSender,
1921
update::update,
2022
util::{get_hostname, print_dix_diff},
2123
};
@@ -34,7 +36,7 @@ impl DarwinArgs {
3436
match self.subcommand {
3537
DarwinSubcommand::Switch(args) => args.rebuild(&Switch, elevation),
3638
DarwinSubcommand::Build(args) => {
37-
if args.common.ask || args.common.dry {
39+
if args.common.ask.is_some() || args.common.dry {
3840
warn!("`--ask` and `--dry` have no effect for `nh darwin build`");
3941
}
4042
args.rebuild(&Build, elevation)
@@ -49,6 +51,16 @@ enum DarwinRebuildVariant {
4951
Build,
5052
}
5153

54+
impl fmt::Display for DarwinRebuildVariant {
55+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56+
let s = match self {
57+
DarwinRebuildVariant::Build => "build",
58+
DarwinRebuildVariant::Switch => "switch",
59+
};
60+
write!(f, "{s}")
61+
}
62+
}
63+
5264
impl DarwinRebuildArgs {
5365
fn rebuild(
5466
self,
@@ -144,13 +156,27 @@ impl DarwinRebuildArgs {
144156
let _ = print_dix_diff(&PathBuf::from(CURRENT_PROFILE), &target_profile);
145157
}
146158

147-
if self.common.ask && !self.common.dry && !matches!(variant, Build) {
148-
let confirmation = inquire::Confirm::new("Apply the config?")
149-
.with_default(false)
150-
.prompt()?;
159+
if !self.common.dry && !matches!(variant, Build) {
160+
if let Some(ask) = self.common.ask {
161+
let confirmation = match ask {
162+
NotifyAskMode::Prompt => {
163+
inquire::Confirm::new("Apply the config?")
164+
.with_default(false)
165+
.prompt()?
166+
},
167+
#[cfg(all(unix, not(target_os = "macos")))]
168+
NotifyAskMode::Notify => {
169+
NotificationSender::new(
170+
&format!("nh darwin {variant}"),
171+
"Do you want to apply the Darwin configuration?",
172+
)
173+
.ask()
174+
},
175+
};
151176

152-
if !confirmation {
153-
bail!("User rejected the new config");
177+
if !confirmation {
178+
bail!("User rejected the new config");
179+
}
154180
}
155181
}
156182

src/home.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,16 @@ use crate::{
1010
commands,
1111
commands::Command,
1212
installable::Installable,
13-
interface::{self, DiffType, HomeRebuildArgs, HomeReplArgs, HomeSubcommand},
13+
interface::{
14+
self,
15+
DiffType,
16+
HomeRebuildArgs,
17+
HomeReplArgs,
18+
HomeSubcommand,
19+
NotifyAskMode,
20+
},
1421
nh_info,
22+
notify::NotificationSender,
1523
update::update,
1624
util::{get_hostname, print_dix_diff},
1725
};
@@ -27,7 +35,7 @@ impl interface::HomeArgs {
2735
match self.subcommand {
2836
HomeSubcommand::Switch(args) => args.rebuild(&Switch),
2937
HomeSubcommand::Build(args) => {
30-
if args.common.ask || args.common.dry {
38+
if args.common.ask.is_some() || args.common.dry {
3139
warn!("`--ask` and `--dry` have no effect for `nh home build`");
3240
}
3341
args.rebuild(&Build)
@@ -151,16 +159,28 @@ impl HomeRebuildArgs {
151159
}
152160

153161
if self.common.dry || matches!(variant, Build) {
154-
if self.common.ask {
162+
if self.common.ask.is_some() {
155163
warn!("--ask has no effect as dry run was requested");
156164
}
157165
return Ok(());
158166
}
159167

160-
if self.common.ask {
161-
let confirmation = inquire::Confirm::new("Apply the config?")
162-
.with_default(false)
163-
.prompt()?;
168+
if let Some(ask) = &self.common.ask {
169+
let confirmation = match ask {
170+
NotifyAskMode::Prompt => {
171+
inquire::Confirm::new("Apply the config?")
172+
.with_default(false)
173+
.prompt()?
174+
},
175+
#[cfg(all(unix, not(target_os = "macos")))]
176+
NotifyAskMode::Notify => {
177+
NotificationSender::new(
178+
"nh home switch",
179+
"Do you want to apply the Home Manager configuration?",
180+
)
181+
.ask()
182+
},
183+
};
164184

165185
if !confirmation {
166186
bail!("User rejected the new config");

src/interface.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,24 @@ pub enum DiffType {
264264
Never,
265265
}
266266

267+
#[derive(Debug, Clone, ValueEnum, PartialEq)]
268+
pub enum NotifyAskMode {
269+
/// Ask in the terminal (stdin prompt)
270+
Prompt,
271+
/// Ask via a desktop notification action
272+
#[cfg(all(unix, not(target_os = "macos")))]
273+
Notify,
274+
}
275+
267276
#[derive(Debug, Args)]
268277
pub struct OsRollbackArgs {
269278
/// Only print actions, without performing them
270279
#[arg(long, short = 'n')]
271280
pub dry: bool,
272281

273282
/// Ask for confirmation
274-
#[arg(long, short)]
275-
pub ask: bool,
283+
#[arg(long, short, value_enum, default_missing_value = "prompt", num_args = 0..=1)]
284+
pub ask: Option<NotifyAskMode>,
276285

277286
/// Explicitly select some specialisation
278287
#[arg(long, short)]
@@ -303,8 +312,8 @@ pub struct CommonRebuildArgs {
303312
pub dry: bool,
304313

305314
/// Ask for confirmation
306-
#[arg(long, short)]
307-
pub ask: bool,
315+
#[arg(long, short, default_missing_value = "prompt", num_args = 0..=1)]
316+
pub ask: Option<NotifyAskMode>,
308317

309318
#[command(flatten)]
310319
pub installable: Installable,
@@ -435,8 +444,8 @@ pub struct CleanArgs {
435444
pub dry: bool,
436445

437446
/// Ask for confirmation
438-
#[arg(long, short)]
439-
pub ask: bool,
447+
#[arg(long, short, default_missing_value = "prompt", num_args = 0..=1)]
448+
pub ask: Option<NotifyAskMode>,
440449

441450
/// Don't run nix store --gc
442451
#[arg(long = "no-gc", alias = "nogc")]

src/logging.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ where
3131

3232
match *level {
3333
Level::ERROR => {
34-
write!(writer, "{} ", Paint::new("ERROR").fg(Color::Red))?
34+
write!(writer, "{} ", Paint::new("ERROR").fg(Color::Red))?;
3535
},
3636
Level::WARN => write!(writer, "{} ", Paint::new("!").fg(Color::Yellow))?,
3737
Level::INFO => write!(writer, "{} ", Paint::new(">").fg(Color::Green))?,
3838
Level::DEBUG => {
39-
write!(writer, "{} ", Paint::new("DEBUG").fg(Color::Blue))?
39+
write!(writer, "{} ", Paint::new("DEBUG").fg(Color::Blue))?;
4040
},
4141
Level::TRACE => {
42-
write!(writer, "{} ", Paint::new("TRACE").fg(Color::Cyan))?
42+
write!(writer, "{} ", Paint::new("TRACE").fg(Color::Cyan))?;
4343
},
4444
}
4545

src/nixos.rs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::{
1414
interface::{
1515
self,
1616
DiffType,
17+
NotifyAskMode,
1718
OsBuildVmArgs,
1819
OsGenerationsArgs,
1920
OsRebuildArgs,
@@ -22,6 +23,7 @@ use crate::{
2223
OsSubcommand::{self},
2324
},
2425
nh_info,
26+
notify::NotificationSender,
2527
update::update,
2628
util::{ensure_ssh_key_login, get_hostname, print_dix_diff},
2729
};
@@ -39,7 +41,7 @@ impl interface::OsArgs {
3941
OsSubcommand::Test(args) => args.rebuild(&Test, None, elevation),
4042
OsSubcommand::Switch(args) => args.rebuild(&Switch, None, elevation),
4143
OsSubcommand::Build(args) => {
42-
if args.common.ask || args.common.dry {
44+
if args.common.ask.is_some() || args.common.dry {
4345
warn!("`--ask` and `--dry` have no effect for `nh os build`");
4446
}
4547
args.rebuild(&Build, None, elevation)
@@ -132,7 +134,7 @@ impl OsRebuildArgs {
132134
self.handle_dix_diff(&target_profile);
133135

134136
if self.common.dry || matches!(variant, Build | BuildVm) {
135-
if self.common.ask {
137+
if self.common.ask.is_some() {
136138
warn!("--ask has no effect as dry run was requested");
137139
}
138140

@@ -317,10 +319,22 @@ impl OsRebuildArgs {
317319
) -> Result<()> {
318320
use OsRebuildVariant::{Boot, Switch, Test};
319321

320-
if self.common.ask {
321-
let confirmation = inquire::Confirm::new("Apply the config?")
322-
.with_default(false)
323-
.prompt()?;
322+
if let Some(ask) = &self.common.ask {
323+
let confirmation = match ask {
324+
NotifyAskMode::Prompt => {
325+
inquire::Confirm::new("Apply the config?")
326+
.with_default(false)
327+
.prompt()?
328+
},
329+
#[cfg(all(unix, not(target_os = "macos")))]
330+
NotifyAskMode::Notify => {
331+
NotificationSender::new(
332+
"nh os switch",
333+
"Do you want to apply the NixOS configuration?",
334+
)
335+
.ask()
336+
},
337+
};
324338

325339
if !confirmation {
326340
bail!("User rejected the new config");
@@ -465,13 +479,25 @@ impl OsRollbackArgs {
465479
return Ok(());
466480
}
467481

468-
if self.ask {
469-
let confirmation = inquire::Confirm::new(&format!(
470-
"Roll back to generation {}?",
471-
target_generation.number
472-
))
473-
.with_default(false)
474-
.prompt()?;
482+
if let Some(ask) = &self.ask {
483+
let confirmation = match ask {
484+
NotifyAskMode::Prompt => {
485+
inquire::Confirm::new(&format!(
486+
"Roll back to generation {}?",
487+
target_generation.number
488+
))
489+
.with_default(false)
490+
.prompt()?
491+
},
492+
#[cfg(all(unix, not(target_os = "macos")))]
493+
NotifyAskMode::Notify => {
494+
NotificationSender::new(
495+
"nh os rollback",
496+
&format!("Roll back to generation {}?", target_generation.number),
497+
)
498+
.ask()
499+
},
500+
};
475501

476502
if !confirmation {
477503
bail!("User rejected the rollback");
@@ -659,7 +685,7 @@ fn print_vm_instructions(out_path: &Path) {
659685
}
660686
}
661687

662-
/// Runs the built NixOS VM by executing the VM runner script.
688+
/// Runs the built `NixOS` VM by executing the VM runner script.
663689
///
664690
/// Locates the VM runner script in the build output directory and executes it,
665691
/// streaming its output to the user. Returns an error if the script cannot be

0 commit comments

Comments
 (0)