-
-
Notifications
You must be signed in to change notification settings - Fork 92
Expand file tree
/
Copy pathdarwin.rs
More file actions
161 lines (140 loc) · 5.88 KB
/
darwin.rs
File metadata and controls
161 lines (140 loc) · 5.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use color_eyre::eyre::{bail, Context};
use tracing::{debug, warn};
use crate::commands::Command;
use crate::installable::Installable;
use crate::interface::{DarwinArgs, DarwinRebuildArgs, DarwinReplArgs, DarwinSubcommand};
use crate::update::update;
use crate::util::get_hostname;
use crate::util::platform;
use crate::Result;
/// System profile path for darwin configurations
const SYSTEM_PROFILE: &str = "/nix/var/nix/profiles/system";
/// Current system profile path for darwin
const CURRENT_PROFILE: &str = "/run/current-system";
impl DarwinArgs {
/// Entry point for processing darwin commands
///
/// Handles the different subcommands for darwin configurations:
/// - Switch: Builds and activates the configuration
/// - Build: Only builds the configuration
/// - Repl: Opens a REPL for exploring the configuration
pub fn run(self) -> Result<()> {
use DarwinRebuildVariant::{Build, Switch};
match self.subcommand {
DarwinSubcommand::Switch(args) => args.rebuild(Switch),
DarwinSubcommand::Build(args) => {
if args.common.ask || args.common.dry {
warn!("`--ask` and `--dry` have no effect for `nh darwin build`");
}
args.rebuild(Build)
}
DarwinSubcommand::Repl(args) => args.run(),
}
}
}
/// Variants of the darwin rebuild operation
///
/// Each variant represents a different mode of operation:
/// - Switch: Build and activate the configuration
/// - Build: Only build the configuration without activation
enum DarwinRebuildVariant {
Switch,
Build,
}
impl DarwinRebuildArgs {
/// Performs the rebuild operation for darwin configurations
///
/// This function handles building and potentially activating darwin configurations.
/// It first builds the configuration, then shows a diff of changes compared to the
/// current system, and finally activates the configuration if needed.
///
/// The darwin activation process is unique and requires special handling compared
/// to `NixOS`, particularly around determining whether root privileges are needed.
fn rebuild(self, variant: DarwinRebuildVariant) -> Result<()> {
use DarwinRebuildVariant::{Build, Switch};
// Check if running as root
platform::check_not_root(false)?;
if self.update_args.update {
update(&self.common.installable, self.update_args.update_input)?;
}
let hostname = self.hostname.ok_or(()).or_else(|()| get_hostname())?;
// Create temporary output path
let out_path = platform::create_output_path(self.common.out_link, "nh-darwin")?;
debug!(?out_path);
// Resolve the installable from env var or from the provided argument
let installable =
platform::resolve_env_installable("NH_DARWIN_FLAKE", self.common.installable.clone());
// Build the darwin configuration with proper attribute path handling
let target_profile = platform::handle_rebuild_workflow(
installable,
"darwinConfigurations",
&["toplevel"],
Some(hostname),
out_path.as_ref(),
&self.extra_args,
None, // Darwin doesn't use remote builders
"Building Darwin configuration",
self.common.no_nom,
"", // Darwin doesn't use specialisations like NixOS
false,
None,
CURRENT_PROFILE,
false,
)?;
// Allow users to confirm before applying changes
if !platform::confirm_action(
self.common.ask && !matches!(variant, Build),
self.common.dry,
)? {
return Ok(());
}
if matches!(variant, Switch) {
Command::new("nix")
.args(["build", "--no-link", "--profile", SYSTEM_PROFILE])
.arg(&target_profile)
.elevate(true)
.dry(self.common.dry)
.run()?;
let darwin_rebuild = target_profile.join("sw/bin/darwin-rebuild");
let activate_user = target_profile.join("activate-user");
// Darwin activation may or may not need root privileges
// This checks if we need elevation based on the activation-user script
let needs_elevation = !activate_user
.try_exists()
.context("Failed to check if activate-user file exists")?
|| std::fs::read_to_string(&activate_user)
.context("Failed to read activate-user file")?
.contains("# nix-darwin: deprecated");
// Actually activate the configuration using darwin-rebuild
Command::new(darwin_rebuild)
.arg("activate")
.message("Activating configuration")
.elevate(needs_elevation)
.dry(self.common.dry)
.run()?;
}
// Make sure out_path is not accidentally dropped
// https://docs.rs/tempfile/3.12.0/tempfile/index.html#early-drop-pitfall
Ok(())
}
}
impl DarwinReplArgs {
/// Opens a Nix REPL for exploring darwin configurations
///
/// Provides an interactive environment to explore and evaluate
/// components of a darwin configuration.
fn run(self) -> Result<()> {
if let Installable::Store { .. } = self.installable {
bail!("Nix doesn't support nix store installables.");
}
let hostname = self.hostname.ok_or(()).or_else(|()| get_hostname())?;
// Open an interactive REPL session for exploring darwin configurations
platform::run_repl(
platform::resolve_env_installable("NH_DARWIN_FLAKE", self.installable),
"darwinConfigurations",
&[], // REPL doesn't need additional path elements
Some(hostname),
&[], // No extra REPL args
)
}
}