From aa283bbe1b3dd34f9ce13139ef57982b585fcc80 Mon Sep 17 00:00:00 2001 From: Sander Date: Fri, 3 Jan 2025 12:34:07 +0400 Subject: [PATCH 1/6] cachix: disable when `cachix.enable` is false --- devenv/src/cnix.rs | 42 ++++++++++++++++++++++++++++-------------- src/modules/cachix.nix | 9 ++++----- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/devenv/src/cnix.rs b/devenv/src/cnix.rs index 7f881f735..f830d082e 100644 --- a/devenv/src/cnix.rs +++ b/devenv/src/cnix.rs @@ -607,17 +607,23 @@ impl<'a> Nix<'a> { ..self.options }; let caches_raw = self.eval(&["devenv.cachix"]).await?; - let cachix = serde_json::from_str(&caches_raw).expect("Failed to parse JSON"); - let known_keys = if let Ok(known_keys) = - std::fs::read_to_string(self.cachix_trusted_keys.as_path()) - { - serde_json::from_str(&known_keys).expect("Failed to parse JSON") - } else { - HashMap::new() - }; + let cachix_config: CachixConfig = + serde_json::from_str(&caches_raw).expect("Failed to parse JSON"); + + // Return empty caches if the Cachix integration is disabled + if !cachix_config.enable { + *self.cachix_caches.borrow_mut() = Some(CachixCaches::default()); + return Ok(Ref::map(self.cachix_caches.borrow(), |option| { + option.as_ref().unwrap() + })); + } + + let known_keys = std::fs::read_to_string(self.cachix_trusted_keys.as_path()) + .map(|known_keys| serde_json::from_str(&known_keys).expect("Failed to parse JSON")) + .unwrap_or_default(); let mut caches = CachixCaches { - caches: cachix, + caches: cachix_config.caches, known_keys, }; @@ -626,7 +632,7 @@ impl<'a> Nix<'a> { for name in caches.caches.pull.iter() { if !caches.known_keys.contains_key(name) { let mut request = - client.get(&format!("https://cachix.org/api/v1/cache/{}", name)); + client.get(format!("https://cachix.org/api/v1/cache/{}", name)); if let Ok(ret) = env::var("CACHIX_AUTH_TOKEN") { request = request.bearer_auth(ret); } @@ -857,13 +863,21 @@ fn display_command(cmd: &std::process::Command) -> String { format!("{command} {args}") } -#[derive(Deserialize, Clone)] +/// The Cachix module configuration +#[derive(Deserialize, Default, Clone)] +pub struct CachixConfig { + enable: bool, + #[serde(flatten)] + caches: Cachix, +} + +#[derive(Deserialize, Default, Clone)] pub struct Cachix { - pull: Vec, - push: Option, + pub pull: Vec, + pub push: Option, } -#[derive(Deserialize, Clone)] +#[derive(Deserialize, Default, Clone)] pub struct CachixCaches { caches: Cachix, known_keys: HashMap, diff --git a/src/modules/cachix.nix b/src/modules/cachix.nix index 054e084fb..f848099d3 100644 --- a/src/modules/cachix.nix +++ b/src/modules/cachix.nix @@ -12,13 +12,13 @@ in pull = lib.mkOption { type = lib.types.listOf lib.types.str; - description = "What caches to pull from."; - default = [ ]; + description = "Which Cachix caches to pull from."; + default = [ "devenv" ]; }; push = lib.mkOption { type = lib.types.nullOr lib.types.str; - description = "What cache to push to. Automatically also adds it to the list of caches to pull from."; + description = "Which Cachix cache to push to. This cache is also added to `cachix.pull`."; default = null; }; @@ -29,8 +29,7 @@ in }; config = lib.mkIf cfg.enable { - cachix.pull = [ "devenv" ] - ++ (lib.optionals (cfg.push != null) [ config.cachix.push ]); + cachix.pull = lib.optional (cfg.push != null) config.cachix.push; warnings = lib.optionals (!config.devenv.flakesIntegration && lib.versionOlder config.devenv.cliVersion "1.0") [ '' From 5dc06a2cea0b9921f879bf9db490966135018fa7 Mon Sep 17 00:00:00 2001 From: Sander Date: Fri, 3 Jan 2025 12:39:14 +0400 Subject: [PATCH 2/6] cachix: remove missing known keys from the caching setup hint --- devenv/src/cnix.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devenv/src/cnix.rs b/devenv/src/cnix.rs index f830d082e..7eaff65ce 100644 --- a/devenv/src/cnix.rs +++ b/devenv/src/cnix.rs @@ -719,9 +719,10 @@ impl<'a> Nix<'a> { .map(|s| s.split_whitespace().collect::>()); if let Some(trusted_public_keys) = trusted_public_keys { - for (_name, key) in caches.known_keys.iter() { - if !trusted_public_keys.iter().any(|p| p == key) { - missing_public_keys.push(key.clone()); + let mut known_keys = caches.known_keys.values(); + for key in trusted_public_keys.iter() { + if !known_keys.any(|k| k == key) { + missing_public_keys.push(key.to_string()); } } } From 82fabecbf274db78e9cba6f0140ab3a4dc83c542 Mon Sep 17 00:00:00 2001 From: Sander Date: Sun, 5 Jan 2025 13:39:27 +0100 Subject: [PATCH 3/6] cachix: test cache config detection --- devenv/src/cnix.rs | 133 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 30 deletions(-) diff --git a/devenv/src/cnix.rs b/devenv/src/cnix.rs index 7eaff65ce..bfdfec575 100644 --- a/devenv/src/cnix.rs +++ b/devenv/src/cnix.rs @@ -697,36 +697,11 @@ impl<'a> Nix<'a> { // Check if all of the requested caches and their public keys are in the substituters and trusted-public-keys lists. // If not, suggest actions to remedy the issue. if trusted == Some(0) { - let mut missing_caches = Vec::new(); - let mut missing_public_keys = Vec::new(); - - if let Ok(nix_conf) = self.get_nix_config().await { - let substituters = nix_conf - .get("substituters") - .map(|s| s.split_whitespace().collect::>()); - - if let Some(substituters) = substituters { - for cache in caches.caches.pull.iter() { - let cache_url = format!("https://{}.cachix.org", cache); - if !substituters.iter().any(|s| s == &cache_url) { - missing_caches.push(cache_url); - } - } - } - - let trusted_public_keys = nix_conf - .get("trusted-public-keys") - .map(|s| s.split_whitespace().collect::>()); - - if let Some(trusted_public_keys) = trusted_public_keys { - let mut known_keys = caches.known_keys.values(); - for key in trusted_public_keys.iter() { - if !known_keys.any(|k| k == key) { - missing_public_keys.push(key.to_string()); - } - } - } - } + let (missing_caches, missing_public_keys) = self + .get_nix_config() + .await + .map(|nix_conf| detect_missing_caches(&caches, nix_conf)) + .unwrap_or_default(); if !missing_caches.is_empty() || !missing_public_keys.is_empty() { if !Path::new("/etc/NIXOS").exists() { @@ -895,6 +870,50 @@ struct StorePing { trusted: Option, } +fn detect_missing_caches(caches: &CachixCaches, nix_conf: NixConf) -> (Vec, Vec) { + let mut missing_caches = Vec::new(); + let mut missing_public_keys = Vec::new(); + + let substituters = nix_conf + .get("substituters") + .map(|s| s.split_whitespace().collect::>()); + let extra_substituters = nix_conf + .get("extra-substituters") + .map(|s| s.split_whitespace().collect::>()); + let all_substituters = substituters + .into_iter() + .flatten() + .chain(extra_substituters.into_iter().flatten()) + .collect::>(); + + for cache in caches.caches.pull.iter() { + let cache_url = format!("https://{}.cachix.org", cache); + if !all_substituters.iter().any(|s| s == &cache_url) { + missing_caches.push(cache_url); + } + } + + let trusted_public_keys = nix_conf + .get("trusted-public-keys") + .map(|s| s.split_whitespace().collect::>()); + let extra_trusted_public_keys = nix_conf + .get("extra-trusted-public-keys") + .map(|s| s.split_whitespace().collect::>()); + let all_trusted_public_keys = trusted_public_keys + .into_iter() + .flatten() + .chain(extra_trusted_public_keys.into_iter().flatten()) + .collect::>(); + + for (_name, key) in caches.known_keys.iter() { + if !all_trusted_public_keys.iter().any(|p| p == key) { + missing_public_keys.push(key.clone()); + } + } + + (missing_caches, missing_public_keys) +} + #[cfg(test)] mod tests { use super::*; @@ -919,4 +938,58 @@ mod tests { let store_ping: StorePing = serde_json::from_str(store_ping).unwrap(); assert_eq!(store_ping.trusted, Some(0)); } + + #[test] + fn test_missing_substituters() { + let mut cachix = CachixCaches::default(); + cachix.caches.pull = vec!["cache1".to_string(), "cache2".to_string()]; + cachix + .known_keys + .insert("cache1".to_string(), "key1".to_string()); + cachix + .known_keys + .insert("cache2".to_string(), "key2".to_string()); + let nix_conf = NixConf::parse_stdout( + r#" + substituters = https://cache1.cachix.org https://cache3.cachix.org + trusted-public-keys = key1 key3 + "# + .as_bytes(), + ) + .expect("Failed to parse NixConf"); + assert_eq!( + detect_missing_caches(&cachix, nix_conf), + ( + vec!["https://cache2.cachix.org".to_string()], + vec!["key2".to_string()] + ) + ); + } + + #[test] + fn test_extra_missing_substituters() { + let mut cachix = CachixCaches::default(); + cachix.caches.pull = vec!["cache1".to_string(), "cache2".to_string()]; + cachix + .known_keys + .insert("cache1".to_string(), "key1".to_string()); + cachix + .known_keys + .insert("cache2".to_string(), "key2".to_string()); + let nix_conf = NixConf::parse_stdout( + r#" + extra-substituters = https://cache1.cachix.org https://cache3.cachix.org + extra-trusted-public-keys = key1 key3 + "# + .as_bytes(), + ) + .expect("Failed to parse NixConf"); + assert_eq!( + detect_missing_caches(&cachix, nix_conf), + ( + vec!["https://cache2.cachix.org".to_string()], + vec!["key2".to_string()] + ) + ); + } } From 085a7b8c4a510b93184bea2b7fadc8f4331099a6 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:55:26 +0000 Subject: [PATCH 4/6] Auto generate docs/reference/options.md --- docs/reference/options.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/reference/options.md b/docs/reference/options.md index 0542f4619..f42abb939 100644 --- a/docs/reference/options.md +++ b/docs/reference/options.md @@ -923,7 +923,7 @@ package -What caches to pull from. +Which Cachix caches to pull from. @@ -933,7 +933,12 @@ list of string *Default:* -` [ ] ` + +``` +[ + "devenv" +] +``` *Declared by:* - [https://github.com/cachix/devenv/blob/main/src/modules/cachix.nix](https://github.com/cachix/devenv/blob/main/src/modules/cachix.nix) @@ -944,7 +949,7 @@ list of string -What cache to push to. Automatically also adds it to the list of caches to pull from. +Which Cachix cache to push to. This cache is also added to ` cachix.pull `. From f3242e522fb69c92c1a2bcd5ed472f48dade5915 Mon Sep 17 00:00:00 2001 From: Sander Date: Mon, 6 Jan 2025 16:54:32 +0100 Subject: [PATCH 5/6] cachix: apply default devenv cache in config, but add default to docs --- src/modules/cachix.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/cachix.nix b/src/modules/cachix.nix index f848099d3..f1e087615 100644 --- a/src/modules/cachix.nix +++ b/src/modules/cachix.nix @@ -13,7 +13,8 @@ in pull = lib.mkOption { type = lib.types.listOf lib.types.str; description = "Which Cachix caches to pull from."; - default = [ "devenv" ]; + default = [ ]; + defaultText = lib.literalExpression ''[ "devenv" ]''; }; push = lib.mkOption { @@ -29,7 +30,8 @@ in }; config = lib.mkIf cfg.enable { - cachix.pull = lib.optional (cfg.push != null) config.cachix.push; + cachix.pull = [ "devenv" ] + ++ (lib.optional (cfg.push != null) config.cachix.push); warnings = lib.optionals (!config.devenv.flakesIntegration && lib.versionOlder config.devenv.cliVersion "1.0") [ '' From a7218fe3be689dee2576833a456b4a04aeb774fb Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:10:45 +0000 Subject: [PATCH 6/6] Auto generate docs/reference/options.md --- docs/reference/options.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/reference/options.md b/docs/reference/options.md index f42abb939..35554c59f 100644 --- a/docs/reference/options.md +++ b/docs/reference/options.md @@ -933,12 +933,7 @@ list of string *Default:* - -``` -[ - "devenv" -] -``` +` [ "devenv" ] ` *Declared by:* - [https://github.com/cachix/devenv/blob/main/src/modules/cachix.nix](https://github.com/cachix/devenv/blob/main/src/modules/cachix.nix)