Deployment and lifecycle management for Nix
0
fork

Configure Feed

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

load config file

+174 -45
+73 -20
Cargo.lock
··· 67 67 68 68 [[package]] 69 69 name = "autocfg" 70 - version = "1.1.0" 70 + version = "1.2.0" 71 71 source = "registry+https://github.com/rust-lang/crates.io-index" 72 - checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 72 + checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" 73 73 74 74 [[package]] 75 75 name = "backtrace" 76 - version = "0.3.69" 76 + version = "0.3.71" 77 77 source = "registry+https://github.com/rust-lang/crates.io-index" 78 - checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 78 + checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 79 79 dependencies = [ 80 80 "addr2line", 81 81 "cc", ··· 112 112 113 113 [[package]] 114 114 name = "bytes" 115 - version = "1.5.0" 115 + version = "1.6.0" 116 116 source = "registry+https://github.com/rust-lang/crates.io-index" 117 - checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 117 + checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 118 118 119 119 [[package]] 120 120 name = "cc" ··· 136 136 137 137 [[package]] 138 138 name = "clap" 139 - version = "4.5.3" 139 + version = "4.5.4" 140 140 source = "registry+https://github.com/rust-lang/crates.io-index" 141 - checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" 141 + checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" 142 142 dependencies = [ 143 143 "clap_builder", 144 144 "clap_derive", ··· 158 158 159 159 [[package]] 160 160 name = "clap_derive" 161 - version = "4.5.3" 161 + version = "4.5.4" 162 162 source = "registry+https://github.com/rust-lang/crates.io-index" 163 - checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" 163 + checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" 164 164 dependencies = [ 165 165 "heck 0.5.0", 166 166 "proc-macro2", ··· 409 409 410 410 [[package]] 411 411 name = "indexmap" 412 - version = "2.2.5" 412 + version = "2.2.6" 413 413 source = "registry+https://github.com/rust-lang/crates.io-index" 414 - checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" 414 + checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 415 415 dependencies = [ 416 416 "equivalent", 417 417 "hashbrown", ··· 425 425 426 426 [[package]] 427 427 name = "itoa" 428 - version = "1.0.10" 428 + version = "1.0.11" 429 429 source = "registry+https://github.com/rust-lang/crates.io-index" 430 - checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 430 + checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 431 431 432 432 [[package]] 433 433 name = "js-sys" ··· 462 462 463 463 [[package]] 464 464 name = "memchr" 465 - version = "2.7.1" 465 + version = "2.7.2" 466 466 source = "registry+https://github.com/rust-lang/crates.io-index" 467 - checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 467 + checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 468 468 469 469 [[package]] 470 470 name = "mime" ··· 740 740 741 741 [[package]] 742 742 name = "serde_json" 743 - version = "1.0.114" 743 + version = "1.0.115" 744 744 source = "registry+https://github.com/rust-lang/crates.io-index" 745 - checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" 745 + checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" 746 746 dependencies = [ 747 747 "itoa", 748 748 "ryu", 749 + "serde", 750 + ] 751 + 752 + [[package]] 753 + name = "serde_spanned" 754 + version = "0.6.5" 755 + source = "registry+https://github.com/rust-lang/crates.io-index" 756 + checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" 757 + dependencies = [ 749 758 "serde", 750 759 ] 751 760 ··· 805 814 "serde", 806 815 "strum", 807 816 "tokio", 817 + "toml", 808 818 ] 809 819 810 820 [[package]] ··· 843 853 844 854 [[package]] 845 855 name = "syn" 846 - version = "2.0.53" 856 + version = "2.0.55" 847 857 source = "registry+https://github.com/rust-lang/crates.io-index" 848 - checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" 858 + checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" 849 859 dependencies = [ 850 860 "proc-macro2", 851 861 "quote", ··· 946 956 "pin-project-lite", 947 957 "tokio", 948 958 "tracing", 959 + ] 960 + 961 + [[package]] 962 + name = "toml" 963 + version = "0.8.12" 964 + source = "registry+https://github.com/rust-lang/crates.io-index" 965 + checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" 966 + dependencies = [ 967 + "serde", 968 + "serde_spanned", 969 + "toml_datetime", 970 + "toml_edit", 971 + ] 972 + 973 + [[package]] 974 + name = "toml_datetime" 975 + version = "0.6.5" 976 + source = "registry+https://github.com/rust-lang/crates.io-index" 977 + checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 978 + dependencies = [ 979 + "serde", 980 + ] 981 + 982 + [[package]] 983 + name = "toml_edit" 984 + version = "0.22.9" 985 + source = "registry+https://github.com/rust-lang/crates.io-index" 986 + checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" 987 + dependencies = [ 988 + "indexmap", 989 + "serde", 990 + "serde_spanned", 991 + "toml_datetime", 992 + "winnow", 949 993 ] 950 994 951 995 [[package]] ··· 1251 1295 version = "0.52.4" 1252 1296 source = "registry+https://github.com/rust-lang/crates.io-index" 1253 1297 checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 1298 + 1299 + [[package]] 1300 + name = "winnow" 1301 + version = "0.6.5" 1302 + source = "registry+https://github.com/rust-lang/crates.io-index" 1303 + checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" 1304 + dependencies = [ 1305 + "memchr", 1306 + ] 1254 1307 1255 1308 [[package]] 1256 1309 name = "winreg"
+1
cli/Cargo.toml
··· 12 12 serde = { version = "1.0", features = ["derive"] } 13 13 strum = { version = "0.26", features = ["derive"] } 14 14 tokio = { version = "1", features = ["full"] } 15 + toml = "0.8.12"
+58 -8
cli/src/main.rs
··· 2 2 3 3 use clap::Parser; 4 4 use clap::Subcommand; 5 + use serde::Deserialize; 6 + use std::fs; 5 7 6 8 mod sower; 7 9 ··· 10 12 struct Cli { 11 13 #[command(subcommand)] 12 14 action: Actions, 15 + 16 + #[arg(long, short, global = true)] 17 + config: Option<String>, 13 18 14 19 #[arg(long, short, global = true)] 15 20 name: Option<String>, ··· 50 55 subcommand_help_heading = "Seed commands" 51 56 )] 52 57 enum SeedCommands { 53 - /// download and activate 58 + /// activate 54 59 Activate { 55 60 #[arg(long, short, value_name = "how to activate nixos")] 56 61 mode: Option<ActivationMode>, ··· 83 88 }, 84 89 } 85 90 91 + #[derive(Debug, Deserialize)] 92 + pub struct Config { 93 + name: Option<String>, 94 + seed_type: Option<SeedType>, 95 + url: Option<String>, 96 + } 97 + 98 + impl Config { 99 + pub fn name(self, name: Option<String>) -> Self { 100 + if let Some(_) = &name { 101 + Self { name, ..self } 102 + } else { 103 + self 104 + } 105 + } 106 + 107 + pub fn seed_type(self, seed_type: SeedType) -> Self { 108 + Self { 109 + seed_type: Some(seed_type), 110 + ..self 111 + } 112 + } 113 + 114 + pub fn url(self, url: Option<String>) -> Self { 115 + if let Some(_) = &url { 116 + Self { url, ..self } 117 + } else { 118 + self 119 + } 120 + } 121 + } 122 + 86 123 #[tokio::main] 87 124 async fn main() -> Result<(), Box<dyn std::error::Error>> { 88 125 let cli = Cli::parse(); 89 126 90 - let url = cli.url.expect("missing Sower URL"); 91 - let seed = Sower::new(url.clone()) 92 - .expect("failed to find latest seed") 93 - .find_seed(cli.name.clone(), cli.seed_type.clone()) 94 - .await?; 127 + let config: Config = match cli.config { 128 + Some(path) => { 129 + let config_string = fs::read_to_string(path)?; 130 + toml::from_str(&config_string).expect("failed to parse config file") 131 + } 132 + None => Config { 133 + name: None, 134 + seed_type: None, 135 + url: None, 136 + }, 137 + }; 138 + 139 + let config = config.name(cli.name).seed_type(cli.seed_type).url(cli.url); 140 + 141 + let tree = Tree::new(&config).await?; 142 + let seed = &tree.seed; 143 + 144 + dbg!(&tree); 95 145 96 146 match &cli.action { 97 147 Actions::Seed { action } => match action { 98 148 SeedCommands::Activate { mode, .. } => { 99 - seed.activate(&mode).expect("failed to activate"); 149 + seed.activate(mode.clone()).expect("failed to activate"); 100 150 } 101 151 102 152 SeedCommands::Download {} => { ··· 108 158 TreeCommands::Upgrade { mode, reboot, yes } => { 109 159 seed.realize() 110 160 .expect("failed to realize") 111 - .activate(&mode) 161 + .activate(mode.clone()) 112 162 .expect("failed to activate"); 113 163 114 164 if reboot.clone() {
+42 -17
cli/src/sower.rs
··· 1 + use crate::*; 2 + 1 3 use clap::ValueEnum; 2 4 use serde::Deserialize; 3 5 use std::env; ··· 25 27 } 26 28 } 27 29 28 - pub fn activate(&self, mode: &Option<ActivationMode>) -> Result<&Self, String> { 30 + pub fn activate(&self, mode: Option<ActivationMode>) -> Result<&Self, String> { 29 31 match &self.seed_type { 30 32 SeedType::HomeManager => self.activate_generic(), 31 33 SeedType::NixDarwin => self.activate_generic(), ··· 40 42 } 41 43 } 42 44 43 - fn activate_nixos(&self, mode: &Option<ActivationMode>) -> Result<&Self, String> { 44 - let mode = mode.clone().unwrap_or(ActivationMode::DryActivate); 45 + fn activate_nixos(&self, mode: Option<ActivationMode>) -> Result<&Self, String> { 46 + let mode = mode.unwrap_or(ActivationMode::DryActivate); 45 47 46 48 // nixos profile needs to be manually set to ensure correct switching 47 49 run_command( ··· 67 69 } 68 70 } 69 71 70 - #[derive(Clone, Debug, Display, VariantNames, Deserialize, ValueEnum)] 72 + #[derive(Clone, Copy, Debug, Display, VariantNames, Deserialize, ValueEnum)] 71 73 #[serde(rename_all = "kebab-case")] 72 74 #[strum(serialize_all = "kebab-case")] 73 75 pub enum SeedType { ··· 86 88 None, 87 89 } 88 90 89 - #[derive(Debug)] 91 + #[derive(Clone, Debug)] 90 92 pub struct Sower { 91 93 pub url: String, 92 94 } 93 95 94 96 impl Sower { 95 - pub fn new(url: String) -> Result<Sower, Box<dyn std::error::Error>> { 96 - let seed_url = format!("{}/api/seeds/latest", url); 97 + pub fn new(config: &Config) -> Result<Sower, Box<dyn std::error::Error>> { 98 + let seed_url = format!( 99 + "{}/api/seeds/latest", 100 + config.url.clone().expect("URL is required") 101 + ); 97 102 98 103 Ok(Self { url: seed_url }) 99 104 } 100 105 101 106 pub async fn find_seed( 102 107 &self, 103 - name: Option<String>, 108 + name: String, 104 109 seed_type: SeedType, 105 110 ) -> Result<Seed, Box<dyn std::error::Error>> { 106 - let name = name.clone().unwrap_or(match seed_type.clone() { 107 - SeedType::Nixos | SeedType::NixDarwin => nix::unistd::gethostname() 108 - .expect("Failed getting hostname") 109 - .into_string() 110 - .unwrap(), 111 - SeedType::HomeManager => env::var("USER").expect("can not detect username"), 112 - }); 113 - 114 111 let client = reqwest::Client::new(); 115 112 Ok(client 116 113 .get(&self.url) ··· 123 120 } 124 121 125 122 #[derive(Debug)] 126 - pub struct Tree {} 123 + pub struct Tree { 124 + pub name: String, 125 + pub seed: Seed, 126 + pub seed_type: SeedType, 127 + pub sower: Sower, 128 + } 127 129 128 130 impl Tree { 131 + pub async fn new(config: &Config) -> Result<Tree, Box<dyn std::error::Error>> { 132 + let name = 133 + config 134 + .name 135 + .clone() 136 + .unwrap_or(match config.seed_type.expect("seed type is required") { 137 + SeedType::Nixos | SeedType::NixDarwin => nix::unistd::gethostname() 138 + .expect("Failed getting hostname") 139 + .into_string() 140 + .unwrap(), 141 + SeedType::HomeManager => env::var("USER").expect("can not detect username"), 142 + }); 143 + let seed_type = config.seed_type.unwrap(); 144 + let sower = Sower::new(&config)?; 145 + 146 + Ok(Tree { 147 + name: name.clone(), 148 + seed_type, 149 + sower: sower.clone(), 150 + seed: sower.find_seed(name, seed_type).await?, 151 + }) 152 + } 153 + 129 154 pub fn reboot(confirm: bool) { 130 155 if Self::reboot_needed().expect("failed to check reboot state") { 131 156 println!("Reboot needed.");