CLI: Fix image --quiet to output truncated ids instead of repository:tag (#40855)

David Bennett committed Jun 19, 2026 at 22:24 UTC 83effc5b485475adcc140b8c290914b38dae498d
5 files changed +63 -25
src/windows/wslc/tasks/ImageTasks.cpp
+2 -2
@@ -118,10 +118,10 @@ void ListImages(CLIExecutionContext& context)
118
119 if (context.Args.Contains(ArgType::Quiet))
120 {
121 - // Print only the image names.
121 + bool trunc = !context.Args.Contains(ArgType::NoTrunc);
122 for (const auto& image : images)
123 {
124 - PrintMessage(MultiByteToWide(image.Repository.value_or("<untagged>") + ":" + image.Tag.value_or("<untagged>")));
124 + context.Reporter.Output(L"{}\n", trunc ? TruncateId(image.Id, true) : image.Id);
125 }
126
127 return;
test/windows/wslc/e2e/WSLCE2EHelpers.cpp
+11 -9
@@ -358,13 +358,14 @@ void EnsureImageContainersAreDeleted(const TestImage& image)
358
359 void EnsureImageIsDeleted(const TestImage& image)
360 {
361 - auto result = RunWslc(L"image list -q");
361 + auto result = RunWslc(L"image list --format json");
362 result.Verify({.Stderr = L"", .ExitCode = 0});
363
364 - auto outputLines = result.GetStdoutLines();
365 - for (const auto& line : outputLines)
364 + auto images = wsl::shared::FromJson<std::vector<wsl::windows::wslc::models::ImageInformation>>(result.Stdout.value().c_str());
365 + for (const auto& img : images)
366 {
367 - if (line.find(image.NameAndTag()) != std::wstring::npos)
367 + if (img.Repository == wsl::shared::string::WideToMultiByte(image.Name) &&
368 + img.Tag == wsl::shared::string::WideToMultiByte(image.Tag))
369 {
370 EnsureImageContainersAreDeleted(image);
371 auto deleteResult = RunWslc(std::format(L"image delete --force {}", image.NameAndTag()));
@@ -376,19 +377,20 @@ void EnsureImageIsDeleted(const TestImage& image)
377
378 void EnsureImageIsLoaded(const TestImage& image, const std::wstring& sessionName)
379 {
379 - std::wstring listCommand = L"image list -q";
380 + std::wstring listCommand = L"image list --format json";
381 if (!sessionName.empty())
382 {
382 - listCommand = std::format(L"--session \"{}\" image list -q", sessionName);
383 + listCommand = std::format(L"--session \"{}\" image list --format json", sessionName);
384 }
385
386 auto result = RunWslc(listCommand);
387 result.Verify({.Stderr = L"", .ExitCode = 0});
388
388 - auto outputLines = result.GetStdoutLines();
389 - for (const auto& line : outputLines)
389 + auto images = wsl::shared::FromJson<std::vector<wsl::windows::wslc::models::ImageInformation>>(result.Stdout.value().c_str());
390 + for (const auto& img : images)
391 {
391 - if (line.find(image.NameAndTag()) != std::wstring::npos)
392 + if (img.Repository == wsl::shared::string::WideToMultiByte(image.Name) &&
393 + img.Tag == wsl::shared::string::WideToMultiByte(image.Tag))
394 {
395 return;
396 }
test/windows/wslc/e2e/WSLCE2EImageDeleteTests.cpp
+3 -3
@@ -134,12 +134,12 @@ class WSLCE2EImageDeleteTests
134
135 VerifyImageIsListed(DebianImage);
136
137 - auto listAfter = RunWslc(L"image list -q");
137 + auto listAfter = RunWslc(L"image list");
138 listAfter.Verify({.Stderr = L"", .ExitCode = 0});
139 for (const auto& line : listAfter.GetStdoutLines())
140 {
141 - VERIFY_IS_TRUE(
142 - line.find(NoPruneTaggedImage.NameAndTag()) == std::wstring::npos,
141 + VERIFY_IS_FALSE(
142 + line.find(NoPruneTaggedImage.Name) != std::wstring::npos && line.find(NoPruneTaggedImage.Tag) != std::wstring::npos,
143 L"Secondary tag should have been removed by `image delete --no-prune`");
144 }
145 }
test/windows/wslc/e2e/WSLCE2EImageListTests.cpp
+44 -9
@@ -19,7 +19,7 @@ Abstract:
19
20 namespace WSLCE2ETests {
21 using namespace wsl::shared;
22 -
22 +using namespace wsl::windows::common::string;
23 using namespace wsl::windows::wslc::models;
24
25 class WSLCE2EImageListTests
@@ -61,22 +61,57 @@ class WSLCE2EImageListTests
61 VERIFY_FAIL(L"Failed to find the loaded image in the output");
62 }
63
64 - WSLC_TEST_METHOD(WSLCE2E_Image_List_QuietOption_OutputsNamesOnly)
64 + WSLC_TEST_METHOD(WSLCE2E_Image_List_QuietOption_OutputsIdsOnly)
65 {
66 - const auto result = RunWslc(L"image list --quiet");
67 - result.Verify({.Stderr = L"", .ExitCode = 0});
66 + // Get the expected image ID from JSON output.
67 + auto jsonResult = RunWslc(L"image list --format json");
68 + jsonResult.Verify({.Stderr = L"", .ExitCode = 0});
69 + const auto images = wsl::shared::FromJson<std::vector<ImageInformation>>(jsonResult.Stdout.value().c_str());
70
69 - bool imageFound = false;
70 - for (const auto& line : result.GetStdoutLines())
71 + std::string debianId;
72 + for (const auto& image : images)
73 {
72 - if (line == DebianImage.NameAndTag())
74 + if (image.Repository == wsl::shared::string::WideToMultiByte(DebianImage.Name) &&
75 + image.Tag == wsl::shared::string::WideToMultiByte(DebianImage.Tag))
76 {
74 - imageFound = true;
77 + debianId = image.Id;
78 break;
79 }
80 }
81 + VERIFY_ARE_NOT_EQUAL(std::string{}, debianId, L"Debian image was not present in `image list --format json` output");
82 +
83 + const auto truncatedDebianId = wsl::shared::string::MultiByteToWide(TruncateId(debianId, true));
84 + const auto fullDebianIdW = wsl::shared::string::MultiByteToWide(debianId);
85 +
86 + // Default --quiet truncates to 12 chars.
87 + auto truncResult = RunWslc(L"image list --quiet");
88 + truncResult.Verify({.Stderr = L"", .ExitCode = 0});
89
79 - VERIFY_IS_TRUE(imageFound);
90 + bool truncatedFound = false;
91 + for (const auto& line : truncResult.GetStdoutLines())
92 + {
93 + if (line == truncatedDebianId)
94 + {
95 + truncatedFound = true;
96 + break;
97 + }
98 + }
99 + VERIFY_IS_TRUE(truncatedFound, L"Truncated image ID not found in --quiet output");
100 +
101 + // --quiet --no-trunc shows the full id with sha256: prefix.
102 + auto noTruncResult = RunWslc(L"image list --quiet --no-trunc");
103 + noTruncResult.Verify({.Stderr = L"", .ExitCode = 0});
104 +
105 + bool fullFound = false;
106 + for (const auto& line : noTruncResult.GetStdoutLines())
107 + {
108 + if (line == fullDebianIdW)
109 + {
110 + fullFound = true;
111 + break;
112 + }
113 + }
114 + VERIFY_IS_TRUE(fullFound, L"Full image ID not found in --quiet --no-trunc output");
115 }
116
117 WSLC_TEST_METHOD(WSLCE2E_Image_List_InvalidFormatOption)
test/windows/wslc/e2e/WSLCE2EPushPullTests.cpp
+3 -2
@@ -77,10 +77,11 @@ class WSLCE2EPushPullTests
77 result.Verify({.Stderr = L"", .ExitCode = 0});
78
79 // Verify the image is now present.
80 - result = RunWslc(L"image list -q");
80 + auto registryRepo = registryImage.substr(0, registryImage.rfind(L':'));
81 + result = RunWslc(L"image list --format json");
82 result.Verify({.Stderr = L"", .ExitCode = 0});
83 VERIFY_IS_TRUE(result.Stdout.has_value());
83 - VERIFY_IS_TRUE(result.Stdout->find(registryImage) != std::wstring::npos);
84 + VERIFY_IS_TRUE(result.Stdout->find(registryRepo) != std::wstring::npos);
85 }
86 }
87