From 0c7fc5825d92115e4f3368461cf4bb4706e5658c Mon Sep 17 00:00:00 2001 From: Tom Plant Date: Fri, 15 May 2026 21:15:05 +1000 Subject: [PATCH 1/7] Run GitHub Copilot cloud agent on Windows --- .github/workflows/copilot-setup-steps.yml | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/copilot-setup-steps.yml diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 0000000000..daee628f88 --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,24 @@ +name: "Copilot Setup Steps" + +# Automatically run the setup steps when they are changed to allow for easy validation, and +# allow manual testing through the repository's "Actions" tab +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: windows-latest + + # Set the permissions to the lowest permissions possible needed for your steps. + # Copilot will be given its own token for its operations. + permissions: + # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. + # If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. + contents: read From 50514bf15c53d109976243830bffca2412b8a9db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 15 May 2026 11:19:17 +0000 Subject: [PATCH 2/7] Initial plan From 4125656c79a1f0f9b42a4f95b2266a30f3666105 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 15 May 2026 11:25:40 +0000 Subject: [PATCH 3/7] Add LoggingUseCMTrace setting with CMTrace log format support and tests Agent-Logs-Url: https://github.com/devicie/winget-cli/sessions/c4d184c0-b0f2-46bc-9e42-e68d558e7f8a Co-authored-by: pl4nty <21111317+pl4nty@users.noreply.github.com> --- src/AppInstallerCLITests/UserSettings.cpp | 40 +++++++++++++ src/AppInstallerCommonCore/FileLogger.cpp | 57 ++++++++++++++++++- .../Public/winget/UserSettings.h | 2 + src/AppInstallerCommonCore/UserSettings.cpp | 1 + 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/AppInstallerCLITests/UserSettings.cpp b/src/AppInstallerCLITests/UserSettings.cpp index b1b28519b3..3bb213d957 100644 --- a/src/AppInstallerCLITests/UserSettings.cpp +++ b/src/AppInstallerCLITests/UserSettings.cpp @@ -925,6 +925,46 @@ TEST_CASE("SettingOutputSortDirection", "[settings]") } } +TEST_CASE("SettingLoggingUseCMTrace", "[settings]") +{ + auto again = DeleteUserSettingsFiles(); + + SECTION("Default value") + { + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == false); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Enabled") + { + std::string_view json = R"({ "logging": { "useCMTrace": true } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == true); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Disabled") + { + std::string_view json = R"({ "logging": { "useCMTrace": false } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == false); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Bad value type") + { + std::string_view json = R"({ "logging": { "useCMTrace": "yes" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == false); + REQUIRE(userSettingTest.GetWarnings().size() == 1); + } +} + TEST_CASE("ConvertToSortField", "[settings]") { SECTION("Valid values - lowercase") diff --git a/src/AppInstallerCommonCore/FileLogger.cpp b/src/AppInstallerCommonCore/FileLogger.cpp index a430694ef6..14751b8909 100644 --- a/src/AppInstallerCommonCore/FileLogger.cpp +++ b/src/AppInstallerCommonCore/FileLogger.cpp @@ -29,6 +29,53 @@ namespace AppInstaller::Logging return std::move(strstr).str(); } + // Formats a log line in CMTrace format. + // CMTrace log format: + std::string ToCMTraceLogLine(Channel channel, Level level, std::string_view message) + { + auto now = std::chrono::system_clock::now(); + auto tt = std::chrono::system_clock::to_time_t(now); + tm localTime{}; + _localtime64_s(&localTime, &tt); + + auto sinceEpoch = now.time_since_epoch(); + auto leftoverMillis = std::chrono::duration_cast(sinceEpoch) - std::chrono::duration_cast(sinceEpoch); + + // Get UTC bias in minutes (positive means west of UTC, CMTrace uses positive for west) + long timezoneBiasSeconds = 0; + _get_timezone(&timezoneBiasSeconds); + long biasMins = timezoneBiasSeconds / 60; + + // CMTrace type: 1=Info/Verbose, 2=Warning, 3=Error/Critical + int type; + switch (level) + { + case Level::Warning: type = 2; break; + case Level::Error: + case Level::Crit: type = 3; break; + default: type = 1; break; + } + + std::stringstream strstr; + strstr << "" + << ""; + return std::move(strstr).str(); + } + // Determines the difference between the given position and the maximum as an offset. std::ofstream::off_type CalculateDiff(const std::ofstream::pos_type& position, std::ofstream::off_type maximum) { @@ -94,7 +141,15 @@ namespace AppInstaller::Logging void FileLogger::Write(Channel channel, Level level, std::string_view message) noexcept try { - std::string log = ToLogLine(channel, level, message); + std::string log; + if (Settings::User().Get()) + { + log = ToCMTraceLogLine(channel, level, message); + } + else + { + log = ToLogLine(channel, level, message); + } WriteDirect(channel, level, log); } catch (...) {} diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 9909d9acd3..fe82dd294d 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -133,6 +133,7 @@ namespace AppInstaller::Settings LoggingFileTotalSizeLimitInMB, LoggingFileIndividualSizeLimitInMB, LoggingFileCountLimit, + LoggingUseCMTrace, // Uninstall behavior UninstallPurgePortablePackage, // Download behavior @@ -237,6 +238,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileTotalSizeLimitInMB, uint32_t, uint32_t, 128, ".logging.file.totalSizeLimitInMB"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileIndividualSizeLimitInMB, uint32_t, uint32_t, 16, ".logging.file.individualSizeLimitInMB"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileCountLimit, uint32_t, uint32_t, 0, ".logging.file.countLimit"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::LoggingUseCMTrace, bool, bool, false, ".logging.useCMTrace"sv); // Interactivity SETTINGMAPPING_SPECIALIZATION(Setting::InteractivityDisable, bool, bool, false, ".interactivity.disable"sv); // Output behavior diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 38cdd0847a..203e2a216c 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -299,6 +299,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(LoggingFileTotalSizeLimitInMB) WINGET_VALIDATE_PASS_THROUGH(LoggingFileIndividualSizeLimitInMB) WINGET_VALIDATE_PASS_THROUGH(LoggingFileCountLimit) + WINGET_VALIDATE_PASS_THROUGH(LoggingUseCMTrace) #ifndef AICLI_DISABLE_TEST_HOOKS WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump) From ad9fd589a5e355b6a4409c477050792209006094 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 07:09:13 +0000 Subject: [PATCH 4/7] Rename CMTrace to CCM, change bool setting to logging.format enum, add build workflow Agent-Logs-Url: https://github.com/devicie/winget-cli/sessions/7d39e80e-abcc-4238-8301-b2a0f059dcaf Co-authored-by: pl4nty <21111317+pl4nty@users.noreply.github.com> --- .github/workflows/build-msix.yml | 75 +++++++++++++++++++ src/AppInstallerCLITests/UserSettings.cpp | 38 +++++++--- src/AppInstallerCommonCore/FileLogger.cpp | 12 +-- .../Public/winget/UserSettings.h | 4 +- src/AppInstallerCommonCore/UserSettings.cpp | 17 ++++- .../Public/AppInstallerLogging.h | 9 +++ 6 files changed, 136 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/build-msix.yml diff --git a/.github/workflows/build-msix.yml b/.github/workflows/build-msix.yml new file mode 100644 index 0000000000..94220a9bd2 --- /dev/null +++ b/.github/workflows/build-msix.yml @@ -0,0 +1,75 @@ +name: Build MSIX + +on: + workflow_dispatch: + push: + branches: + - 'copilot/**' + paths: + - 'src/**' + - '.github/workflows/build-msix.yml' + pull_request: + paths: + - 'src/**' + - '.github/workflows/build-msix.yml' + +jobs: + build: + runs-on: windows-latest + timeout-minutes: 120 + + permissions: + contents: read + + strategy: + matrix: + platform: [x64] + configuration: [Release] + + env: + solution: 'src\AppInstallerCLI.sln' + buildOutDir: ${{ github.workspace }}\src\${{ matrix.platform }}\${{ matrix.configuration }} + appxPackageDir: ${{ github.workspace }}\artifacts\${{ matrix.platform }}\AppxPackages + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install NuGet + uses: NuGet/setup-nuget@v2 + + - name: Restore Solution (NuGet) + run: nuget restore ${{ env.solution }} + + - name: Restore AppInstallerCLIPackage (NuGet) + run: nuget restore src\AppInstallerCLIPackage\AppInstallerCLIPackage.wapproj + + - name: Restore .NET projects + run: dotnet restore src + + - name: Integrate vcpkg + run: | + & "$env:VCPKG_INSTALLATION_ROOT\vcpkg.exe" integrate install + shell: pwsh + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v2 + + - name: Build Solution + run: | + msbuild ${{ env.solution }} ` + /p:Platform=${{ matrix.platform }} ` + /p:Configuration=${{ matrix.configuration }} ` + /p:AppxBundlePlatforms="${{ matrix.platform }}" ` + /p:AppxPackageDir="${{ env.appxPackageDir }}" ` + /p:AppxBundle=Always ` + /p:UapAppxPackageBuildMode=SideloadOnly ` + /m + shell: pwsh + + - name: Upload MSIX artifacts + uses: actions/upload-artifact@v4 + with: + name: msix-${{ matrix.platform }}-${{ matrix.configuration }} + path: ${{ env.appxPackageDir }} + retention-days: 7 diff --git a/src/AppInstallerCLITests/UserSettings.cpp b/src/AppInstallerCLITests/UserSettings.cpp index 3bb213d957..1063a810d3 100644 --- a/src/AppInstallerCLITests/UserSettings.cpp +++ b/src/AppInstallerCLITests/UserSettings.cpp @@ -925,7 +925,7 @@ TEST_CASE("SettingOutputSortDirection", "[settings]") } } -TEST_CASE("SettingLoggingUseCMTrace", "[settings]") +TEST_CASE("SettingLoggingFormat", "[settings]") { auto again = DeleteUserSettingsFiles(); @@ -933,34 +933,52 @@ TEST_CASE("SettingLoggingUseCMTrace", "[settings]") { UserSettingsTest userSettingTest; - REQUIRE(userSettingTest.Get() == false); + REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); REQUIRE(userSettingTest.GetWarnings().size() == 0); } - SECTION("Enabled") + SECTION("WinGet") { - std::string_view json = R"({ "logging": { "useCMTrace": true } })"; + std::string_view json = R"({ "logging": { "format": "winget" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; - REQUIRE(userSettingTest.Get() == true); + REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); REQUIRE(userSettingTest.GetWarnings().size() == 0); } - SECTION("Disabled") + SECTION("CCM") { - std::string_view json = R"({ "logging": { "useCMTrace": false } })"; + std::string_view json = R"({ "logging": { "format": "ccm" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; - REQUIRE(userSettingTest.Get() == false); + REQUIRE(userSettingTest.Get() == LogFileFormat::CCM); REQUIRE(userSettingTest.GetWarnings().size() == 0); } + SECTION("Case insensitive CCM") + { + std::string_view json = R"({ "logging": { "format": "CCM" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == LogFileFormat::CCM); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Bad value") + { + std::string_view json = R"({ "logging": { "format": "cmtrace" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); + REQUIRE(userSettingTest.GetWarnings().size() == 1); + } SECTION("Bad value type") { - std::string_view json = R"({ "logging": { "useCMTrace": "yes" } })"; + std::string_view json = R"({ "logging": { "format": true } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; - REQUIRE(userSettingTest.Get() == false); + REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); REQUIRE(userSettingTest.GetWarnings().size() == 1); } } diff --git a/src/AppInstallerCommonCore/FileLogger.cpp b/src/AppInstallerCommonCore/FileLogger.cpp index 14751b8909..aefd8f97d9 100644 --- a/src/AppInstallerCommonCore/FileLogger.cpp +++ b/src/AppInstallerCommonCore/FileLogger.cpp @@ -29,9 +29,9 @@ namespace AppInstaller::Logging return std::move(strstr).str(); } - // Formats a log line in CMTrace format. - // CMTrace log format: - std::string ToCMTraceLogLine(Channel channel, Level level, std::string_view message) + // Formats a log line in CCM (CMTrace-compatible) format. + // CCM log format: + std::string ToCCMLogLine(Channel channel, Level level, std::string_view message) { auto now = std::chrono::system_clock::now(); auto tt = std::chrono::system_clock::to_time_t(now); @@ -46,7 +46,7 @@ namespace AppInstaller::Logging _get_timezone(&timezoneBiasSeconds); long biasMins = timezoneBiasSeconds / 60; - // CMTrace type: 1=Info/Verbose, 2=Warning, 3=Error/Critical + // CCM type: 1=Info/Verbose, 2=Warning, 3=Error/Critical int type; switch (level) { @@ -142,9 +142,9 @@ namespace AppInstaller::Logging void FileLogger::Write(Channel channel, Level level, std::string_view message) noexcept try { std::string log; - if (Settings::User().Get()) + if (Settings::User().Get() == LogFileFormat::CCM) { - log = ToCMTraceLogLine(channel, level, message); + log = ToCCMLogLine(channel, level, message); } else { diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index fe82dd294d..2761367192 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -133,7 +133,7 @@ namespace AppInstaller::Settings LoggingFileTotalSizeLimitInMB, LoggingFileIndividualSizeLimitInMB, LoggingFileCountLimit, - LoggingUseCMTrace, + LoggingFormat, // Uninstall behavior UninstallPurgePortablePackage, // Download behavior @@ -238,7 +238,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileTotalSizeLimitInMB, uint32_t, uint32_t, 128, ".logging.file.totalSizeLimitInMB"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileIndividualSizeLimitInMB, uint32_t, uint32_t, 16, ".logging.file.individualSizeLimitInMB"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileCountLimit, uint32_t, uint32_t, 0, ".logging.file.countLimit"sv); - SETTINGMAPPING_SPECIALIZATION(Setting::LoggingUseCMTrace, bool, bool, false, ".logging.useCMTrace"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFormat, std::string, Logging::LogFileFormat, Logging::LogFileFormat::WinGet, ".logging.format"sv); // Interactivity SETTINGMAPPING_SPECIALIZATION(Setting::InteractivityDisable, bool, bool, false, ".interactivity.disable"sv); // Output behavior diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 203e2a216c..7c3272abe5 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -299,7 +299,6 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(LoggingFileTotalSizeLimitInMB) WINGET_VALIDATE_PASS_THROUGH(LoggingFileIndividualSizeLimitInMB) WINGET_VALIDATE_PASS_THROUGH(LoggingFileCountLimit) - WINGET_VALIDATE_PASS_THROUGH(LoggingUseCMTrace) #ifndef AICLI_DISABLE_TEST_HOOKS WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump) @@ -531,6 +530,22 @@ namespace AppInstaller::Settings return value * 24h; } + WINGET_VALIDATE_SIGNATURE(LoggingFormat) + { + static constexpr std::string_view s_format_winget = "winget"; + static constexpr std::string_view s_format_ccm = "ccm"; + + if (Utility::CaseInsensitiveEquals(value, s_format_winget)) + { + return LogFileFormat::WinGet; + } + else if (Utility::CaseInsensitiveEquals(value, s_format_ccm)) + { + return LogFileFormat::CCM; + } + return {}; + } + WINGET_VALIDATE_SIGNATURE(OutputSortOrder) { std::vector fields; diff --git a/src/AppInstallerSharedLib/Public/AppInstallerLogging.h b/src/AppInstallerSharedLib/Public/AppInstallerLogging.h index 39dbed0b6f..f7094e942e 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerLogging.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerLogging.h @@ -98,6 +98,15 @@ namespace AppInstaller::Logging ShortGuid, }; + // The format used when writing log entries to a file. + enum class LogFileFormat + { + // Default WinGet format: " [channel] message" + WinGet, + // CCM/CMTrace-compatible format recognized by CMTrace and Microsoft Endpoint Configuration Manager log viewers + CCM, + }; + // Indicates a location of significance in the logging stream. enum class Tag { From 046bef92f38736ad59da36119d9a5f33759fefab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 02:19:17 +0000 Subject: [PATCH 5/7] Fix spelling check: add 'fff' (datetime milliseconds token) to expect.txt --- .github/actions/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 6551758cbd..10ab13c0bf 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -179,6 +179,7 @@ fdw fdwgp FECAFEB fedorapeople +fff fileinuse filemode Filetime From f46a9548b15dbd0557b8f4466dbb28203682d5e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:17:11 +0000 Subject: [PATCH 6/7] Address review: move format syntax to enum comment, remove redundant FileLogger comment, move SettingLoggingFormat test, remove fff from spellcheck --- .github/actions/spelling/expect.txt | 1 - src/AppInstallerCLITests/UserSettings.cpp | 116 +++++++++--------- src/AppInstallerCommonCore/FileLogger.cpp | 1 - .../Public/AppInstallerLogging.h | 2 +- 4 files changed, 59 insertions(+), 61 deletions(-) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 10ab13c0bf..6551758cbd 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -179,7 +179,6 @@ fdw fdwgp FECAFEB fedorapeople -fff fileinuse filemode Filetime diff --git a/src/AppInstallerCLITests/UserSettings.cpp b/src/AppInstallerCLITests/UserSettings.cpp index 1063a810d3..3d7b4d2ac8 100644 --- a/src/AppInstallerCLITests/UserSettings.cpp +++ b/src/AppInstallerCLITests/UserSettings.cpp @@ -386,6 +386,64 @@ TEST_CASE("SettingLoggingFileNameStrategy", "[settings]") { } } +TEST_CASE("SettingLoggingFormat", "[settings]") +{ + auto again = DeleteUserSettingsFiles(); + + SECTION("Default value") + { + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("WinGet") + { + std::string_view json = R"({ "logging": { "format": "winget" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("CCM") + { + std::string_view json = R"({ "logging": { "format": "ccm" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == LogFileFormat::CCM); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Case insensitive CCM") + { + std::string_view json = R"({ "logging": { "format": "CCM" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == LogFileFormat::CCM); + REQUIRE(userSettingTest.GetWarnings().size() == 0); + } + SECTION("Bad value") + { + std::string_view json = R"({ "logging": { "format": "cmtrace" } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); + REQUIRE(userSettingTest.GetWarnings().size() == 1); + } + SECTION("Bad value type") + { + std::string_view json = R"({ "logging": { "format": true } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); + REQUIRE(userSettingTest.GetWarnings().size() == 1); + } +} + TEST_CASE("SettingAutoUpdateIntervalInMinutes", "[settings]") { auto again = DeleteUserSettingsFiles(); @@ -925,64 +983,6 @@ TEST_CASE("SettingOutputSortDirection", "[settings]") } } -TEST_CASE("SettingLoggingFormat", "[settings]") -{ - auto again = DeleteUserSettingsFiles(); - - SECTION("Default value") - { - UserSettingsTest userSettingTest; - - REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); - REQUIRE(userSettingTest.GetWarnings().size() == 0); - } - SECTION("WinGet") - { - std::string_view json = R"({ "logging": { "format": "winget" } })"; - SetSetting(Stream::PrimaryUserSettings, json); - UserSettingsTest userSettingTest; - - REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); - REQUIRE(userSettingTest.GetWarnings().size() == 0); - } - SECTION("CCM") - { - std::string_view json = R"({ "logging": { "format": "ccm" } })"; - SetSetting(Stream::PrimaryUserSettings, json); - UserSettingsTest userSettingTest; - - REQUIRE(userSettingTest.Get() == LogFileFormat::CCM); - REQUIRE(userSettingTest.GetWarnings().size() == 0); - } - SECTION("Case insensitive CCM") - { - std::string_view json = R"({ "logging": { "format": "CCM" } })"; - SetSetting(Stream::PrimaryUserSettings, json); - UserSettingsTest userSettingTest; - - REQUIRE(userSettingTest.Get() == LogFileFormat::CCM); - REQUIRE(userSettingTest.GetWarnings().size() == 0); - } - SECTION("Bad value") - { - std::string_view json = R"({ "logging": { "format": "cmtrace" } })"; - SetSetting(Stream::PrimaryUserSettings, json); - UserSettingsTest userSettingTest; - - REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); - REQUIRE(userSettingTest.GetWarnings().size() == 1); - } - SECTION("Bad value type") - { - std::string_view json = R"({ "logging": { "format": true } })"; - SetSetting(Stream::PrimaryUserSettings, json); - UserSettingsTest userSettingTest; - - REQUIRE(userSettingTest.Get() == LogFileFormat::WinGet); - REQUIRE(userSettingTest.GetWarnings().size() == 1); - } -} - TEST_CASE("ConvertToSortField", "[settings]") { SECTION("Valid values - lowercase") diff --git a/src/AppInstallerCommonCore/FileLogger.cpp b/src/AppInstallerCommonCore/FileLogger.cpp index aefd8f97d9..aa512006f1 100644 --- a/src/AppInstallerCommonCore/FileLogger.cpp +++ b/src/AppInstallerCommonCore/FileLogger.cpp @@ -30,7 +30,6 @@ namespace AppInstaller::Logging } // Formats a log line in CCM (CMTrace-compatible) format. - // CCM log format: std::string ToCCMLogLine(Channel channel, Level level, std::string_view message) { auto now = std::chrono::system_clock::now(); diff --git a/src/AppInstallerSharedLib/Public/AppInstallerLogging.h b/src/AppInstallerSharedLib/Public/AppInstallerLogging.h index f7094e942e..223b16a8f1 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerLogging.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerLogging.h @@ -103,7 +103,7 @@ namespace AppInstaller::Logging { // Default WinGet format: " [channel] message" WinGet, - // CCM/CMTrace-compatible format recognized by CMTrace and Microsoft Endpoint Configuration Manager log viewers + // CCM/CMTrace-compatible format: CCM, }; From 261302e46c75a0006a8562f0199d0f18b30bc3fe Mon Sep 17 00:00:00 2001 From: Tom Plant Date: Mon, 8 Jun 2026 23:28:36 +1000 Subject: [PATCH 7/7] Fix LogFileFormat --- src/AppInstallerSharedLib/Public/AppInstallerLogging.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerSharedLib/Public/AppInstallerLogging.h b/src/AppInstallerSharedLib/Public/AppInstallerLogging.h index 223b16a8f1..ceb3959054 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerLogging.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerLogging.h @@ -103,7 +103,7 @@ namespace AppInstaller::Logging { // Default WinGet format: " [channel] message" WinGet, - // CCM/CMTrace-compatible format: + // CCM/CMTrace-compatible format: "" date="" component="" context="" type="N" thread="" file="">" CCM, };