Opinionated Android 15+ Linux Terminal Setup
android linux command-line-tools

Merge pull request #8 from tsirysndr/feat/npm

feat: add support for installing global npm packages

authored by tsiry-sandratraina.com and committed by

GitHub 67b1ea9c 15f59e61

+70 -5
+6 -5
README.md
··· 14 14 - [ble.sh](https://github.com/akinomyoga/ble.sh) integration for a better terminal experience 15 15 - [Pkgx](https://github.com/pkgxdev/pkgx) package manager for easy software installation 16 16 - [Mise](https://github.com/jdx/mise) integration for a modern command-line experience 17 + - [Npm](https://www.npmjs.com/) integration for managing Node.js global packages 17 18 - [Stow](https://www.gnu.org/software/stow) integration for managing dotfiles 18 19 - [Nix](https://github.com/NixOS/nix) package manager 19 20 - SSH support for secure remote access ··· 38 39 39 40 ## Supported Operating Systems 40 41 41 - | OS | Status | 42 - |----------------------------|----------| 43 - | Android 15+ Linux Terminal | ✅ | 44 - | ChromeOS Linux Terminal (Crostini) | ✅ | 45 - | Debian Linux | ✅ | 42 + | OS | Status | 43 + |------------------------------------|----------| 44 + | Android 15+ Linux Terminal | ✅ | 45 + | ChromeOS Linux Terminal (Crostini) | ✅ | 46 + | Debian Linux | ✅ | 46 47 47 48 48 49 ## Configuration
+28
src/apply.rs
··· 25 25 Tailscale(bool), 26 26 Neofetch(bool), 27 27 Doppler(bool), 28 + Npm(&'a HashMap<String, String>), 28 29 } 29 30 30 31 impl<'a> SetupStep<'a> { ··· 45 46 SetupStep::Tailscale(enabled) => enable_tailscale(*enabled), 46 47 SetupStep::Neofetch(enabled) => enable_neofetch(*enabled), 47 48 SetupStep::Doppler(enabled) => enable_doppler(*enabled), 49 + SetupStep::Npm(map) => setup_npm(map), 48 50 } 49 51 } 50 52 ··· 213 215 "Doppler".blue().bold(), 214 216 "(Install and configure Doppler for secrets management)".italic(), 215 217 enabled.to_string().green() 218 + ) 219 + } 220 + SetupStep::Npm(map) => { 221 + let npm_list = map 222 + .iter() 223 + .map(|(k, v)| format!(" - {}: {}", k.green(), v.cyan())) 224 + .collect::<Vec<_>>() 225 + .join("\n"); 226 + format!( 227 + "{} {}\n{}", 228 + "Npm".blue().bold(), 229 + "(Install global npm packages)".italic(), 230 + npm_list 216 231 ) 217 232 } 218 233 } ··· 561 576 } 562 577 Ok(()) 563 578 } 579 + 580 + fn setup_npm(map: &HashMap<String, String>) -> Result<(), Error> { 581 + for (package, version) in map { 582 + run_command( 583 + "bash", 584 + &[ 585 + "-c", 586 + &format!("source ~/.bashrc && npm install -g {}@{}", package, version), 587 + ], 588 + )?; 589 + } 590 + Ok(()) 591 + }
+2
src/cmd/setup.rs
··· 54 54 return Ok(()); 55 55 } 56 56 57 + cfg.validate()?; 58 + 57 59 println!("The following changes will be made:"); 58 60 for d in diffs.iter().clone() { 59 61 println!("{}", d);
+33
src/config.rs
··· 70 70 71 71 #[serde(skip_serializing_if = "Option::is_none")] 72 72 pub doppler: Option<bool>, 73 + 74 + #[serde(skip_serializing_if = "Option::is_none")] 75 + pub npm: Option<HashMap<String, String>>, 73 76 } 74 77 75 78 impl Configuration { ··· 89 92 ssh: None, 90 93 neofetch: None, 91 94 doppler: None, 95 + npm: None, 92 96 } 97 + } 98 + 99 + pub fn validate(&self) -> Result<()> { 100 + if self.npm.is_some() { 101 + let has_node_via_mise = self 102 + .mise 103 + .as_ref() 104 + .map(|mise| mise.contains_key("node")) 105 + .unwrap_or(false); 106 + 107 + let has_node_via_pkgx = self 108 + .pkgx 109 + .as_ref() 110 + .map(|pkgx| pkgx.contains_key("node") || pkgx.contains_key("nodejs.org")) 111 + .unwrap_or(false); 112 + 113 + if !has_node_via_mise && !has_node_via_pkgx { 114 + return Err(Error::msg( 115 + "npm packages specified but node is not configured. Please add node to either mise or pkgx configuration.", 116 + )); 117 + } 118 + } 119 + Ok(()) 93 120 } 94 121 95 122 pub fn setup_environment(&self, dry_run: bool, diffs: Vec<Diff>) -> Result<()> { ··· 280 307 steps.push(SetupStep::Doppler(doppler_enabled)); 281 308 } 282 309 } 310 + "npm" => { 311 + if let Some(npm_packages) = &self.npm { 312 + steps.push(SetupStep::Npm(npm_packages)); 313 + } 314 + } 283 315 _ => {} // Ignore unknown configuration keys 284 316 } 285 317 } ··· 351 383 }), 352 384 neofetch: Some(true), 353 385 doppler: Some(false), 386 + npm: None, 354 387 } 355 388 } 356 389 }
+1
src/diff.rs
··· 352 352 diffs.extend(compare_hashmap("pkgx", &old.pkgx, &new.pkgx)); 353 353 diffs.extend(compare_hashmap("curl", &old.curl, &new.curl)); 354 354 diffs.extend(compare_hashmap("alias", &old.alias, &new.alias)); 355 + diffs.extend(compare_hashmap("npm", &old.npm, &new.npm)); 355 356 356 357 diffs.extend(compare_vec("apt-get", &old.apt_get, &new.apt_get)); 357 358