Skip to content

Commit 019d2da

Browse files
domenkozarclaude
andcommitted
Use cachix from PATH instead of evaluating the cachix derivation
The cachix daemon spawn was failing because config.cachix.binary pointed to a Nix store path that was never realized. The devenv wrapper already bundles cachix on PATH, so prefer that. Only fall back to evaluating config.cachix.binary when cachix is not on PATH. Additionally, split the cachix config evaluation into individual fields (enable, pull, push) to avoid forcing the expensive binary field (which evaluates the cachix derivation) in the common case. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f30a244 commit 019d2da

5 files changed

Lines changed: 41 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Bug Fixes
66

7+
- Fixed cachix daemon failing to start because the evaluated store path for the cachix binary was never realized. Now uses the cachix bundled with devenv via PATH, falling back to evaluating `cachix.binary` only when needed.
78
- Fixed warning messages from Nix not being forwarded and displayed during evaluation.
89
- Fixed `devenv test` leaving orphaned processes after test failures by ensuring processes are always stopped before propagating errors.
910
- Fixed adding a new input to `devenv.yaml` causing all existing inputs to be re-fetched instead of only resolving the new one ([#2688](https://github.com/cachix/devenv/issues/2688)).

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

devenv-core/src/cachix.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,6 @@ pub struct Cachix {
200200
pub struct CachixCacheInfo {
201201
pub caches: Cachix,
202202
pub known_keys: BTreeMap<String, String>,
203-
/// Path to the cachix binary
204-
pub binary: PathBuf,
205203
}
206204

207205
/// Cachix API response containing cache metadata

devenv-nix-backend/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ nix-bindings-fetchers.workspace = true
3535
nix-bindings-bindgen-raw.workspace = true
3636
nix-cmd.workspace = true
3737
tempfile.workspace = true
38+
which.workspace = true
3839

3940
[dev-dependencies]
4041
devenv-nix-backend-macros.workspace = true

devenv-nix-backend/src/nix_backend.rs

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use tokio_shutdown::Shutdown;
1818
use devenv_activity::{Activity, ActivityInstrument, ActivityLevel};
1919
use devenv_cache_core::compute_string_hash;
2020
use devenv_core::PortAllocator;
21-
use devenv_core::cachix::{CachixCacheInfo, CachixConfig, CachixManager};
21+
use devenv_core::cachix::{Cachix, CachixCacheInfo, CachixManager};
2222
use devenv_core::config::{Input, NixpkgsConfig};
2323
use devenv_core::eval_op::EvalOp;
2424
use devenv_core::nix_args::NixArgs;
@@ -817,48 +817,57 @@ in cfg // {{
817817
Ok(())
818818
}
819819

820-
/// Get cachix configuration from devenv.nix via eval cache.
820+
/// Evaluate a single cachix config field via the eval cache.
821821
///
822-
/// Returns the cachix configuration if enabled, None otherwise.
823-
async fn get_cachix_config(&self) -> Result<Option<CachixCacheInfo>> {
824-
if self.nix_settings.offline {
825-
return Ok(None);
826-
}
827-
822+
/// Evaluating individual fields avoids forcing expensive fields like `binary`
823+
/// (which requires evaluating the cachix derivation) when they are not needed.
824+
async fn eval_cachix_field<T: serde::de::DeserializeOwned>(&self, field: &str) -> Result<T> {
828825
let caching_state = self
829826
.caching_eval_state
830827
.get()
831828
.expect("assemble() must be called first");
832829

833-
let cache_key = caching_state.cache_key("config.cachix");
834-
let activity = Activity::evaluate("Checking cachix config")
830+
let attr_path = format!("config.cachix.{}", field);
831+
let cache_key = caching_state.cache_key(&attr_path);
832+
let activity = Activity::evaluate(format!("Checking cachix.{}", field))
835833
.level(ActivityLevel::Debug)
836834
.start();
837835

838-
let (json_str, _cache_hit) = async {
836+
let (json_str, _) = async {
839837
caching_state
840838
.cached_eval()
841839
.eval(&cache_key, &activity, || async {
842-
self.eval_attr_uncached("config.cachix", "config.cachix", &activity)
840+
self.eval_attr_uncached(&attr_path, &attr_path, &activity)
843841
})
844842
.await
845843
}
846844
.in_activity(&activity)
847845
.await
848846
.map_err(cache_error_to_miette)?;
849847

850-
let cachix_config: CachixConfig = match serde_json::from_str(&json_str) {
851-
Ok(config) => config,
852-
Err(e) => {
853-
tracing::warn!("Failed to parse cachix config: {}", e);
854-
return Ok(None);
855-
}
856-
};
848+
serde_json::from_str(&json_str)
849+
.into_diagnostic()
850+
.wrap_err(format!("Failed to parse cachix.{}", field))
851+
}
852+
853+
/// Get cachix configuration from devenv.nix via eval cache.
854+
///
855+
/// Only evaluates lightweight fields (enable, pull, push). The expensive
856+
/// `binary` field (which forces the cachix derivation) is evaluated
857+
/// separately via `eval_cachix_field` only when needed.
858+
async fn get_cachix_config(&self) -> Result<Option<CachixCacheInfo>> {
859+
if self.nix_settings.offline {
860+
return Ok(None);
861+
}
857862

858-
if !cachix_config.enable {
863+
let enable: bool = self.eval_cachix_field("enable").await?;
864+
if !enable {
859865
return Ok(None);
860866
}
861867

868+
let pull: Vec<String> = self.eval_cachix_field("pull").await?;
869+
let push: Option<String> = self.eval_cachix_field("push").await?;
870+
862871
// Load known keys from trusted keys file
863872
let trusted_keys_path = &self.cachix_manager.paths.trusted_keys;
864873
let known_keys = if trusted_keys_path.exists() {
@@ -871,9 +880,8 @@ in cfg // {{
871880
};
872881

873882
Ok(Some(CachixCacheInfo {
874-
caches: cachix_config.caches,
883+
caches: Cachix { pull, push },
875884
known_keys,
876-
binary: cachix_config.binary,
877885
}))
878886
}
879887

@@ -1238,8 +1246,14 @@ impl NixBackend for NixRustBackend {
12381246
if let Some(cachix_config) = self.get_cachix_config().await? {
12391247
self.apply_cachix_substituters(&cachix_config).await?;
12401248
if let Some(ref push_cache) = cachix_config.caches.push {
1241-
self.init_cachix_daemon(push_cache, &cachix_config.binary)
1242-
.await?;
1249+
// Prefer "cachix" from PATH (bundled via the devenv wrapper).
1250+
// Only evaluate config.cachix.binary (which forces the cachix
1251+
// derivation) as a fallback when cachix is not on PATH.
1252+
let binary = match which::which("cachix") {
1253+
Ok(path) => path,
1254+
Err(_) => self.eval_cachix_field::<PathBuf>("binary").await?,
1255+
};
1256+
self.init_cachix_daemon(push_cache, &binary).await?;
12431257
}
12441258
}
12451259

0 commit comments

Comments
 (0)