From c08d428177b3d699ef3841ab8ebe09eef4146ff4 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:35:41 +0800 Subject: [PATCH 1/4] fix: sort packages when serializing repodata --- .../rattler_conda_types/src/repo_data/mod.rs | 21 +- ...test__deserialize_no_noarch_empty_str.snap | 20 +- ...__test__deserialize_no_packages_conda.snap | 78 +++--- ...__repo_data__test__serialize_packages.snap | 262 +++++++++--------- crates/rattler_conda_types/src/utils/serde.rs | 24 ++ 5 files changed, 220 insertions(+), 185 deletions(-) diff --git a/crates/rattler_conda_types/src/repo_data/mod.rs b/crates/rattler_conda_types/src/repo_data/mod.rs index 8a3371d8db..301d168a04 100644 --- a/crates/rattler_conda_types/src/repo_data/mod.rs +++ b/crates/rattler_conda_types/src/repo_data/mod.rs @@ -22,7 +22,10 @@ use url::Url; use crate::{ build_spec::BuildNumber, package::{IndexJson, RunExportsJson}, - utils::{serde::DeserializeFromStrUnchecked, UrlWithTrailingSlash}, + utils::{ + serde::sort_index_map_alphabetically, serde::sort_map_alphabetically, + serde::DeserializeFromStrUnchecked, UrlWithTrailingSlash, + }, Arch, Channel, MatchSpec, Matches, NoArchType, PackageName, PackageUrl, ParseMatchSpecError, ParseStrictness, Platform, RepoDataRecord, VersionWithSource, }; @@ -37,13 +40,17 @@ pub struct RepoData { pub info: Option, /// The tar.bz2 packages contained in the repodata.json file - #[serde(default)] + #[serde(default, serialize_with = "sort_index_map_alphabetically")] pub packages: IndexMap, /// The conda packages contained in the repodata.json file (under a /// different key for backwards compatibility with previous conda /// versions) - #[serde(default, rename = "packages.conda")] + #[serde( + default, + rename = "packages.conda", + serialize_with = "sort_index_map_alphabetically" + )] pub conda_packages: IndexMap, /// removed packages (files are still accessible, but they are not @@ -422,10 +429,14 @@ struct PackageRunExports { pub struct SubdirRunExportsJson { info: Option, - #[serde(default)] + #[serde(default, serialize_with = "sort_map_alphabetically")] packages: ahash::HashMap, - #[serde(default, rename = "packages.conda")] + #[serde( + default, + rename = "packages.conda", + serialize_with = "sort_map_alphabetically" + )] conda_packages: ahash::HashMap, } diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_noarch_empty_str.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_noarch_empty_str.snap index 3f206e0953..0fe684c918 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_noarch_empty_str.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_noarch_empty_str.snap @@ -6,16 +6,6 @@ info: subdir: noarch base_url: "../linux-64" packages: - foo-1-xxx.tar.bz2: - build: xxx - build_number: 0 - depends: [] - extra_depends: - with-bar: - - bar <2 - name: foo - subdir: linux-64 - version: "1" bar-1-xxx.tar.bz2: build: xxx build_number: 0 @@ -30,5 +20,15 @@ packages: name: bar subdir: linux-64 version: "2" + foo-1-xxx.tar.bz2: + build: xxx + build_number: 0 + depends: [] + extra_depends: + with-bar: + - bar <2 + name: foo + subdir: linux-64 + version: "1" packages.conda: {} repodata_version: 2 diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_packages_conda.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_packages_conda.snap index ef54f72983..7ad5d15346 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_packages_conda.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_packages_conda.snap @@ -6,45 +6,6 @@ info: subdir: linux-64 base_url: "../linux-64" packages: - foo-3.0.2-py36h1af98f8_1.tar.bz2: - build: py36h1af98f8_1 - build_number: 1 - depends: [] - license: MIT - license_family: MIT - md5: d65ab674acf3b7294ebacaec05fc5b54 - name: foo - sha256: 1154fceeb5c4ee9bb97d245713ac21eb1910237c724d2b7103747215663273c2 - size: 414494 - subdir: linux-64 - timestamp: 1605110689658 - version: 3.0.2 - foo-3.0.2-py36h1af98f8_1.conda: - build: py36h1af98f8_1 - build_number: 1 - depends: [] - license: MIT - license_family: MIT - md5: fb731d9290f0bcbf3a054665f33ec94f - name: foo - sha256: 67a63bec3fd3205170eaad532d487595b8aaceb9814d13c6858d7bac3ef24cd4 - size: 414494 - subdir: linux-64 - timestamp: 1605110689658 - version: 3.0.2 - foo-4.0.2-py36h1af98f8_2.tar.bz2: - build: py36h1af98f8_2 - build_number: 1 - depends: [] - license: MIT - license_family: MIT - md5: bc13aa58e2092bcb0b97c561373d3905 - name: foo - sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a - size: 414494 - subdir: linux-64 - timestamp: 1605110689658 - version: 4.0.2 bar-1.0-unix_py36h1af98f8_2.tar.bz2: build: unix_py36h1af98f8_2 build_number: 1 @@ -151,6 +112,45 @@ packages: subdir: linux-64 timestamp: 1605110689658 version: "2.1" + foo-3.0.2-py36h1af98f8_1.conda: + build: py36h1af98f8_1 + build_number: 1 + depends: [] + license: MIT + license_family: MIT + md5: fb731d9290f0bcbf3a054665f33ec94f + name: foo + sha256: 67a63bec3fd3205170eaad532d487595b8aaceb9814d13c6858d7bac3ef24cd4 + size: 414494 + subdir: linux-64 + timestamp: 1605110689658 + version: 3.0.2 + foo-3.0.2-py36h1af98f8_1.tar.bz2: + build: py36h1af98f8_1 + build_number: 1 + depends: [] + license: MIT + license_family: MIT + md5: d65ab674acf3b7294ebacaec05fc5b54 + name: foo + sha256: 1154fceeb5c4ee9bb97d245713ac21eb1910237c724d2b7103747215663273c2 + size: 414494 + subdir: linux-64 + timestamp: 1605110689658 + version: 3.0.2 + foo-4.0.2-py36h1af98f8_2.tar.bz2: + build: py36h1af98f8_2 + build_number: 1 + depends: [] + license: MIT + license_family: MIT + md5: bc13aa58e2092bcb0b97c561373d3905 + name: foo + sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a + size: 414494 + subdir: linux-64 + timestamp: 1605110689658 + version: 4.0.2 foobar-2.0-bla_1.tar.bz2: build: bla_1 build_number: 1 diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages.snap index 24976d3842..24bd71e304 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages.snap @@ -6,88 +6,6 @@ info: subdir: linux-64 base_url: "../linux-64" packages: - cuda-version-12.5-hd4f0392_3.conda: - build: hd4f0392_3 - build_number: 3 - constrains: - - __cuda >=12.1 - depends: [] - license: LicenseRef-NVIDIA-End-User-License-Agreement - license_family: LicenseRef-NVIDIA-End-User-License-Agreement - md5: 6ae1a563a4aa61e55e8ae8260f0d021b - name: cuda-version - sha256: e45a5d14909296abd0784a073da9ee5c420fa58671fbc999f8a9ec898cf3486b - size: 21151 - subdir: noarch - timestamp: 1716314536803 - version: "12.5" - foo-3.0.2-py36h1af98f8_1.tar.bz2: - build: py36h1af98f8_1 - build_number: 1 - depends: [] - license: MIT - license_family: MIT - md5: d65ab674acf3b7294ebacaec05fc5b54 - name: foo - sha256: 1154fceeb5c4ee9bb97d245713ac21eb1910237c724d2b7103747215663273c2 - size: 414494 - subdir: linux-64 - timestamp: 1605110689658 - version: 3.0.2 - foo-3.0.2-py36h1af98f8_1.conda: - build: py36h1af98f8_1 - build_number: 1 - depends: [] - license: MIT - license_family: MIT - md5: fb731d9290f0bcbf3a054665f33ec94f - name: foo - sha256: 67a63bec3fd3205170eaad532d487595b8aaceb9814d13c6858d7bac3ef24cd4 - size: 414494 - subdir: linux-64 - timestamp: 1715610974 - version: 3.0.2 - foo-3.0.2-py36h1af98f8_2.conda: - build: py36h1af98f8_2 - build_number: 2 - depends: [] - license: MIT - license_family: MIT - md5: fb731d9290f0bcbf3a054665f33ec94f - name: foo - sha256: 67a63bec3fd3205170eaad532d487595b8aaceb9814d13c6858d7bac3ef24cd4 - size: 414494 - subdir: linux-64 - timestamp: 1715610974 - version: 3.0.2 - foo-3.0.2-py36h1af98f8_3.conda: - build: py36h1af98f8_3 - build_number: 3 - constrains: - - bors <2.0 - depends: [] - license: MIT - license_family: MIT - md5: fb731d9290f0bcbf3a054665f33ec94f - name: foo - sha256: 67a63bec3fd3205170eaad532d487595b8aaceb9814d13c6858d7bac3ef24cd4 - size: 414494 - subdir: linux-64 - timestamp: 1715610974 - version: 3.0.2 - foo-4.0.2-py36h1af98f8_2.tar.bz2: - build: py36h1af98f8_2 - build_number: 1 - depends: [] - license: MIT - license_family: MIT - md5: bc13aa58e2092bcb0b97c561373d3905 - name: foo - sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a - size: 414494 - subdir: linux-64 - timestamp: 1715610974 - version: 4.0.2 bar-1.0-unix_py36h1af98f8_2.tar.bz2: build: unix_py36h1af98f8_2 build_number: 1 @@ -194,6 +112,88 @@ packages: subdir: linux-64 timestamp: 1715610974 version: "2.1" + cuda-version-12.5-hd4f0392_3.conda: + build: hd4f0392_3 + build_number: 3 + constrains: + - __cuda >=12.1 + depends: [] + license: LicenseRef-NVIDIA-End-User-License-Agreement + license_family: LicenseRef-NVIDIA-End-User-License-Agreement + md5: 6ae1a563a4aa61e55e8ae8260f0d021b + name: cuda-version + sha256: e45a5d14909296abd0784a073da9ee5c420fa58671fbc999f8a9ec898cf3486b + size: 21151 + subdir: noarch + timestamp: 1716314536803 + version: "12.5" + foo-3.0.2-py36h1af98f8_1.conda: + build: py36h1af98f8_1 + build_number: 1 + depends: [] + license: MIT + license_family: MIT + md5: fb731d9290f0bcbf3a054665f33ec94f + name: foo + sha256: 67a63bec3fd3205170eaad532d487595b8aaceb9814d13c6858d7bac3ef24cd4 + size: 414494 + subdir: linux-64 + timestamp: 1715610974 + version: 3.0.2 + foo-3.0.2-py36h1af98f8_1.tar.bz2: + build: py36h1af98f8_1 + build_number: 1 + depends: [] + license: MIT + license_family: MIT + md5: d65ab674acf3b7294ebacaec05fc5b54 + name: foo + sha256: 1154fceeb5c4ee9bb97d245713ac21eb1910237c724d2b7103747215663273c2 + size: 414494 + subdir: linux-64 + timestamp: 1605110689658 + version: 3.0.2 + foo-3.0.2-py36h1af98f8_2.conda: + build: py36h1af98f8_2 + build_number: 2 + depends: [] + license: MIT + license_family: MIT + md5: fb731d9290f0bcbf3a054665f33ec94f + name: foo + sha256: 67a63bec3fd3205170eaad532d487595b8aaceb9814d13c6858d7bac3ef24cd4 + size: 414494 + subdir: linux-64 + timestamp: 1715610974 + version: 3.0.2 + foo-3.0.2-py36h1af98f8_3.conda: + build: py36h1af98f8_3 + build_number: 3 + constrains: + - bors <2.0 + depends: [] + license: MIT + license_family: MIT + md5: fb731d9290f0bcbf3a054665f33ec94f + name: foo + sha256: 67a63bec3fd3205170eaad532d487595b8aaceb9814d13c6858d7bac3ef24cd4 + size: 414494 + subdir: linux-64 + timestamp: 1715610974 + version: 3.0.2 + foo-4.0.2-py36h1af98f8_2.tar.bz2: + build: py36h1af98f8_2 + build_number: 1 + depends: [] + license: MIT + license_family: MIT + md5: bc13aa58e2092bcb0b97c561373d3905 + name: foo + sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a + size: 414494 + subdir: linux-64 + timestamp: 1715610974 + version: 4.0.2 foobar-2.0-bla_1.tar.bz2: build: bla_1 build_number: 1 @@ -237,47 +237,49 @@ packages: subdir: linux-64 timestamp: 1715610974 version: "2.1" - xfoo-1-xxx.tar.bz2: + track-features-1-xxx.tar.bz2: build: xxx build_number: 0 depends: [] license: MIT license_family: MIT md5: bc13aa58e2092bcb0b97c561373d3905 - name: xfoo + name: track-features sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a size: 414494 subdir: linux-64 timestamp: 1715610974 + track_features: foo version: "1" - xfoo-2-xxx.tar.bz2: + track-features-2-xxx.tar.bz2: build: xxx build_number: 0 depends: [] license: MIT license_family: MIT md5: bc13aa58e2092bcb0b97c561373d3905 - name: xfoo + name: track-features sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a size: 414494 subdir: linux-64 timestamp: 1715610974 + track_features: foo bar version: "2" - xbar-1-xxx.tar.bz2: + track-features-3-xxx.tar.bz2: build: xxx build_number: 0 - depends: - - xfoo >=2 + depends: [] license: MIT license_family: MIT md5: bc13aa58e2092bcb0b97c561373d3905 - name: xbar + name: track-features sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a size: 414494 subdir: linux-64 timestamp: 1715610974 - version: "1" - track-features-1-xxx.tar.bz2: + track_features: foo bar + version: "3" + track-features-4-xxx.tar.bz2: build: xxx build_number: 0 depends: [] @@ -289,66 +291,63 @@ packages: size: 414494 subdir: linux-64 timestamp: 1715610974 - track_features: foo - version: "1" - track-features-2-xxx.tar.bz2: + track_features: foo bar + version: "4" + xbar-1-xxx.tar.bz2: build: xxx build_number: 0 - depends: [] + depends: + - xfoo >=2 license: MIT license_family: MIT md5: bc13aa58e2092bcb0b97c561373d3905 - name: track-features + name: xbar sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a size: 414494 subdir: linux-64 timestamp: 1715610974 - track_features: foo bar - version: "2" - track-features-3-xxx.tar.bz2: + version: "1" + xfoo-1-xxx.tar.bz2: build: xxx build_number: 0 depends: [] license: MIT license_family: MIT md5: bc13aa58e2092bcb0b97c561373d3905 - name: track-features + name: xfoo sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a size: 414494 subdir: linux-64 timestamp: 1715610974 - track_features: foo bar - version: "3" - track-features-4-xxx.tar.bz2: + version: "1" + xfoo-2-xxx.tar.bz2: build: xxx build_number: 0 depends: [] license: MIT license_family: MIT md5: bc13aa58e2092bcb0b97c561373d3905 - name: track-features + name: xfoo sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a size: 414494 subdir: linux-64 timestamp: 1715610974 - track_features: foo bar - version: "4" + version: "2" packages.conda: - foobar-2.0-bla_1.conda: + bors-1.1-bla_1.conda: build: bla_1 build_number: 1 - depends: - - bors <2.0 + depends: [] license: MIT license_family: MIT md5: bc13aa58e2092bcb0b97c561373d3905 - name: foobar + name: bors sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a size: 414494 subdir: linux-64 timestamp: 1605110689658 - version: "2.0" - bors-1.1-bla_1.conda: + version: "1.1" + bors-2.1-bla_1.conda: build: bla_1 build_number: 1 depends: [] @@ -359,21 +358,33 @@ packages.conda: sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a size: 414494 subdir: linux-64 - timestamp: 1605110689658 - version: "1.1" - bors-2.1-bla_1.conda: + timestamp: 1715610974 + version: "2.1" + conda-only-0.1.0-foobar_0.conda: + build: foobar + build_number: 0 + depends: [] + md5: bc13aa58e2092bcb0b97c561373d3905 + name: conda-only + sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a + size: 414494 + subdir: linux-64 + timestamp: 1715610974 + version: 0.1.0 + foobar-2.0-bla_1.conda: build: bla_1 build_number: 1 - depends: [] + depends: + - bors <2.0 license: MIT license_family: MIT md5: bc13aa58e2092bcb0b97c561373d3905 - name: bors + name: foobar sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a size: 414494 subdir: linux-64 - timestamp: 1715610974 - version: "2.1" + timestamp: 1605110689658 + version: "2.0" issue_717-2.1-bla_1.conda: build: issue_717 build_number: 0 @@ -389,15 +400,4 @@ packages.conda: subdir: linux-64 timestamp: 1715610974 version: "2.1" - conda-only-0.1.0-foobar_0.conda: - build: foobar - build_number: 0 - depends: [] - md5: bc13aa58e2092bcb0b97c561373d3905 - name: conda-only - sha256: 97ec377d2ad83dfef1194b7aa31b0c9076194e10d995a6e696c9d07dd782b14a - size: 414494 - subdir: linux-64 - timestamp: 1715610974 - version: 0.1.0 repodata_version: 2 diff --git a/crates/rattler_conda_types/src/utils/serde.rs b/crates/rattler_conda_types/src/utils/serde.rs index 365898d133..5ffd26392d 100644 --- a/crates/rattler_conda_types/src/utils/serde.rs +++ b/crates/rattler_conda_types/src/utils/serde.rs @@ -1,7 +1,9 @@ use chrono::{DateTime, Utc}; +use indexmap::IndexMap; use serde::{de::Error as _, ser::Error, Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; use std::borrow::Cow; +use std::collections::{BTreeMap, HashMap}; use std::{ marker::PhantomData, path::{Path, PathBuf}, @@ -143,6 +145,28 @@ impl SerializeAs> for Timestamp { /// string. pub struct DeserializeFromStrUnchecked; +/// A helper function used to sort map alphabetically when serializing. +pub(crate) fn sort_map_alphabetically( + value: &HashMap, + serializer: S, +) -> Result { + value + .iter() + .collect::>() + .serialize(serializer) +} + +/// A helper function used to sort map alphabetically when serializing. +pub(crate) fn sort_index_map_alphabetically( + value: &IndexMap, + serializer: S, +) -> Result { + value + .iter() + .collect::>() + .serialize(serializer) +} + /// A helper to serialize and deserialize `track_features` in repodata. Track /// features are expected to be a space separated list. However, in the past we /// have serialized and deserialized them as a list of strings so for From 350faecf92290d1b981041820e96f2a56ace0110 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:00:43 +0800 Subject: [PATCH 2/4] test: add test to verify that packages are sorted --- .../rattler_conda_types/src/repo_data/mod.rs | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/crates/rattler_conda_types/src/repo_data/mod.rs b/crates/rattler_conda_types/src/repo_data/mod.rs index 52698ed6eb..97f6bb95c4 100644 --- a/crates/rattler_conda_types/src/repo_data/mod.rs +++ b/crates/rattler_conda_types/src/repo_data/mod.rs @@ -769,4 +769,110 @@ mod test { "package 'foo=3.0.2=py36h1af98f8_3' has constraint 'bors <2.0', which is not satisfied by 'bors=2.1=bla_1' in the environment" )); } + + #[test] + fn test_packages_serialized_alphabetically() { + use crate::{PackageName, Version}; + + // Create a RepoData with packages inserted in NON-alphabetical order + let mut packages = IndexMap::default(); + let mut conda_packages = IndexMap::default(); + + // Insert packages in deliberately non-alphabetical order: z, a, m, b + packages.insert( + "zebra-1.0-h123.tar.bz2".to_string(), + PackageRecord::new( + PackageName::new_unchecked("zebra"), + Version::major(1), + "h123".to_string(), + ), + ); + packages.insert( + "apple-2.0-h456.tar.bz2".to_string(), + PackageRecord::new( + PackageName::new_unchecked("apple"), + Version::major(2), + "h456".to_string(), + ), + ); + packages.insert( + "mango-1.5-h789.tar.bz2".to_string(), + PackageRecord::new( + PackageName::new_unchecked("mango"), + Version::major(1), + "h789".to_string(), + ), + ); + packages.insert( + "banana-3.0-habc.tar.bz2".to_string(), + PackageRecord::new( + PackageName::new_unchecked("banana"), + Version::major(3), + "habc".to_string(), + ), + ); + + // Insert conda packages in non-alphabetical order too + conda_packages.insert( + "xray-1.0-h111.conda".to_string(), + PackageRecord::new( + PackageName::new_unchecked("xray"), + Version::major(1), + "h111".to_string(), + ), + ); + conda_packages.insert( + "alpha-2.0-h222.conda".to_string(), + PackageRecord::new( + PackageName::new_unchecked("alpha"), + Version::major(2), + "h222".to_string(), + ), + ); + conda_packages.insert( + "omega-3.0-h333.conda".to_string(), + PackageRecord::new( + PackageName::new_unchecked("omega"), + Version::major(3), + "h333".to_string(), + ), + ); + + let repodata = RepoData { + version: Some(2), + info: None, + packages, + conda_packages, + removed: Default::default(), + }; + + // Serialize to JSON string + let json = serde_json::to_string(&repodata).unwrap(); + + // Parse the JSON to extract the package keys + let json_value: serde_json::Value = serde_json::from_str(&json).unwrap(); + + // Check that packages are in alphabetical order + if let Some(packages) = json_value.get("packages").and_then(|p| p.as_object()) { + let keys: Vec<&String> = packages.keys().collect(); + let mut sorted_keys = keys.clone(); + sorted_keys.sort(); + assert_eq!( + keys, sorted_keys, + "packages should be serialized in alphabetical order" + ); + } + + // Check that packages.conda are in alphabetical order + if let Some(conda_packages) = json_value.get("packages.conda").and_then(|p| p.as_object()) + { + let keys: Vec<&String> = conda_packages.keys().collect(); + let mut sorted_keys = keys.clone(); + sorted_keys.sort(); + assert_eq!( + keys, sorted_keys, + "packages.conda should be serialized in alphabetical order" + ); + } + } } From e4c8ec1959964997e6d7869d3f014128f70bb0d7 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:04:22 +0800 Subject: [PATCH 3/4] fix: fmt --- crates/rattler_conda_types/src/repo_data/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/rattler_conda_types/src/repo_data/mod.rs b/crates/rattler_conda_types/src/repo_data/mod.rs index 97f6bb95c4..c5b341b4ac 100644 --- a/crates/rattler_conda_types/src/repo_data/mod.rs +++ b/crates/rattler_conda_types/src/repo_data/mod.rs @@ -864,8 +864,7 @@ mod test { } // Check that packages.conda are in alphabetical order - if let Some(conda_packages) = json_value.get("packages.conda").and_then(|p| p.as_object()) - { + if let Some(conda_packages) = json_value.get("packages.conda").and_then(|p| p.as_object()) { let keys: Vec<&String> = conda_packages.keys().collect(); let mut sorted_keys = keys.clone(); sorted_keys.sort(); From cbabf280c1021d2c485efc964194210af7a586f6 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:20:07 +0800 Subject: [PATCH 4/4] fix: clippy --- crates/rattler_conda_types/src/repo_data/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rattler_conda_types/src/repo_data/mod.rs b/crates/rattler_conda_types/src/repo_data/mod.rs index c5b341b4ac..1dca7a1eae 100644 --- a/crates/rattler_conda_types/src/repo_data/mod.rs +++ b/crates/rattler_conda_types/src/repo_data/mod.rs @@ -843,7 +843,7 @@ mod test { info: None, packages, conda_packages, - removed: Default::default(), + removed: ahash::HashSet::default(), }; // Serialize to JSON string