Skip to content

Commit e33dcb1

Browse files
authored
Merge branch 'main' into push-nuwzykznqkxz
2 parents f57e52a + ab6e912 commit e33dcb1

File tree

28 files changed

+824
-143
lines changed

28 files changed

+824
-143
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ jobs:
278278
- name: Test pixi
279279
run: pixi run test-slow --retries 2
280280
env:
281+
CARGO_PROFILE_DEV_DEBUG: 0 # Disable debuginfo only on windows because it takes a lot of space
281282
CARGO_TARGET_DIR: ${{ env.DEV_DRIVE }}/target
282283
PIXI_TEST_R2_ACCESS_KEY_ID: ${{ secrets.PIXI_TEST_R2_ACCESS_KEY_ID }}
283284
PIXI_TEST_R2_SECRET_ACCESS_KEY: ${{ secrets.PIXI_TEST_R2_SECRET_ACCESS_KEY }}

Cargo.lock

Lines changed: 13 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ crossbeam-channel = "0.5.14"
5555
csv = "1.3.1"
5656
ctrlc = "3.4.5"
5757
dashmap = "6.1.0"
58-
deno_task_shell = "0.26.2"
58+
deno_task_shell = "0.28.0"
5959
derive_more = "2.0.1"
6060
dialoguer = "0.12.0"
6161
digest = "0.10"

crates/pixi-build-backend/src/generated_recipe.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,13 @@ impl GeneratedRecipe {
195195
documentation: derive_value!(documentation).map(Value::Concrete),
196196
repository: derive_value!(repository).map(Value::Concrete),
197197
license_file: match model.license_file {
198-
Some(v) => Some(Value::Concrete(v.display().to_string())),
198+
Some(v) => Some(vec![Value::Concrete(v.display().to_string())]),
199199
None => provider
200200
.license_files()
201201
.map_err(|e| {
202202
GenerateRecipeError::MetadataProviderError(String::from("license-files"), e)
203203
})?
204-
.map(|files| Value::Concrete(files.join(", "))),
204+
.map(|files| files.into_iter().map(Value::Concrete).collect()),
205205
},
206206
summary: provider
207207
.summary()

crates/pixi/tests/integration_rust/build_tests.rs

Lines changed: 226 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use fs_err as fs;
2-
use pixi_build_backend_passthrough::{ObservableBackend, PassthroughBackend};
2+
use pixi_build_backend_passthrough::{BackendEvent, ObservableBackend, PassthroughBackend};
33
use pixi_build_frontend::BackendOverride;
44
use pixi_consts::consts;
55
use rattler_conda_types::{Platform, package::RunExportsJson};
@@ -812,3 +812,228 @@ test-source-pkg = {{ path = "./source-package" }}
812812
"Second invocation should report lock-file is already up to date"
813813
);
814814
}
815+
816+
/// Test that verifies changing `[package.build.config]` invalidates the metadata cache
817+
/// and causes the build backend to be re-queried.
818+
///
819+
/// This tests the fix for issue #5309 where changes to build configuration
820+
/// (like `noarch = true` to `noarch = false`) did not invalidate the metadata cache.
821+
///
822+
/// The test uses ObservableBackend to verify that the backend is called again
823+
/// when the configuration changes.
824+
#[tokio::test]
825+
async fn test_build_config_change_invalidates_cache() {
826+
setup_tracing();
827+
828+
// Create an observable passthrough backend to track calls
829+
let passthrough = PassthroughBackend::instantiator();
830+
let (instantiator, mut observer) = ObservableBackend::instantiator(passthrough);
831+
let backend_override = BackendOverride::from_memory(instantiator);
832+
833+
let pixi = PixiControl::new()
834+
.unwrap()
835+
.with_backend_override(backend_override);
836+
837+
// Create a source package directory
838+
let source_dir = pixi.workspace_path().join("my-package");
839+
fs::create_dir_all(&source_dir).unwrap();
840+
841+
// Create the source package manifest WITHOUT any [package.build.config] section
842+
let source_pixi_toml_no_config = r#"
843+
[package]
844+
name = "my-package"
845+
version = "1.0.0"
846+
847+
[package.build]
848+
backend = { name = "in-memory", version = "0.1.0" }
849+
"#;
850+
fs::write(source_dir.join("pixi.toml"), source_pixi_toml_no_config).unwrap();
851+
852+
// Create the workspace manifest
853+
let manifest_content = format!(
854+
r#"
855+
[workspace]
856+
channels = []
857+
platforms = ["{}"]
858+
preview = ["pixi-build"]
859+
860+
[dependencies]
861+
my-package = {{ path = "./my-package" }}
862+
"#,
863+
Platform::current()
864+
);
865+
866+
fs::write(pixi.manifest_path(), manifest_content).unwrap();
867+
868+
// Helper to filter CondaOutputsCalled events
869+
fn count_conda_outputs_events(events: &[BackendEvent]) -> usize {
870+
events
871+
.iter()
872+
.filter(|e| matches!(e, BackendEvent::CondaOutputsCalled))
873+
.count()
874+
}
875+
876+
// First invocation: Generate the lock-file (no config section)
877+
let workspace = pixi.workspace().unwrap();
878+
let (lock_file_data, was_updated) = workspace
879+
.update_lock_file(pixi_core::UpdateLockFileOptions::default())
880+
.await
881+
.expect("First lock file generation should succeed");
882+
883+
assert!(was_updated, "First invocation should create the lock-file");
884+
885+
// Verify the package is in the lock-file
886+
let lock_file = lock_file_data.into_lock_file();
887+
assert!(
888+
lock_file.contains_conda_package(
889+
consts::DEFAULT_ENVIRONMENT_NAME,
890+
Platform::current(),
891+
"my-package",
892+
),
893+
"Lock file should contain my-package"
894+
);
895+
896+
// Check that conda_outputs was called once
897+
let events_after_first = observer.events();
898+
assert_eq!(
899+
count_conda_outputs_events(&events_after_first),
900+
1,
901+
"conda_outputs should be called once for first lock file generation"
902+
);
903+
904+
// Now add an EMPTY [package.build.config] section
905+
// This should NOT invalidate the cache since empty config hashes the same as no config
906+
let source_pixi_toml_empty_config = r#"
907+
[package]
908+
name = "my-package"
909+
version = "1.0.0"
910+
911+
[package.build]
912+
backend = { name = "in-memory", version = "0.1.0" }
913+
914+
[package.build.config]
915+
"#;
916+
fs::write(source_dir.join("pixi.toml"), source_pixi_toml_empty_config).unwrap();
917+
918+
// Second invocation with empty config section: Should NOT call backend again (cache hit)
919+
let workspace = pixi.workspace().unwrap();
920+
let (_lock_file_data, was_updated_empty_config) = workspace
921+
.update_lock_file(pixi_core::UpdateLockFileOptions::default())
922+
.await
923+
.expect("Second lock file check should succeed");
924+
925+
assert!(
926+
!was_updated_empty_config,
927+
"Adding empty [package.build.config] should NOT update lock-file"
928+
);
929+
930+
// Verify no additional conda_outputs calls
931+
let events_after_empty_config = observer.events();
932+
assert_eq!(
933+
count_conda_outputs_events(&events_after_empty_config),
934+
0,
935+
"conda_outputs should NOT be called when adding empty [package.build.config] (cache hit)"
936+
);
937+
938+
// Now add actual configuration values
939+
let source_pixi_toml_with_config = r#"
940+
[package]
941+
name = "my-package"
942+
version = "1.0.0"
943+
944+
[package.build]
945+
backend = { name = "in-memory", version = "0.1.0" }
946+
947+
[package.build.config]
948+
noarch = true
949+
"#;
950+
fs::write(source_dir.join("pixi.toml"), source_pixi_toml_with_config).unwrap();
951+
952+
// Third invocation: Should detect config change and call backend again
953+
let workspace = pixi.workspace().unwrap();
954+
let (_lock_file_data, _was_updated_after_config_added) = workspace
955+
.update_lock_file(pixi_core::UpdateLockFileOptions::default())
956+
.await
957+
.expect("Third lock file generation should succeed");
958+
959+
// Verify conda_outputs was called again due to config change
960+
let events_after_config_added = observer.events();
961+
assert_eq!(
962+
count_conda_outputs_events(&events_after_config_added),
963+
1,
964+
"conda_outputs should be called when [package.build.config] gets actual values (cache invalidated)"
965+
);
966+
967+
// Fourth invocation without changes: Should NOT call backend again (cache hit)
968+
let workspace = pixi.workspace().unwrap();
969+
let (_lock_file_data, was_updated_no_change) = workspace
970+
.update_lock_file(pixi_core::UpdateLockFileOptions::default())
971+
.await
972+
.expect("Fourth lock file check should succeed");
973+
974+
assert!(
975+
!was_updated_no_change,
976+
"Fourth invocation without changes should NOT update lock-file"
977+
);
978+
979+
// Verify no additional conda_outputs calls
980+
let events_after_no_change = observer.events();
981+
assert_eq!(
982+
count_conda_outputs_events(&events_after_no_change),
983+
0,
984+
"conda_outputs should NOT be called again when config hasn't changed (cache hit)"
985+
);
986+
987+
// Now change the build configuration (noarch = true -> noarch = false)
988+
let source_pixi_toml_changed_config = r#"
989+
[package]
990+
name = "my-package"
991+
version = "1.0.0"
992+
993+
[package.build]
994+
backend = { name = "in-memory", version = "0.1.0" }
995+
996+
[package.build.config]
997+
noarch = false
998+
"#;
999+
fs::write(
1000+
source_dir.join("pixi.toml"),
1001+
source_pixi_toml_changed_config,
1002+
)
1003+
.unwrap();
1004+
1005+
// Fifth invocation: Should detect config change and call backend again
1006+
let workspace = pixi.workspace().unwrap();
1007+
let (_lock_file_data, _was_updated_after_config_change) = workspace
1008+
.update_lock_file(pixi_core::UpdateLockFileOptions::default())
1009+
.await
1010+
.expect("Fifth lock file generation should succeed");
1011+
1012+
// Verify conda_outputs was called again due to config change
1013+
let events_after_config_change = observer.events();
1014+
assert_eq!(
1015+
count_conda_outputs_events(&events_after_config_change),
1016+
1,
1017+
"conda_outputs should be called again when [package.build.config] values change (cache invalidated)"
1018+
);
1019+
1020+
// Sixth invocation: Should NOT call backend again (cache is now fresh)
1021+
let workspace = pixi.workspace().unwrap();
1022+
let (_, was_updated_sixth) = workspace
1023+
.update_lock_file(pixi_core::UpdateLockFileOptions::default())
1024+
.await
1025+
.expect("Sixth lock file check should succeed");
1026+
1027+
assert!(
1028+
!was_updated_sixth,
1029+
"Sixth invocation should NOT update lock-file (cache is now fresh)"
1030+
);
1031+
1032+
// Verify no additional conda_outputs calls
1033+
let events_after_sixth = observer.events();
1034+
assert_eq!(
1035+
count_conda_outputs_events(&events_after_sixth),
1036+
0,
1037+
"conda_outputs should NOT be called again after cache is updated"
1038+
);
1039+
}

crates/pixi/tests/integration_rust/common/builders.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,17 @@ pub struct LockBuilder {
621621
pub args: lock::Args,
622622
}
623623

624+
impl LockBuilder {
625+
pub fn with_dry_run(mut self, dry_run: bool) -> Self {
626+
self.args.dry_run = dry_run;
627+
self
628+
}
629+
pub fn with_check(mut self, check: bool) -> Self {
630+
self.args.check = check;
631+
self
632+
}
633+
}
634+
624635
impl IntoFuture for LockBuilder {
625636
type Output = miette::Result<()>;
626637
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + 'static>>;

crates/pixi/tests/integration_rust/common/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ impl PixiControl {
718718
no_install_config: NoInstallConfig { no_install: false },
719719
check: false,
720720
json: false,
721+
dry_run: false,
721722
},
722723
}
723724
}

0 commit comments

Comments
 (0)