Respect the distribution manifest ordering when listing distributions (#13561)
* Respect the distribution manifest ordering when listing distributions * Fix test * Update test/windows/UnitTests.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Blue committed
Oct 6, 2025 at 12:42 UTC
9b5659d4b9805084afcf2abb88796cf45dbf88b9
5 files changed
+98
-10
CMakeLists.txt
+2
-2
@@ -34,8 +34,8 @@ FetchContent_GetProperties(GSL SOURCE_DIR GSL_SOURCE_DIR)
34
35
36
FetchContent_Declare(nlohmannjson
37
- URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
38
- URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d)
37
+ URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz
38
+ URL_HASH SHA256=42f6e95cad6ec532fd372391373363b62a14af6d771056dbfc86160e6dfff7aa )
39
40
FetchContent_MakeAvailable(nlohmannjson)
41
FetchContent_GetProperties(nlohmannjson SOURCE_DIR NLOHMAN_JSON_SOURCE_DIR)
src/shared/inc/JsonUtils.h
+5
-5
@@ -46,18 +46,18 @@ std::wstring ToJsonW(const T& Value)
46
return wsl::shared::string::MultiByteToWide(ToJson(Value));
47
}
48
49
-template <typename T>
49
+template <typename T, typename TJson = nlohmann::json>
50
T FromJson(const char* Value)
51
{
52
try
53
{
54
- auto json = nlohmann::json::parse(Value);
54
+ auto json = TJson::parse(Value);
55
T object{};
56
from_json(json, object);
57
58
return object;
59
}
60
- catch (const nlohmann::json::exception& e)
60
+ catch (const TJson::exception& e)
61
{
62
63
#ifdef WIN32
@@ -72,10 +72,10 @@ T FromJson(const char* Value)
72
}
73
}
74
75
-template <typename T>
75
+template <typename T, typename TJson = nlohmann::json>
76
T FromJson(const wchar_t* Value)
77
{
78
- return FromJson<T>(wsl::shared::string::WideToMultiByte(Value).c_str());
78
+ return FromJson<T, TJson>(wsl::shared::string::WideToMultiByte(Value).c_str());
79
}
80
81
template <typename T>
src/windows/common/Distribution.cpp
+1
-1
@@ -112,7 +112,7 @@ DistributionList ReadFromManifest(const std::wstring& url)
112
content = response.Content().ReadAsStringAsync().get();
113
}
114
115
- auto distros = wsl::shared::FromJson<DistributionList>(content.c_str());
115
+ auto distros = wsl::shared::FromJson<DistributionList, nlohmann::ordered_json>(content.c_str());
116
117
if (distros.Distributions.has_value())
118
{
src/windows/common/Distribution.h
+20
-2
@@ -58,10 +58,28 @@ struct Distribution
58
struct DistributionList
59
{
60
std::optional<std::vector<Distribution>> Distributions;
61
- std::optional<std::map<std::string, std::vector<ModernDistributionVersion>>> ModernDistributions;
61
+ std::optional<nlohmann::ordered_map<std::string, std::vector<ModernDistributionVersion>>> ModernDistributions;
62
std::optional<std::wstring> Default;
63
64
- NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(DistributionList, Distributions, ModernDistributions, Default)
64
+ friend void from_json(const nlohmann::ordered_json& nlohmann_json_j, DistributionList& nlohmann_json_t)
65
+ {
66
+ const DistributionList nlohmann_json_default_obj{};
67
+ NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, Distributions, Default));
68
+
69
+ auto modernDistributions = nlohmann_json_j.find("ModernDistributions");
70
+ if (modernDistributions != nlohmann_json_j.end())
71
+ {
72
+ nlohmann_json_t.ModernDistributions.emplace();
73
+
74
+ for (const auto& e : modernDistributions->items())
75
+ {
76
+ std::vector<ModernDistributionVersion> distros;
77
+ from_json(e.value(), distros);
78
+
79
+ nlohmann_json_t.ModernDistributions->emplace_back(e.key(), std::move(distros));
80
+ }
81
+ }
82
+ }
83
};
84
85
constexpr inline auto c_distroUrlRegistryValue = L"DistributionListUrl";
test/windows/UnitTests.cpp
+70
@@ -5326,6 +5326,76 @@ Error code: Wsl/InstallDistro/WSL_E_INVALID_JSON\r\n",
5326
5327
VERIFY_ARE_EQUAL(error, L"");
5328
}
5329
+
5330
+ // Validate that manifest distribution ordering is preserved.
5331
+ {
5332
+ auto validateOrder = [](const std::vector<LPCWSTR>& expected) {
5333
+ auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L"--list --online");
5334
+
5335
+ auto lines = wsl::shared::string::Split<wchar_t>(out, '\n');
5336
+
5337
+ for (size_t i = 0; i < expected.size(); i++)
5338
+ {
5339
+ auto end = lines[i + 4].find_first_of(L" \t");
5340
+ VERIFY_ARE_NOT_EQUAL(end, std::wstring::npos);
5341
+
5342
+ auto distro = lines[i + 4].substr(0, end);
5343
+
5344
+ VERIFY_ARE_EQUAL(expected[i], distro);
5345
+ }
5346
+ };
5347
+
5348
+ {
5349
+ auto manifest =
5350
+ R"({
5351
+ "ModernDistributions": {
5352
+ "distro1": [
5353
+ {
5354
+ "Name": "distro1",
5355
+ "FriendlyName": "distro1Name",
5356
+ "Amd64Url": {"Url": "","Sha256": ""}
5357
+ }
5358
+ ],
5359
+ "distro2": [
5360
+ {
5361
+ "Name": "distro2",
5362
+ "FriendlyName": "distro2Name",
5363
+ "Amd64Url": {"Url": "","Sha256": ""}
5364
+ }
5365
+ ]
5366
+ }
5367
+})";
5368
+
5369
+ auto restore = SetManifest(manifest);
5370
+ validateOrder({L"distro1", L"distro2"});
5371
+ }
5372
+
5373
+ {
5374
+ auto manifest =
5375
+ R"({
5376
+ "ModernDistributions": {
5377
+ "distro2": [
5378
+ {
5379
+ "Name": "distro2",
5380
+ "FriendlyName": "distro2Name",
5381
+ "Amd64Url": {"Url": "","Sha256": ""}
5382
+ }
5383
+ ],
5384
+ "distro1": [
5385
+ {
5386
+ "Name": "distro1",
5387
+ "FriendlyName": "distro1Name",
5388
+ "Amd64Url": {"Url": "","Sha256": ""}
5389
+ }
5390
+ ]
5391
+ }
5392
+})";
5393
+
5394
+ auto restore = SetManifest(manifest);
5395
+
5396
+ validateOrder({L"distro2", L"distro1"});
5397
+ }
5398
+ }
5399
}
5400
5401
TEST_METHOD(ModernInstallEndToEnd)