Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cachix: use cachix.enable to disable cache setup logic #1660

Merged
merged 6 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 131 additions & 43 deletions devenv/src/cnix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand All @@ -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);
}
Expand Down Expand Up @@ -691,35 +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::<Vec<_>>());

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::<Vec<_>>());

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 (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() {
Expand Down Expand Up @@ -857,13 +839,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<String>,
push: Option<String>,
pub pull: Vec<String>,
pub push: Option<String>,
}

#[derive(Deserialize, Clone)]
#[derive(Deserialize, Default, Clone)]
pub struct CachixCaches {
caches: Cachix,
known_keys: HashMap<String, String>,
Expand All @@ -880,6 +870,50 @@ struct StorePing {
trusted: Option<u8>,
}

fn detect_missing_caches(caches: &CachixCaches, nix_conf: NixConf) -> (Vec<String>, Vec<String>) {
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::<Vec<_>>());
let extra_substituters = nix_conf
.get("extra-substituters")
.map(|s| s.split_whitespace().collect::<Vec<_>>());
let all_substituters = substituters
.into_iter()
.flatten()
.chain(extra_substituters.into_iter().flatten())
.collect::<Vec<_>>();

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::<Vec<_>>());
let extra_trusted_public_keys = nix_conf
.get("extra-trusted-public-keys")
.map(|s| s.split_whitespace().collect::<Vec<_>>());
let all_trusted_public_keys = trusted_public_keys
.into_iter()
.flatten()
.chain(extra_trusted_public_keys.into_iter().flatten())
.collect::<Vec<_>>();

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::*;
Expand All @@ -904,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()]
)
);
}
}
6 changes: 3 additions & 3 deletions docs/reference/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ package



What caches to pull from.
Which Cachix caches to pull from.



Expand All @@ -933,7 +933,7 @@ 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)
Expand All @@ -944,7 +944,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 `.



Expand Down
7 changes: 4 additions & 3 deletions src/modules/cachix.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ in

pull = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "What caches to pull from.";
description = "Which Cachix caches to pull from.";
default = [ ];
defaultText = lib.literalExpression ''[ "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;
};

Expand All @@ -30,7 +31,7 @@ in

config = lib.mkIf cfg.enable {
cachix.pull = [ "devenv" ]
++ (lib.optionals (cfg.push != null) [ config.cachix.push ]);
++ (lib.optional (cfg.push != null) config.cachix.push);

warnings = lib.optionals (!config.devenv.flakesIntegration && lib.versionOlder config.devenv.cliVersion "1.0") [
''
Expand Down