Skip to content

Commit 14708a2

Browse files
authored
fix: self-clobbering when updating a package (#494)
Also adds a few more tests for clobbering
1 parent 0128bce commit 14708a2

5 files changed

+223
-0
lines changed

crates/rattler/src/install/clobber_registry.rs

+186
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ impl ClobberRegistry {
121121

122122
// if we find an entry, we have a clobbering path!
123123
if let Some(e) = self.paths_registry.get(&path) {
124+
if e == &name_idx {
125+
// A name cannot appear twice in an environment.
126+
// We get into this case if a package is updated (removed and installed again with a new version)
127+
continue;
128+
}
124129
let new_path = Self::clobber_name(&path, &self.package_names[name_idx]);
125130
self.clobbers
126131
.entry(path.clone())
@@ -475,6 +480,14 @@ mod tests {
475480
]
476481
}
477482

483+
fn test_operations_update() -> Vec<RepoDataRecord> {
484+
let repodata_record_1 = get_repodata_record("clobber/clobber-1-0.2.0-h4616a5c_0.tar.bz2");
485+
let repodata_record_2 = get_repodata_record("clobber/clobber-2-0.2.0-h4616a5c_0.tar.bz2");
486+
let repodata_record_3 = get_repodata_record("clobber/clobber-3-0.2.0-h4616a5c_0.tar.bz2");
487+
488+
vec![repodata_record_1, repodata_record_2, repodata_record_3]
489+
}
490+
478491
fn assert_check_files(target_prefix: &Path, expected_files: &[&str]) {
479492
let files = std::fs::read_dir(target_prefix).unwrap();
480493
let files = files
@@ -763,4 +776,177 @@ mod tests {
763776
);
764777
}
765778
}
779+
780+
#[tokio::test]
781+
async fn test_clobber_update() {
782+
// Create a transaction
783+
let operations = test_operations();
784+
785+
let transaction = transaction::Transaction::<PrefixRecord, RepoDataRecord> {
786+
operations,
787+
python_info: None,
788+
current_python_info: None,
789+
platform: Platform::current(),
790+
};
791+
792+
// execute transaction
793+
let target_prefix = tempfile::tempdir().unwrap();
794+
795+
let packages_dir = tempfile::tempdir().unwrap();
796+
let cache = PackageCache::new(packages_dir.path());
797+
798+
execute_transaction(
799+
transaction,
800+
target_prefix.path(),
801+
&reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new()),
802+
&cache,
803+
&InstallDriver::default(),
804+
&InstallOptions::default(),
805+
)
806+
.await;
807+
808+
// check that the files are there
809+
assert_check_files(
810+
target_prefix.path(),
811+
&[
812+
"clobber.txt",
813+
"clobber.txt__clobber-from-clobber-2",
814+
"clobber.txt__clobber-from-clobber-3",
815+
],
816+
);
817+
818+
let mut prefix_records = PrefixRecord::collect_from_prefix(target_prefix.path()).unwrap();
819+
prefix_records.sort_by(|a, b| {
820+
a.repodata_record
821+
.package_record
822+
.name
823+
.as_normalized()
824+
.cmp(&b.repodata_record.package_record.name.as_normalized())
825+
});
826+
827+
let update_ops = test_operations_update();
828+
829+
// remove one of the clobbering files
830+
let transaction = transaction::Transaction::<PrefixRecord, RepoDataRecord> {
831+
operations: vec![TransactionOperation::Change {
832+
old: prefix_records[0].clone(),
833+
new: update_ops[0].clone(),
834+
}],
835+
python_info: None,
836+
current_python_info: None,
837+
platform: Platform::current(),
838+
};
839+
840+
let install_driver = InstallDriver::new(100, Some(&prefix_records));
841+
842+
execute_transaction(
843+
transaction,
844+
target_prefix.path(),
845+
&reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new()),
846+
&cache,
847+
&install_driver,
848+
&InstallOptions::default(),
849+
)
850+
.await;
851+
852+
assert_check_files(
853+
target_prefix.path(),
854+
&[
855+
"clobber.txt",
856+
"clobber.txt__clobber-from-clobber-2",
857+
"clobber.txt__clobber-from-clobber-3",
858+
],
859+
);
860+
861+
// content of clobber.txt
862+
assert_eq!(
863+
fs::read_to_string(target_prefix.path().join("clobber.txt")).unwrap(),
864+
"clobber-1 v2\n"
865+
);
866+
}
867+
868+
#[tokio::test]
869+
async fn test_clobber_update_and_remove() {
870+
// Create a transaction
871+
let operations = test_operations();
872+
873+
let transaction = transaction::Transaction::<PrefixRecord, RepoDataRecord> {
874+
operations,
875+
python_info: None,
876+
current_python_info: None,
877+
platform: Platform::current(),
878+
};
879+
880+
// execute transaction
881+
let target_prefix = tempfile::tempdir().unwrap();
882+
883+
let packages_dir = tempfile::tempdir().unwrap();
884+
let cache = PackageCache::new(packages_dir.path());
885+
886+
execute_transaction(
887+
transaction,
888+
target_prefix.path(),
889+
&reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new()),
890+
&cache,
891+
&InstallDriver::default(),
892+
&InstallOptions::default(),
893+
)
894+
.await;
895+
896+
// check that the files are there
897+
assert_check_files(
898+
target_prefix.path(),
899+
&[
900+
"clobber.txt",
901+
"clobber.txt__clobber-from-clobber-2",
902+
"clobber.txt__clobber-from-clobber-3",
903+
],
904+
);
905+
906+
let mut prefix_records = PrefixRecord::collect_from_prefix(target_prefix.path()).unwrap();
907+
prefix_records.sort_by(|a, b| {
908+
a.repodata_record
909+
.package_record
910+
.name
911+
.as_normalized()
912+
.cmp(&b.repodata_record.package_record.name.as_normalized())
913+
});
914+
915+
let update_ops = test_operations_update();
916+
917+
// remove one of the clobbering files
918+
let transaction = transaction::Transaction::<PrefixRecord, RepoDataRecord> {
919+
operations: vec![
920+
TransactionOperation::Change {
921+
old: prefix_records[2].clone(),
922+
new: update_ops[2].clone(),
923+
},
924+
TransactionOperation::Remove(prefix_records[0].clone()),
925+
TransactionOperation::Remove(prefix_records[1].clone()),
926+
],
927+
python_info: None,
928+
current_python_info: None,
929+
platform: Platform::current(),
930+
};
931+
932+
let install_driver = InstallDriver::new(100, Some(&prefix_records));
933+
934+
execute_transaction(
935+
transaction,
936+
target_prefix.path(),
937+
&reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new()),
938+
&cache,
939+
&install_driver,
940+
&InstallOptions::default(),
941+
)
942+
.await;
943+
944+
assert_check_files(target_prefix.path(), &["clobber.txt"]);
945+
946+
// content of clobber.txt
947+
assert_eq!(
948+
fs::read_to_string(target_prefix.path().join("clobber.txt")).unwrap(),
949+
"clobber-3 v2\n"
950+
);
951+
}
766952
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:b2127c7a13cf1ca63e8d8a6f9d88087c4997ae77d5aa11f4d598524f55e03d43
3+
size 1355
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:fb257fc0db07b6b1aa2006a61f49f639cfd92dedb7afc35ce1da1c166780a98d
3+
size 1349
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:dd3fea8229b48307c1a403a298c931370d56cfb7b08372f8fe19387c8d219548
3+
size 1339
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
recipe:
2+
name: clobber
3+
version: 0.2.0
4+
5+
outputs:
6+
- package:
7+
name: clobber-1
8+
9+
build:
10+
noarch: generic
11+
script:
12+
- echo "clobber-1 v2" > $PREFIX/clobber.txt
13+
14+
- package:
15+
name: clobber-2
16+
17+
build:
18+
noarch: generic
19+
script:
20+
- echo "clobber-2 v2" > $PREFIX/clobber.txt
21+
22+
- package:
23+
name: clobber-3
24+
25+
build:
26+
noarch: generic
27+
script:
28+
- echo "clobber-3 v2" > $PREFIX/clobber.txt

0 commit comments

Comments
 (0)