From ba9b6a7a546de35b7015f6372605ebccffce0554 Mon Sep 17 00:00:00 2001 From: jschick04 Date: Tue, 14 Jul 2026 19:04:41 +0000 Subject: [PATCH 1/9] Add WinGet auto-publish workflow Submit the multi-architecture .msixbundle to microsoft/winget-pkgs via Microsoft's wingetcreate when a release is published (or on manual dispatch). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/winget.yml | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/winget.yml diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml new file mode 100644 index 00000000..99652b7b --- /dev/null +++ b/.github/workflows/winget.yml @@ -0,0 +1,57 @@ +name: Publish to WinGet + +# Submits the multi-architecture (x64 + arm64) .msixbundle to the Windows Package +# Manager community repo (microsoft/winget-pkgs) using Microsoft's wingetcreate tool. +on: + # Fires when a draft release is manually published as a full (stable) release. + # Pre-releases do not raise this event, so they are never pushed to WinGet. + release: + types: [released] + # Manual run: submit a specific version on demand (blank input = latest release). + workflow_dispatch: + inputs: + version: + description: 'Version to submit, e.g. 26.7.10.1209 (blank = latest release)' + required: false + type: string + +permissions: + contents: read + +env: + # wingetcreate reads this token to fork winget-pkgs and open the pull request. + # Store a classic PAT with the public_repo scope as the WINGET_TOKEN repo secret. + # See https://aka.ms/winget-create-token + WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }} + +jobs: + publish: + name: Submit Microsoft.EventLogExpert to WinGet + runs-on: windows-latest # wingetcreate runs on Windows only + steps: + - name: Submit updated manifest + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + + # Resolve the version: a manual input wins; otherwise use the release tag; + # otherwise fall back to the latest published release. + $version = '${{ inputs.version }}'.Trim() + if (-not $version) { + $version = ('${{ github.event.release.tag_name }}'.Trim()) -replace '^v', '' + } + if (-not $version) { + $headers = @{ 'User-Agent' = 'eventlogexpert-winget'; 'Authorization' = "Bearer $env:WINGET_CREATE_GITHUB_TOKEN" } + $latest = Invoke-RestMethod -Headers $headers 'https://api.github.com/repos/microsoft/EventLogExpert/releases/latest' + $version = ($latest.tag_name) -replace '^v', '' + } + if (-not $version) { throw 'Could not determine a version to submit.' } + + Write-Host "Submitting Microsoft.EventLogExpert $version to WinGet" + + # One self-contained multi-arch bundle; wingetcreate reads both the x64 and + # arm64 packages from it and updates the matching installer nodes. + $bundleUrl = "https://github.com/microsoft/EventLogExpert/releases/download/v$version/EventLogExpert_$version.msixbundle" + + curl.exe -JLO https://aka.ms/wingetcreate/latest + .\wingetcreate.exe update Microsoft.EventLogExpert --submit --version $version --urls $bundleUrl From e48b94c85ce68131a0fb21cbd477f1658789aa0c Mon Sep 17 00:00:00 2001 From: jschick04 Date: Tue, 14 Jul 2026 19:23:56 +0000 Subject: [PATCH 2/9] Address PR review feedback on WinGet workflow Strip a leading 'v' from the manual dispatch version input; only send the Authorization header to the releases API when a token is set; verify the Authenticode signature of wingetcreate.exe before running it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/winget.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 99652b7b..5b490a02 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -36,12 +36,13 @@ jobs: # Resolve the version: a manual input wins; otherwise use the release tag; # otherwise fall back to the latest published release. - $version = '${{ inputs.version }}'.Trim() + $version = ('${{ inputs.version }}'.Trim()) -replace '^v', '' if (-not $version) { $version = ('${{ github.event.release.tag_name }}'.Trim()) -replace '^v', '' } if (-not $version) { - $headers = @{ 'User-Agent' = 'eventlogexpert-winget'; 'Authorization' = "Bearer $env:WINGET_CREATE_GITHUB_TOKEN" } + $headers = @{ 'User-Agent' = 'eventlogexpert-winget' } + if ($env:WINGET_CREATE_GITHUB_TOKEN) { $headers['Authorization'] = "Bearer $env:WINGET_CREATE_GITHUB_TOKEN" } $latest = Invoke-RestMethod -Headers $headers 'https://api.github.com/repos/microsoft/EventLogExpert/releases/latest' $version = ($latest.tag_name) -replace '^v', '' } @@ -53,5 +54,10 @@ jobs: # arm64 packages from it and updates the matching installer nodes. $bundleUrl = "https://github.com/microsoft/EventLogExpert/releases/download/v$version/EventLogExpert_$version.msixbundle" - curl.exe -JLO https://aka.ms/wingetcreate/latest + # Download Microsoft's wingetcreate and verify its Authenticode signature before running it. + curl.exe -sSL -o wingetcreate.exe https://aka.ms/wingetcreate/latest + $signature = Get-AuthenticodeSignature .\wingetcreate.exe + if ($signature.Status -ne 'Valid' -or $signature.SignerCertificate.Subject -notlike '*Microsoft Corporation*') { + throw "wingetcreate.exe failed Authenticode verification (status: $($signature.Status); signer: $($signature.SignerCertificate.Subject))." + } .\wingetcreate.exe update Microsoft.EventLogExpert --submit --version $version --urls $bundleUrl From d75bb89d091862f8fee59d6f3ac1fdb122e7657f Mon Sep 17 00:00:00 2001 From: jschick04 Date: Tue, 14 Jul 2026 19:32:38 +0000 Subject: [PATCH 3/9] Address second review pass on WinGet workflow Use github.event.inputs.version (null-safe on release triggers); use github.repository for the release API and download URLs; validate the resolved version is dotted-numeric before submitting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/winget.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 5b490a02..04cb5563 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -36,23 +36,26 @@ jobs: # Resolve the version: a manual input wins; otherwise use the release tag; # otherwise fall back to the latest published release. - $version = ('${{ inputs.version }}'.Trim()) -replace '^v', '' + $version = ('${{ github.event.inputs.version }}'.Trim()) -replace '^v', '' if (-not $version) { $version = ('${{ github.event.release.tag_name }}'.Trim()) -replace '^v', '' } if (-not $version) { $headers = @{ 'User-Agent' = 'eventlogexpert-winget' } if ($env:WINGET_CREATE_GITHUB_TOKEN) { $headers['Authorization'] = "Bearer $env:WINGET_CREATE_GITHUB_TOKEN" } - $latest = Invoke-RestMethod -Headers $headers 'https://api.github.com/repos/microsoft/EventLogExpert/releases/latest' + $latest = Invoke-RestMethod -Headers $headers 'https://api.github.com/repos/${{ github.repository }}/releases/latest' $version = ($latest.tag_name) -replace '^v', '' } if (-not $version) { throw 'Could not determine a version to submit.' } + if ($version -notmatch '^\d+(\.\d+){1,3}$') { + throw "Resolved version '$version' is not a valid dotted-numeric version (e.g. 26.7.10.1209)." + } Write-Host "Submitting Microsoft.EventLogExpert $version to WinGet" # One self-contained multi-arch bundle; wingetcreate reads both the x64 and # arm64 packages from it and updates the matching installer nodes. - $bundleUrl = "https://github.com/microsoft/EventLogExpert/releases/download/v$version/EventLogExpert_$version.msixbundle" + $bundleUrl = "https://github.com/${{ github.repository }}/releases/download/v$version/EventLogExpert_$version.msixbundle" # Download Microsoft's wingetcreate and verify its Authenticode signature before running it. curl.exe -sSL -o wingetcreate.exe https://aka.ms/wingetcreate/latest From f78c29f08447c97046a3bb7ebc2f38c0d498823d Mon Sep 17 00:00:00 2001 From: jschick04 Date: Tue, 14 Jul 2026 19:41:43 +0000 Subject: [PATCH 4/9] Harden WinGet workflow: prerelease guard and exit-code checks Add a job-level if: guard so release events run only for non-prereleases (keeping the released trigger, which already excludes them). Add curl -f plus explicit LASTEXITCODE checks after the download and the wingetcreate update so native-command failures fail the step immediately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/winget.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 04cb5563..5d7fc282 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -27,6 +27,8 @@ env: jobs: publish: name: Submit Microsoft.EventLogExpert to WinGet + # Stable releases only: manual dispatch runs on demand; release events run only for non-prereleases. + if: ${{ github.event_name != 'release' || github.event.release.prerelease == false }} runs-on: windows-latest # wingetcreate runs on Windows only steps: - name: Submit updated manifest @@ -58,9 +60,11 @@ jobs: $bundleUrl = "https://github.com/${{ github.repository }}/releases/download/v$version/EventLogExpert_$version.msixbundle" # Download Microsoft's wingetcreate and verify its Authenticode signature before running it. - curl.exe -sSL -o wingetcreate.exe https://aka.ms/wingetcreate/latest + curl.exe -fsSL -o wingetcreate.exe https://aka.ms/wingetcreate/latest + if ($LASTEXITCODE -ne 0) { throw "Failed to download wingetcreate.exe (curl exit $LASTEXITCODE)." } $signature = Get-AuthenticodeSignature .\wingetcreate.exe if ($signature.Status -ne 'Valid' -or $signature.SignerCertificate.Subject -notlike '*Microsoft Corporation*') { throw "wingetcreate.exe failed Authenticode verification (status: $($signature.Status); signer: $($signature.SignerCertificate.Subject))." } .\wingetcreate.exe update Microsoft.EventLogExpert --submit --version $version --urls $bundleUrl + if ($LASTEXITCODE -ne 0) { throw "wingetcreate update failed (exit $LASTEXITCODE)." } From 40f2ed5183d82d708855ebf8ee09c70f13e82a3d Mon Sep 17 00:00:00 2001 From: jschick04 Date: Tue, 14 Jul 2026 19:47:40 +0000 Subject: [PATCH 5/9] Fail fast on missing WINGET_TOKEN; align trigger comment with guard Throw a clear error when the WINGET_TOKEN secret is empty instead of letting wingetcreate fail with a cryptic auth error. Reword the release-trigger comment so it is consistent with the job-level pre-release guard. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/winget.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 5d7fc282..08ccac54 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -3,8 +3,8 @@ name: Publish to WinGet # Submits the multi-architecture (x64 + arm64) .msixbundle to the Windows Package # Manager community repo (microsoft/winget-pkgs) using Microsoft's wingetcreate tool. on: - # Fires when a draft release is manually published as a full (stable) release. - # Pre-releases do not raise this event, so they are never pushed to WinGet. + # Fires when a draft release is published as a full (stable) release. `released` + # does not fire for pre-releases; the job-level guard below also enforces stable-only. release: types: [released] # Manual run: submit a specific version on demand (blank input = latest release). @@ -36,6 +36,10 @@ jobs: run: | $ErrorActionPreference = 'Stop' + if (-not $env:WINGET_CREATE_GITHUB_TOKEN) { + throw 'The WINGET_TOKEN secret is not set. Add a classic PAT with the public_repo scope as the WINGET_TOKEN repository secret.' + } + # Resolve the version: a manual input wins; otherwise use the release tag; # otherwise fall back to the latest published release. $version = ('${{ github.event.inputs.version }}'.Trim()) -replace '^v', '' From f56a51b8ae371976ce3d236e13472baf834d8cf0 Mon Sep 17 00:00:00 2001 From: jschick04 Date: Tue, 14 Jul 2026 19:51:56 +0000 Subject: [PATCH 6/9] Use token auth scheme for the releases API fallback Switch the Authorization header on the releases/latest lookup from Bearer to the token scheme, the conventional form for classic PATs (both are accepted by the REST API). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/winget.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 08ccac54..44b8dee6 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -48,7 +48,7 @@ jobs: } if (-not $version) { $headers = @{ 'User-Agent' = 'eventlogexpert-winget' } - if ($env:WINGET_CREATE_GITHUB_TOKEN) { $headers['Authorization'] = "Bearer $env:WINGET_CREATE_GITHUB_TOKEN" } + if ($env:WINGET_CREATE_GITHUB_TOKEN) { $headers['Authorization'] = "token $env:WINGET_CREATE_GITHUB_TOKEN" } $latest = Invoke-RestMethod -Headers $headers 'https://api.github.com/repos/${{ github.repository }}/releases/latest' $version = ($latest.tag_name) -replace '^v', '' } From 9071ca72abb860f4689fccc618f22938b0baea53 Mon Sep 17 00:00:00 2001 From: jschick04 Date: Tue, 14 Jul 2026 20:05:38 +0000 Subject: [PATCH 7/9] Harden WinGet workflow: env-pass event inputs, pin runner Pass github.event.inputs.version and github.event.release.tag_name through step env vars instead of inlining them into the PowerShell script, removing the script-injection vector. Pin runs-on to windows-2022 to match the repo's other workflows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/winget.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 44b8dee6..ff4fbafd 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -29,10 +29,13 @@ jobs: name: Submit Microsoft.EventLogExpert to WinGet # Stable releases only: manual dispatch runs on demand; release events run only for non-prereleases. if: ${{ github.event_name != 'release' || github.event.release.prerelease == false }} - runs-on: windows-latest # wingetcreate runs on Windows only + runs-on: windows-2022 # pinned like the repo's other workflows; wingetcreate runs on Windows only steps: - name: Submit updated manifest shell: pwsh + env: + INPUT_VERSION: ${{ github.event.inputs.version }} + RELEASE_TAG: ${{ github.event.release.tag_name }} run: | $ErrorActionPreference = 'Stop' @@ -41,10 +44,11 @@ jobs: } # Resolve the version: a manual input wins; otherwise use the release tag; - # otherwise fall back to the latest published release. - $version = ('${{ github.event.inputs.version }}'.Trim()) -replace '^v', '' + # otherwise fall back to the latest published release. Event-supplied values + # are read from env vars (not inlined) to avoid script injection. + $version = ("$env:INPUT_VERSION".Trim()) -replace '^v', '' if (-not $version) { - $version = ('${{ github.event.release.tag_name }}'.Trim()) -replace '^v', '' + $version = ("$env:RELEASE_TAG".Trim()) -replace '^v', '' } if (-not $version) { $headers = @{ 'User-Agent' = 'eventlogexpert-winget' } From d46708caf15605ebeab9daedf4b8f976561c8c0d Mon Sep 17 00:00:00 2001 From: jschick04 Date: Tue, 14 Jul 2026 20:23:44 +0000 Subject: [PATCH 8/9] Use GITHUB_TOKEN for the releases API; guard and comment refinements Use the ephemeral job GITHUB_TOKEN (contents: read, least privilege) for the releases/latest lookup and reserve the WINGET_TOKEN PAT for wingetcreate. Use prerelease != true in the stable-only guard, and clarify in the comment that github.repository is a trusted, non-user context value. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/winget.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index ff4fbafd..49ef9b2b 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -28,7 +28,7 @@ jobs: publish: name: Submit Microsoft.EventLogExpert to WinGet # Stable releases only: manual dispatch runs on demand; release events run only for non-prereleases. - if: ${{ github.event_name != 'release' || github.event.release.prerelease == false }} + if: ${{ github.event_name != 'release' || github.event.release.prerelease != true }} runs-on: windows-2022 # pinned like the repo's other workflows; wingetcreate runs on Windows only steps: - name: Submit updated manifest @@ -36,6 +36,9 @@ jobs: env: INPUT_VERSION: ${{ github.event.inputs.version }} RELEASE_TAG: ${{ github.event.release.tag_name }} + # Least privilege: the ephemeral job token (contents: read) is used for the + # Releases API lookup; the WINGET_TOKEN PAT is reserved for wingetcreate. + GH_API_TOKEN: ${{ github.token }} run: | $ErrorActionPreference = 'Stop' @@ -44,15 +47,16 @@ jobs: } # Resolve the version: a manual input wins; otherwise use the release tag; - # otherwise fall back to the latest published release. Event-supplied values - # are read from env vars (not inlined) to avoid script injection. + # otherwise fall back to the latest published release. The event-supplied + # version values are read from env vars (not inlined) to avoid script injection; + # github.repository below is a trusted, non-user-controlled context value. $version = ("$env:INPUT_VERSION".Trim()) -replace '^v', '' if (-not $version) { $version = ("$env:RELEASE_TAG".Trim()) -replace '^v', '' } if (-not $version) { $headers = @{ 'User-Agent' = 'eventlogexpert-winget' } - if ($env:WINGET_CREATE_GITHUB_TOKEN) { $headers['Authorization'] = "token $env:WINGET_CREATE_GITHUB_TOKEN" } + if ($env:GH_API_TOKEN) { $headers['Authorization'] = "Bearer $env:GH_API_TOKEN" } $latest = Invoke-RestMethod -Headers $headers 'https://api.github.com/repos/${{ github.repository }}/releases/latest' $version = ($latest.tag_name) -replace '^v', '' } From 90ab2d1cc8c2aa2dc86ecfb3e64d86b485c2ef90 Mon Sep 17 00:00:00 2001 From: jschick04 Date: Tue, 14 Jul 2026 20:29:22 +0000 Subject: [PATCH 9/9] Require exactly four version components EventLogExpert releases use four-part versions (e.g. 26.7.10.1209); tighten the version-validation regex to reject shorter forms that would build a nonexistent bundle URL. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/winget.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 49ef9b2b..b1f94f34 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -61,8 +61,8 @@ jobs: $version = ($latest.tag_name) -replace '^v', '' } if (-not $version) { throw 'Could not determine a version to submit.' } - if ($version -notmatch '^\d+(\.\d+){1,3}$') { - throw "Resolved version '$version' is not a valid dotted-numeric version (e.g. 26.7.10.1209)." + if ($version -notmatch '^\d+(\.\d+){3}$') { + throw "Resolved version '$version' is not a valid four-part version (e.g. 26.7.10.1209)." } Write-Host "Submitting Microsoft.EventLogExpert $version to WinGet"