Skip to content

Commit 0fdc00b

Browse files
committed
Add --option CLI flag for overriding configuration options.
It requires to specify the infered Nix type: --option languages.redis.enable:bool true This is usefor for temporary changing the configuration, like testing a matrix of options.
1 parent 6599a8c commit 0fdc00b

File tree

6 files changed

+138
-0
lines changed

6 files changed

+138
-0
lines changed

devenv/src/cli.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,15 @@ pub struct GlobalOptions {
144144
help = "Override inputs in devenv.yaml."
145145
)]
146146
pub override_input: Vec<String>,
147+
148+
#[arg(
149+
long,
150+
global = true,
151+
num_args = 2,
152+
value_delimiter = ' ',
153+
help = "Override configuration options with typed values, e.g. --option languages.rust.version:string beta"
154+
)]
155+
pub option: Vec<String>,
147156
}
148157

149158
impl Default for GlobalOptions {
@@ -165,6 +174,7 @@ impl Default for GlobalOptions {
165174
nix_debugger: false,
166175
nix_option: vec![],
167176
override_input: vec![],
177+
option: vec![],
168178
}
169179
}
170180
}

devenv/src/devenv.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,52 @@ impl Devenv {
886886
fs::create_dir_all(&self.devenv_runtime)
887887
.unwrap_or_else(|_| panic!("Failed to create {}", self.devenv_runtime.display()));
888888

889+
// Create cli-options.nix if there are CLI options
890+
if !self.global_options.option.is_empty() {
891+
let mut cli_options = String::from("{\n");
892+
893+
const SUPPORTED_TYPES: &[&str] = &["string", "int", "float", "bool", "path"];
894+
895+
for chunk in self.global_options.option.chunks_exact(2) {
896+
// Parse the path and type from the first value
897+
let key_parts: Vec<&str> = chunk[0].split(':').collect();
898+
if key_parts.len() < 2 {
899+
panic!("Invalid option format: '{}'. Must include type, e.g. 'languages.rust.version:string'. Supported types: {}",
900+
chunk[0], SUPPORTED_TYPES.join(", "));
901+
}
902+
903+
let path = key_parts[0];
904+
let type_name = key_parts[1];
905+
906+
// Format value based on type
907+
let value = match type_name {
908+
"string" => format!("\"{}\"", &chunk[1]),
909+
"int" => chunk[1].clone(),
910+
"float" => chunk[1].clone(),
911+
"bool" => chunk[1].clone(), // true/false will work directly in Nix
912+
"path" => format!("./{}", &chunk[1]), // relative path
913+
_ => panic!(
914+
"Unsupported type: '{}'. Supported types: {}",
915+
type_name,
916+
SUPPORTED_TYPES.join(", ")
917+
),
918+
};
919+
920+
cli_options.push_str(&format!(" {} = {};\n", path, value));
921+
}
922+
923+
cli_options.push_str("}\n");
924+
925+
fs::write(self.devenv_dotfile.join("cli-options.nix"), cli_options)
926+
.expect("Failed to write cli-options.nix");
927+
} else {
928+
// Remove the file if it exists but there are no CLI options
929+
let cli_options_path = self.devenv_dotfile.join("cli-options.nix");
930+
if cli_options_path.exists() {
931+
fs::remove_file(&cli_options_path).expect("Failed to remove cli-options.nix");
932+
}
933+
}
934+
889935
// create flake.devenv.nix
890936
let vars = indoc::formatdoc!(
891937
"version = \"{}\";

devenv/src/flake.tmpl.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
./devenv.nix
9292
(devenv.devenv or { })
9393
(if builtins.pathExists ./devenv.local.nix then ./devenv.local.nix else { })
94+
(if builtins.pathExists (devenv_dotfile + "/cli-options.nix") then import (devenv_dotfile + "/cli-options.nix") else { })
9495
];
9596
};
9697
config = project.config;

tests/cli-options/.test.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env bash
2+
set -eu -o pipefail
3+
4+
# Test CLI options feature
5+
cd "$(dirname "$0")"
6+
7+
# Test basic string and bool types
8+
OUTPUT=$(devenv --option languages.rust.version:string beta --option services.redis.enable:bool true info)
9+
10+
# Check for expected RUST_VERSION
11+
echo "$OUTPUT" | grep -E "RUST_VERSION:.*beta" || {
12+
echo "ERROR: Expected CLI option override for RUST_VERSION to be 'beta'"
13+
exit 1
14+
}
15+
16+
# Check for expected REDIS_ENABLED
17+
echo "$OUTPUT" | grep -E "REDIS_ENABLED:.*true" || {
18+
echo "ERROR: Expected CLI option override for REDIS_ENABLED to be 'true'"
19+
exit 1
20+
}
21+
22+
# Test int type
23+
OUTPUT=$(devenv --option env.TEST_INT:int 42 info)
24+
echo "$OUTPUT" | grep -E "TEST_INT:.*42" || {
25+
echo "ERROR: Expected CLI option override for TEST_INT to be '42'"
26+
exit 1
27+
}
28+
29+
# Test float type
30+
OUTPUT=$(devenv --option env.TEST_FLOAT:float 3.14 info)
31+
echo "$OUTPUT" | grep -E "TEST_FLOAT:.*3.14" || {
32+
echo "ERROR: Expected CLI option override for TEST_FLOAT to be '3.14'"
33+
exit 1
34+
}
35+
36+
# Test path type
37+
OUTPUT=$(devenv --option env.TEST_PATH:path somepath info)
38+
echo "$OUTPUT" | grep -E "TEST_PATH:.*/somepath" || {
39+
echo "ERROR: Expected CLI option override for TEST_PATH to include 'somepath'"
40+
exit 1
41+
}
42+
43+
# Test invalid type (should fail)
44+
if devenv --option languages.rust.version:invalid value info &> /dev/null; then
45+
echo "ERROR: Expected CLI option with invalid type to fail"
46+
exit 1
47+
fi
48+
49+
# Test missing type (should fail)
50+
if devenv --option languages.rust.version value info &> /dev/null; then
51+
echo "ERROR: Expected CLI option without type specification to fail"
52+
exit 1
53+
fi

tests/cli-options/devenv.nix

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{ pkgs, lib, config, ... }:
2+
3+
{
4+
# Default language - will be overridden by CLI option
5+
languages.rust.enable = lib.mkDefault true;
6+
languages.rust.version = "stable";
7+
8+
# Default services - will be overridden by CLI option
9+
services.redis.enable = lib.mkDefault false;
10+
11+
# Environment variables to expose configuration values
12+
env = {
13+
# Base config from the original test
14+
RUST_VERSION = config.languages.rust.version;
15+
REDIS_ENABLED = builtins.toString config.services.redis.enable;
16+
17+
# For testing different value types
18+
TEST_INT = lib.mkDefault "0";
19+
TEST_FLOAT = lib.mkDefault "0.0";
20+
TEST_PATH = lib.mkDefault "";
21+
};
22+
}

tests/cli-options/devenv.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
inputs:
2+
fenix:
3+
url: github:nix-community/fenix
4+
inputs:
5+
nixpkgs:
6+
follows: nixpkgs

0 commit comments

Comments
 (0)