Skip to content

Commit f2b7ffd

Browse files
committed
feat: add notification support to --ask flags
Change-Id: I6a6a69642ac3443525df583fd200827414801d41
1 parent 19179ac commit f2b7ffd

File tree

8 files changed

+151
-53
lines changed

8 files changed

+151
-53
lines changed

src/clean.rs

Lines changed: 22 additions & 7 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:
@@ -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+
NotifyAskMode::Notify => {
346+
NotificationSender::new(
347+
"nh clean",
348+
"Do you want to confirm the cleanup plan?",
349+
)
350+
.ask()
351+
},
352+
NotifyAskMode::Both => unimplemented!(),
353+
};
354+
355+
if !confirmation {
356+
bail!("User rejected the cleanup plan");
357+
}
343358
}
344359

345360
if !args.dry {

src/commands.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ use thiserror::Error;
1515
use tracing::{debug, warn};
1616
use which::which;
1717

18-
use crate::{
19-
installable::Installable,
20-
interface::NixBuildPassthroughArgs,
18+
use crate::{
19+
installable::Installable,
20+
interface::NixBuildPassthroughArgs,
2121
nh_info,
22-
};
22+
};
2323

2424
static PASSWORD_CACHE: OnceLock<Mutex<HashMap<String, SecretString>>> =
2525
OnceLock::new();

src/darwin.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
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};
55

66
use crate::{
77
Result,
8-
commands,
9-
commands::{Command, ElevationStrategy},
8+
commands::{self, Command, ElevationStrategy},
109
installable::Installable,
1110
interface::{
1211
DarwinArgs,
1312
DarwinRebuildArgs,
1413
DarwinReplArgs,
1514
DarwinSubcommand,
1615
DiffType,
16+
NotifyAskMode,
1717
},
1818
nixos::toplevel_for,
19+
notify::NotificationSender,
1920
update::update,
2021
util::{get_hostname, print_dix_diff},
2122
};
@@ -34,7 +35,7 @@ impl DarwinArgs {
3435
match self.subcommand {
3536
DarwinSubcommand::Switch(args) => args.rebuild(&Switch, elevation),
3637
DarwinSubcommand::Build(args) => {
37-
if args.common.ask || args.common.dry {
38+
if args.common.ask.is_some() || args.common.dry {
3839
warn!("`--ask` and `--dry` have no effect for `nh darwin build`");
3940
}
4041
args.rebuild(&Build, elevation)
@@ -49,6 +50,16 @@ enum DarwinRebuildVariant {
4950
Build,
5051
}
5152

53+
impl fmt::Display for DarwinRebuildVariant {
54+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55+
let s = match self {
56+
DarwinRebuildVariant::Build => "build",
57+
DarwinRebuildVariant::Switch => "switch",
58+
};
59+
write!(f, "{s}")
60+
}
61+
}
62+
5263
impl DarwinRebuildArgs {
5364
fn rebuild(
5465
self,
@@ -144,13 +155,29 @@ impl DarwinRebuildArgs {
144155
let _ = print_dix_diff(&PathBuf::from(CURRENT_PROFILE), &target_profile);
145156
}
146157

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()?;
158+
if !self.common.dry && !matches!(variant, Build) {
159+
if let Some(ask) = self.common.ask {
160+
let confirmation = match ask {
161+
NotifyAskMode::Prompt => {
162+
inquire::Confirm::new("Apply the config?")
163+
.with_default(false)
164+
.prompt()?
165+
},
166+
// MacOS doesn't support notification actions
167+
NotifyAskMode::Notify | NotifyAskMode::Both => {
168+
NotificationSender::new(&format!("nh darwin {variant}"), "testing")
169+
.send()
170+
.unwrap();
171+
172+
inquire::Confirm::new("Apply the config?")
173+
.with_default(false)
174+
.prompt()?
175+
},
176+
};
151177

152-
if !confirmation {
153-
bail!("User rejected the new config");
178+
if !confirmation {
179+
bail!("User rejected the new config");
180+
}
154181
}
155182
}
156183

src/home.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use crate::{
1010
commands,
1111
commands::Command,
1212
installable::Installable,
13-
interface::{self, DiffType, HomeRebuildArgs, HomeReplArgs, HomeSubcommand},
13+
interface::{self, DiffType, HomeRebuildArgs, HomeReplArgs, HomeSubcommand, NotifyAskMode},
1414
nh_info,
15+
notify::NotificationSender,
1516
update::update,
1617
util::{get_hostname, print_dix_diff},
1718
};
@@ -27,7 +28,7 @@ impl interface::HomeArgs {
2728
match self.subcommand {
2829
HomeSubcommand::Switch(args) => args.rebuild(&Switch),
2930
HomeSubcommand::Build(args) => {
30-
if args.common.ask || args.common.dry {
31+
if args.common.ask.is_some() || args.common.dry {
3132
warn!("`--ask` and `--dry` have no effect for `nh home build`");
3233
}
3334
args.rebuild(&Build)
@@ -151,16 +152,24 @@ impl HomeRebuildArgs {
151152
}
152153

153154
if self.common.dry || matches!(variant, Build) {
154-
if self.common.ask {
155+
if self.common.ask.is_some() {
155156
warn!("--ask has no effect as dry run was requested");
156157
}
157158
return Ok(());
158159
}
159160

160-
if self.common.ask {
161-
let confirmation = inquire::Confirm::new("Apply the config?")
162-
.with_default(false)
163-
.prompt()?;
161+
if let Some(ask) = &self.common.ask {
162+
let confirmation = match ask {
163+
NotifyAskMode::Prompt => {
164+
inquire::Confirm::new("Apply the config?")
165+
.with_default(false)
166+
.prompt()?
167+
},
168+
NotifyAskMode::Notify => {
169+
NotificationSender::new("nh home switch", "Do you want to apply the Home Manager configuration?").ask()
170+
},
171+
NotifyAskMode::Both => unimplemented!(),
172+
};
164173

165174
if !confirmation {
166175
bail!("User rejected the new config");

src/interface.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,25 @@ 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+
Notify,
273+
/// Show both notification and terminal prompt (fallback-safe)
274+
Both,
275+
}
276+
267277
#[derive(Debug, Args)]
268278
pub struct OsRollbackArgs {
269279
/// Only print actions, without performing them
270280
#[arg(long, short = 'n')]
271281
pub dry: bool,
272282

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

277287
/// Explicitly select some specialisation
278288
#[arg(long, short)]
@@ -303,8 +313,8 @@ pub struct CommonRebuildArgs {
303313
pub dry: bool,
304314

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

309319
#[command(flatten)]
310320
pub installable: Installable,
@@ -435,8 +445,8 @@ pub struct CleanArgs {
435445
pub dry: bool,
436446

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

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

src/logging.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ pub fn setup_logging(
9999
macro_rules! nh_info {
100100
($($arg:tt)*) => {{
101101
use notify_rust::Urgency;
102-
use crate::notify::NotificationSender;
102+
use $crate::notify::NotificationSender;
103103
let message = format!($($arg)*);
104104
tracing::info!($($arg)*);
105105
NotificationSender::new("nh info", &message).urgency(Urgency::Normal).send().unwrap();
@@ -110,7 +110,7 @@ macro_rules! nh_info {
110110
macro_rules! nh_warn {
111111
($($arg:tt)*) => {{
112112
use notify_rust::Urgency;
113-
use crate::notify::NotificationSender;
113+
use $crate::notify::NotificationSender;
114114
let message = format!($($arg)*);
115115
tracing::warn!($($arg)*);
116116
NotificationSender::new("nh warn", &message).urgency(Urgency::Normal).send().unwrap();

src/nixos.rs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::{
1515
interface::{
1616
self,
1717
DiffType,
18+
NotifyAskMode,
1819
OsBuildVmArgs,
1920
OsGenerationsArgs,
2021
OsRebuildArgs,
@@ -23,6 +24,7 @@ use crate::{
2324
OsSubcommand::{self},
2425
},
2526
nh_info,
27+
notify::NotificationSender,
2628
update::update,
2729
util::{ensure_ssh_key_login, get_hostname, print_dix_diff},
2830
};
@@ -40,7 +42,7 @@ impl interface::OsArgs {
4042
OsSubcommand::Test(args) => args.rebuild(&Test, None, elevation),
4143
OsSubcommand::Switch(args) => args.rebuild(&Switch, None, elevation),
4244
OsSubcommand::Build(args) => {
43-
if args.common.ask || args.common.dry {
45+
if args.common.ask.is_some() || args.common.dry {
4446
warn!("`--ask` and `--dry` have no effect for `nh os build`");
4547
}
4648
args.rebuild(&Build, None, elevation)
@@ -276,7 +278,7 @@ impl OsRebuildArgs {
276278
}
277279

278280
if self.common.dry || matches!(variant, Build | BuildVm) {
279-
if self.common.ask {
281+
if self.common.ask.is_some() {
280282
warn!("--ask has no effect as dry run was requested");
281283
}
282284

@@ -288,10 +290,22 @@ impl OsRebuildArgs {
288290
return Ok(());
289291
}
290292

291-
if self.common.ask {
292-
let confirmation = inquire::Confirm::new("Apply the config?")
293-
.with_default(false)
294-
.prompt()?;
293+
if let Some(ask) = &self.common.ask {
294+
let confirmation = match ask {
295+
NotifyAskMode::Prompt => {
296+
inquire::Confirm::new("Apply the config?")
297+
.with_default(false)
298+
.prompt()?
299+
},
300+
NotifyAskMode::Notify => {
301+
NotificationSender::new(
302+
"nh os switch",
303+
"Do you want to apply the NixOS configuration?",
304+
)
305+
.ask()
306+
},
307+
NotifyAskMode::Both => unimplemented!(),
308+
};
295309

296310
if !confirmation {
297311
bail!("User rejected the new config");
@@ -453,13 +467,25 @@ impl OsRollbackArgs {
453467
return Ok(());
454468
}
455469

456-
if self.ask {
457-
let confirmation = inquire::Confirm::new(&format!(
458-
"Roll back to generation {}?",
459-
target_generation.number
460-
))
461-
.with_default(false)
462-
.prompt()?;
470+
if let Some(ask) = &self.ask {
471+
let confirmation = match ask {
472+
NotifyAskMode::Prompt => {
473+
inquire::Confirm::new(&format!(
474+
"Roll back to generation {}?",
475+
target_generation.number
476+
))
477+
.with_default(false)
478+
.prompt()?
479+
},
480+
NotifyAskMode::Notify => {
481+
NotificationSender::new(
482+
"nh os rollback",
483+
&format!("Roll back to generation {}?", target_generation.number),
484+
)
485+
.ask()
486+
},
487+
NotifyAskMode::Both => unimplemented!(),
488+
};
463489

464490
if !confirmation {
465491
bail!("User rejected the rollback");

0 commit comments

Comments
 (0)