Skip to content

Commit 3c85748

Browse files
fix: solve false positive ChannelPriorityCombinationError (#4943)
Co-authored-by: Ruben Arts <[email protected]>
1 parent 70d7390 commit 3c85748

File tree

2 files changed

+94
-2
lines changed

2 files changed

+94
-2
lines changed

crates/pixi_core/src/workspace/environment.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,4 +1026,95 @@ mod tests {
10261026
pixi_manifest::SolveStrategy::Highest
10271027
);
10281028
}
1029+
1030+
/// Test that combining channel priorities from multiple features
1031+
/// results in a ChannelPriorityCombinationError if the priorities conflict.
1032+
#[test]
1033+
fn test_channel_priority_compatibility() {
1034+
use crate::workspace::grouped_environment::GroupedEnvironment;
1035+
1036+
// First part - priorities are compatible
1037+
{
1038+
let contents = r#"
1039+
[project]
1040+
name = "test"
1041+
platforms = ["linux-64"]
1042+
channels = ["conda-forge"]
1043+
channel-priority = "disabled"
1044+
1045+
[feature.feat1]
1046+
channel-priority = "disabled" # Is ok as it's the same as the default
1047+
1048+
[environments]
1049+
test = ["feat1"]
1050+
"#;
1051+
1052+
let workspace = Workspace::from_str(Path::new("pixi.toml"), contents).unwrap();
1053+
let env = workspace.environment("test").unwrap();
1054+
let grouped_env = GroupedEnvironment::from(env);
1055+
1056+
// This should not return an error because both features have the same channel-priority
1057+
let result = grouped_env.channel_priority();
1058+
assert!(
1059+
result.is_ok(),
1060+
"Channel priorities were expected to be compatible"
1061+
);
1062+
}
1063+
// Second part, priorities conflict
1064+
{
1065+
let contents = r#"
1066+
[project]
1067+
name = "test"
1068+
platforms = ["linux-64"]
1069+
channels = ["conda-forge"]
1070+
channel-priority = "disabled"
1071+
1072+
[feature.feat1]
1073+
channel-priority = "strict" # Conflicts with the default
1074+
1075+
[environments]
1076+
test = ["feat1"]
1077+
"#;
1078+
1079+
let workspace = Workspace::from_str(Path::new("pixi.toml"), contents).unwrap();
1080+
let env = workspace.environment("test").unwrap();
1081+
let grouped_env = GroupedEnvironment::from(env);
1082+
1083+
// This should return an error because the features have conflicting channel-priorities
1084+
let result = grouped_env.channel_priority();
1085+
assert!(
1086+
result.is_err(),
1087+
"Channel priorities were expected to be incompatible"
1088+
);
1089+
}
1090+
// Third part, there is a default priority, but the feature doesn't have a priority set.
1091+
{
1092+
let contents = r#"
1093+
[project]
1094+
name = "test"
1095+
platforms = ["linux-64"]
1096+
channels = ["conda-forge"]
1097+
channel-priority = "disabled"
1098+
1099+
# Adding a dependency, just so the feature has at least some data
1100+
[feature.feat1.dependencies]
1101+
python = ">=3.13"
1102+
1103+
[environments]
1104+
test = ["feat1"]
1105+
"#;
1106+
1107+
let workspace = Workspace::from_str(Path::new("pixi.toml"), contents).unwrap();
1108+
let env = workspace.environment("test").unwrap();
1109+
let grouped_env = GroupedEnvironment::from(env);
1110+
1111+
// This should not return an error because the default feature sets the priority
1112+
let result = grouped_env.channel_priority();
1113+
assert_eq!(
1114+
result.unwrap(),
1115+
Some(pixi_manifest::ChannelPriority::Disabled),
1116+
"Channel priorities were expected to be compatible"
1117+
);
1118+
}
1119+
}
10291120
}

crates/pixi_manifest/src/features_ext.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub trait FeaturesExt<'source>: HasWorkspaceManifest<'source> + HasFeaturesIter<
6868
.collect()
6969
}
7070

71-
/// Returns the channel priority, error on multiple values, return None if
71+
/// Returns the channel priority, error on multiple, different values, return None if
7272
/// no value is set.
7373
///
7474
/// When using multiple channel priorities over different features we should
@@ -77,7 +77,8 @@ pub trait FeaturesExt<'source>: HasWorkspaceManifest<'source> + HasFeaturesIter<
7777
let mut channel_priority = None;
7878
for feature in self.features() {
7979
if let Some(priority) = feature.channel_priority {
80-
if channel_priority == Some(priority) {
80+
// If we already have a priority and it's different, error
81+
if channel_priority.is_some() && channel_priority != Some(priority) {
8182
return Err(ChannelPriorityCombinationError);
8283
}
8384
channel_priority = Some(priority);

0 commit comments

Comments
 (0)