Skip to content
Open
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
2 changes: 2 additions & 0 deletions devenv/src/devenv/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod cachix;
mod container;
mod gc;
mod preflight;
mod search;

use super::{processes, tasks, util};
Expand Down Expand Up @@ -663,6 +664,7 @@ impl Devenv {
let Some(cnix) = self.cnix() else {
return Ok(None);
};
preflight::run(cnix).await?;
cachix::CachixIntegration::init(
cnix,
&self.cachix_manager,
Expand Down
70 changes: 70 additions & 0 deletions devenv/src/devenv/preflight.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! Pre-flight commands.
//!
//! Runs user-defined commands during devenv startup, before subsystems
//! that read `env::var()` (cachix, substituter netrc, …) initialize.
//! Stdout in `KEY=value` form is merged into the process environment.

use std::collections::BTreeMap;

use devenv_activity::{Activity, ActivityLevel, start};
use devenv_nix_backend::NixCBackend;
use miette::{IntoDiagnostic, Result, WrapErr};
use tracing::{debug, warn};

#[derive(serde::Deserialize)]
struct PreFlight {
command: String,
}

pub async fn run(cnix: &NixCBackend) -> Result<()> {
let activity =
start!(Activity::evaluate("Reading config.devenv.preFlight").level(ActivityLevel::Debug));
let json = match cnix.eval_attr("config.devenv.preFlight", &activity).await {
Ok(j) => j,
Err(_) => return Ok(()),
};
let entries: BTreeMap<String, PreFlight> = serde_json::from_str(&json)
.into_diagnostic()
.wrap_err("Failed to deserialize config.devenv.preFlight")?;

if entries.is_empty() {
return Ok(());
}

for (name, pf) in &entries {
debug!(name = %name, "running preFlight command");
let output = tokio::process::Command::new("sh")
.arg("-c")
.arg(&pf.command)
.output()
.await
.into_diagnostic()
.wrap_err_with(|| format!("preFlight `{name}` failed to spawn"))?;

if !output.status.success() {
warn!(
name = %name,
status = %output.status,
stderr = %String::from_utf8_lossy(&output.stderr).trim(),
"preFlight command failed",
);
continue;
}

for line in String::from_utf8_lossy(&output.stdout).lines() {
let Some((key, val)) = line.split_once('=') else {
continue;
};
let key = key.trim();
let val = val.trim();
if key.is_empty() {
continue;
}
// SAFETY: pre-flight runs before subsystems that read env::var().
// The point of this hook is to mutate process env.
unsafe { std::env::set_var(key, val) };
}
}

Ok(())
}
66 changes: 62 additions & 4 deletions docs/src/reference/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -3585,6 +3585,64 @@ string



## devenv.preFlight



Pre-flight commands run during devenv startup, before its
internal subsystems initialize. Useful for fetching tokens or
other values that devenv-core itself reads from the environment
early. Commands run in dependency-key alphabetical order.



*Type:*
attribute set of (submodule)



*Default:*

```nix
{ }
```



*Example:*

```nix
{
cachix-auth.command = "echo CACHIX_AUTH_TOKEN=$(get-token-somehow)";
}

```

*Declared by:*
- [https://github.com/cachix/devenv/blob/main/src/modules/top-level.nix](https://github.com/cachix/devenv/blob/main/src/modules/top-level.nix)



## devenv.preFlight.\<name>.command



Shell command run during devenv startup, before its internal
subsystems (cachix, substituter netrc, etc.) initialize.
Lines of ` KEY=value ` written to stdout are merged into
devenv’s process environment and become visible to those
subsystems.



*Type:*
string

*Declared by:*
- [https://github.com/cachix/devenv/blob/main/src/modules/top-level.nix](https://github.com/cachix/devenv/blob/main/src/modules/top-level.nix)



## devenv.warnOnNewVersion


Expand Down Expand Up @@ -5971,8 +6029,6 @@ submodule

## git-hooks.hooks.biome.enable



Whether to enable this pre-commit hook.


Expand Down Expand Up @@ -6031,6 +6087,8 @@ null or string or absolute path

## git-hooks.hooks.biome.settings.configPath



Path to the configuration JSON file


Expand Down Expand Up @@ -8244,8 +8302,6 @@ false

## git-hooks.hooks.isort.settings.flags



Flags passed to isort. See all available [here](https://pycqa.github.io/isort/docs/configuration/options.html).


Expand Down Expand Up @@ -8292,6 +8348,8 @@ one of “”, “black”, “django”, “pycharm”, “google”, “open_s

## git-hooks.hooks.lacheck



lacheck hook


Expand Down
27 changes: 27 additions & 0 deletions src/modules/top-level.nix
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,33 @@ in
};

devenv = {
preFlight = lib.mkOption {
type = types.attrsOf (types.submodule {
options.command = lib.mkOption {
type = types.str;
description = ''
Shell command run during devenv startup, before its internal
subsystems (cachix, substituter netrc, etc.) initialize.
Lines of `KEY=value` written to stdout are merged into
devenv's process environment and become visible to those
subsystems.
'';
};
});
default = { };
description = ''
Pre-flight commands run during devenv startup, before its
internal subsystems initialize. Useful for fetching tokens or
other values that devenv-core itself reads from the environment
early. Commands run in dependency-key alphabetical order.
'';
example = lib.literalExpression ''
{
cachix-auth.command = "echo CACHIX_AUTH_TOKEN=$(get-token-somehow)";
}
'';
};

root = lib.mkOption {
type = types.str;
internal = true;
Expand Down