Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: trees deal with multiple seeds, not one

+144 -36
+45 -28
client/src/main.rs
··· 5 5 use serde::Deserialize; 6 6 use std::fs; 7 7 use std::path::{Path, PathBuf}; 8 + use tracing::{debug, info}; 8 9 use xdg; 9 10 10 11 mod sower; 12 + use sower::daemon::Daemon; 11 13 12 14 #[derive(Parser)] 13 15 #[command(version, about, long_about = None, arg_required_else_help = true)] ··· 175 177 // cli overrides config 176 178 let config = config.name(cli.name).seed_type(cli.seed_type).url(cli.url); 177 179 178 - let mut tree = Tree::new(&config).await?; 179 - let seed = &tree.seed.as_ref(); 180 + let tree = Tree::new(&config).await?; 180 181 181 182 match &cli.action { 182 - Actions::Seed { action } => match action { 183 - SeedCommands::Activate { mode, .. } => { 184 - let mode = mode.clone().or(config.mode); 185 - seed.unwrap().activate(mode).expect("failed to activate"); 186 - } 183 + Actions::Daemon {} => { 184 + let mut daemon = Daemon::new(tree).expect("Failed to initialize daemon"); 185 + daemon.run().await.unwrap() 186 + } 187 187 188 - SeedCommands::Download {} => { 189 - seed.unwrap().realize().expect("failed to realize"); 188 + Actions::Seed { action } => { 189 + let seed = tree.latest_seed().await; 190 + 191 + match action { 192 + SeedCommands::Activate { mode, .. } => { 193 + let mode = mode.clone().or(config.mode); 194 + seed.unwrap().activate(mode).expect("failed to activate"); 195 + } 196 + 197 + SeedCommands::Download {} => { 198 + seed.unwrap().realize().expect("failed to realize"); 199 + } 190 200 } 191 - }, 201 + } 192 202 193 - Actions::Tree { action } => match action { 194 - TreeCommands::Daemon {} => tree.daemon().await.unwrap(), 203 + Actions::Tree { action } => { 204 + let tree = tree.load_latest().await; 205 + 206 + match action { 207 + TreeCommands::Info {} => info!("{:?}", tree), 195 208 196 - TreeCommands::Info {} => tree.info(), 209 + TreeCommands::Reboot { yes } => { 210 + tree.info(); 211 + tree.reboot(yes.clone()) 212 + } 197 213 198 - TreeCommands::Reboot { yes } => { 199 - tree.info(); 200 - tree.reboot(yes.clone()) 201 - } 214 + TreeCommands::Upgrade { mode, reboot, yes } => { 215 + debug!("{:?}", tree); 202 216 203 - TreeCommands::Upgrade { mode, reboot, yes } => { 204 - tree.info(); 217 + let mode = mode.clone().or(config.mode); 218 + let desired = tree.seeds.clone().unwrap().desired.unwrap(); 219 + info!("Activating seed {:?}", &desired); 220 + desired 221 + .realize() 222 + .expect("failed to realize") 223 + .activate(mode) 224 + .expect("failed to activate"); 205 225 206 - let mode = mode.clone().or(config.mode); 207 - seed.unwrap() 208 - .realize() 209 - .expect("failed to realize") 210 - .activate(mode) 211 - .expect("failed to activate"); 226 + let tree = tree.load_seeds(); 227 + debug!("{:?}", tree); 212 228 213 - if config.reboot.unwrap_or(false) || reboot.clone() { 214 - tree.reboot(yes.clone()); 229 + if config.reboot.unwrap_or(false) || reboot.clone() { 230 + tree.reboot(yes.clone()); 231 + } 215 232 } 216 233 } 217 - }, 234 + } 218 235 } 219 236 220 237 Ok(())
+99 -8
client/src/sower.rs
··· 1 1 use crate::*; 2 - use serde::Serialize; 3 2 4 3 pub mod daemon; 5 4 6 5 use clap::ValueEnum; 7 6 use serde::Deserialize; 7 + use serde::Serialize; 8 8 use std::env; 9 9 use std::fs; 10 10 use std::path::Path; 11 11 use std::process::Command; 12 12 use strum::{Display, VariantNames}; 13 - use tracing::info; 13 + use tracing::{debug, info}; 14 14 15 - #[derive(Debug, Deserialize)] 15 + #[derive(Clone, Debug, Deserialize)] 16 16 pub struct Seed { 17 - pub id: String, 17 + pub id: Option<String>, 18 18 pub name: String, 19 19 #[serde(rename(deserialize = "type"))] 20 20 pub seed_type: SeedType, ··· 70 70 false => Err(format!("failed to realize: {}", &self.out_path)), 71 71 } 72 72 } 73 + 74 + fn new_from_path(name: String, seed_type: SeedType, path: &str) -> Self { 75 + debug!(path); 76 + let path = fs::canonicalize(Path::new(path)).expect("unable to read link"); 77 + Self { 78 + id: None, 79 + name, 80 + seed_type, 81 + out_path: path.to_string_lossy().to_string(), 82 + } 83 + } 73 84 } 74 85 75 86 #[derive( ··· 83 94 Nixos, 84 95 } 85 96 97 + impl SeedType { 98 + fn profile_path(&self) -> String { 99 + match self { 100 + SeedType::HomeManager => { 101 + format!( 102 + "{}/.local/state/nix/profiles/home-manager", 103 + env::var("HOME").expect("missing $HOME environment variable") 104 + ) 105 + } 106 + SeedType::NixDarwin => panic!("unsupported"), 107 + SeedType::Nixos => "/nix/var/nix/profiles/system".to_string(), 108 + } 109 + } 110 + } 111 + 86 112 #[derive(Clone, Debug, Deserialize, Display, ValueEnum, VariantNames)] 87 113 #[serde(rename_all = "kebab-case")] 88 114 #[strum(serialize_all = "kebab-case")] ··· 138 164 #[derive(Debug, Deserialize)] 139 165 pub struct Tree { 140 166 pub name: String, 141 - pub seed: Option<Seed>, 167 + pub seeds: Option<TreeSeeds>, 142 168 pub seed_type: SeedType, 143 169 pub sower: Option<Sower>, 144 170 pub id: Option<String>, 145 171 } 146 172 173 + #[derive(Clone, Debug, Deserialize)] 174 + pub struct TreeSeeds { 175 + pub current: Option<Seed>, 176 + pub booted: Option<Seed>, 177 + pub desired: Option<Seed>, 178 + pub profile: Seed, 179 + } 180 + 147 181 impl Tree { 148 182 pub async fn new(config: &Config) -> Result<Tree, Box<dyn std::error::Error>> { 149 183 let name = ··· 164 198 name: name.clone(), 165 199 seed_type, 166 200 sower: Some(sower.clone()), 167 - seed: sower.find_seed(name, seed_type).await, 201 + seeds: None, 168 202 id: None, 169 - }) 203 + } 204 + .load_seeds()) 170 205 } 171 206 172 207 pub fn info(&self) -> () { ··· 174 209 () 175 210 } 176 211 212 + pub async fn latest_seed(&self) -> Option<Seed> { 213 + self.sower 214 + .as_ref() 215 + .unwrap() 216 + .find_seed(self.name.clone(), self.seed_type.clone()) 217 + .await 218 + } 219 + 220 + pub async fn load_latest(mut self) -> Self { 221 + self.seeds = Some(TreeSeeds { 222 + desired: self.latest_seed().await, 223 + ..self.seeds.unwrap() 224 + }); 225 + self 226 + } 227 + 228 + pub fn load_seeds(mut self) -> Self { 229 + let booted = match self.seed_type { 230 + SeedType::Nixos => Some(Seed::new_from_path( 231 + self.name.clone(), 232 + self.seed_type.clone(), 233 + "/run/booted-system", 234 + )), 235 + SeedType::HomeManager => None, 236 + SeedType::NixDarwin => None, 237 + }; 238 + 239 + let current = match self.seed_type { 240 + SeedType::HomeManager => None, 241 + SeedType::NixDarwin => Some(Seed::new_from_path( 242 + self.name.clone(), 243 + self.seed_type.clone(), 244 + "/run/current-system", 245 + )), 246 + SeedType::Nixos => Some(Seed::new_from_path( 247 + self.name.clone(), 248 + self.seed_type.clone(), 249 + "/run/current-system", 250 + )), 251 + }; 252 + 253 + let profile = Seed::new_from_path( 254 + self.name.clone(), 255 + self.seed_type.clone(), 256 + &self.seed_type.profile_path(), 257 + ); 258 + 259 + self.seeds = Some(TreeSeeds { 260 + booted, 261 + current, 262 + profile, 263 + desired: None, 264 + }); 265 + 266 + self 267 + } 268 + 177 269 pub fn reboot(&self, confirm: bool) { 178 270 if self.seed_type != SeedType::Nixos { 179 271 println!("Non-NixOS Trees aren't rebootable"); ··· 237 329 profile.clone().into_os_string() 238 330 ); 239 331 } 240 - // if running system was updated using switch, don't reboot 241 332 profile != current 242 333 } 243 334 });