Skip to content

Commit d06323c

Browse files
authored
fix: feature activation environment variable priority (#4282)
1 parent d051be2 commit d06323c

File tree

1 file changed

+65
-4
lines changed

1 file changed

+65
-4
lines changed

src/workspace/environment.rs

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,12 +298,21 @@ impl<'p> Environment<'p> {
298298
/// The environment variables of all features are combined in the order they
299299
/// are defined for the environment.
300300
pub(crate) fn activation_env(&self, platform: Option<Platform>) -> IndexMap<String, String> {
301-
self.features()
302-
.map(|f| f.activation_env(platform))
303-
.fold(IndexMap::new(), |mut acc, env| {
301+
// As self.features() would put "default" envs in the last item, but the "default" env priority should be the lowest.
302+
// Here, we use rfold (reverse fold) to ensure later features override earlier features
303+
// for environment variables. Processing features in reverse order means that
304+
// features appearing later in the list will have higher precedence.
305+
//
306+
// Example: If features: [override_feature, user_feature, default]
307+
// - rfold processes as: [default, user_feature, override_feature]
308+
// - Result: override_feature env vars take precedence over all others
309+
self.features().map(|f| f.activation_env(platform)).rfold(
310+
IndexMap::new(),
311+
|mut acc, env| {
304312
acc.extend(env.iter().map(|(k, v)| (k.clone(), v.clone())));
305313
acc
306-
})
314+
},
315+
)
307316
}
308317

309318
/// Validates that the given platform is supported by this environment.
@@ -652,6 +661,58 @@ mod tests {
652661
);
653662
}
654663

664+
#[test]
665+
fn test_activation_env_priority() {
666+
let manifest = Workspace::from_str(
667+
Path::new("pixi.toml"),
668+
r#"
669+
[project]
670+
name = "foobar"
671+
channels = []
672+
platforms = ["linux-64", "osx-64"]
673+
674+
[activation.env]
675+
FOO_VAR = "default"
676+
677+
[feature.foo.activation.env]
678+
FOO_VAR = "foo"
679+
680+
[feature.cuda1.activation.env]
681+
FOO_VAR = "cuda1"
682+
683+
[feature.cuda2.activation.env]
684+
FOO_VAR = "cuda2"
685+
686+
[environments]
687+
foo = ["foo"]
688+
cuda = ["cuda1", "cuda2"]
689+
"#,
690+
)
691+
.unwrap();
692+
693+
let default_env = manifest.default_environment();
694+
let foo_env = manifest.environment("foo").unwrap();
695+
let cuda_env = manifest.environment("cuda").unwrap();
696+
assert_eq!(
697+
default_env.activation_env(None),
698+
indexmap! {
699+
"FOO_VAR".to_string() => "default",
700+
}
701+
);
702+
assert_eq!(
703+
foo_env.activation_env(None),
704+
indexmap! {
705+
"FOO_VAR".to_string() => "foo",
706+
}
707+
);
708+
assert_eq!(
709+
cuda_env.activation_env(None),
710+
indexmap! {
711+
"FOO_VAR".to_string() => "cuda1",
712+
}
713+
);
714+
}
715+
655716
#[test]
656717
fn test_channel_feature_priority() {
657718
let manifest = Workspace::from_str(

0 commit comments

Comments
 (0)