ci: file the winget tracking issue before opening the PR
Split the winget submission into an issue-then-PR two-token flow. The tracking issue is opened first, under the package's owning account, so the winget-pkgs PR that follows can reference it. WINGET_ISSUE_TOKEN files the issue and WINGET_PR_TOKEN opens the PR and comments the issue link back onto it. The tokens stay separate because the ordering depends on the issue number. Capture the winget-pkgs PR URL from wingetcreate's output on both the new-package and update paths, and downgrade a missed parse to a warning rather than failing a release whose manifest PR already shipped. Co-Authored-By: siGit Code v1.6.7-acp <noreply@sigit.si> Co-Authored-By: SplitFire v1.1.1-acp <noreply@splitfire.ai>
Seto Elkahfi committed
Oct 13, 2026 at 12:05 UTC
d50cb1e33d1d6cfc4a3ebe9ee311a279177376f9
1 file changed
+74
-8
.github/workflows/release-winget.yml
+74
-8
@@ -60,6 +60,32 @@ jobs:
60
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
61
echo "date=${DATE}" >> "$GITHUB_OUTPUT"
62
63
+ # Step 1 of the two-token flow: open the tracking issue *first*, under the
64
+ # package's owning account, so the winget-pkgs PR opened in step 2 can
65
+ # reference it. WINGET_ISSUE_TOKEN is a classic PAT with public_repo scope;
66
+ # github.token is not used here because it would attribute the issue to the
67
+ # Actions bot instead of the account that files the PR.
68
+ - name: Open tracking issue
69
+ id: issue
70
+ shell: bash
71
+ env:
72
+ GH_TOKEN: ${{ secrets.WINGET_ISSUE_TOKEN }}
73
+ run: |
74
+ if [ -z "$GH_TOKEN" ]; then
75
+ echo "::error::WINGET_ISSUE_TOKEN is not set. It needs a classic PAT with public_repo scope under the package's owning account so the tracking issue is filed before the winget-pkgs PR."
76
+ exit 1
77
+ fi
78
+
79
+ TAG="${{ steps.release.outputs.tag }}"
80
+ VERSION="${{ steps.release.outputs.version }}"
81
+
82
+ URL=$(gh issue create --repo "$REPO" \
83
+ --title "Publish ${PACKAGE_IDENTIFIER} ${VERSION} to winget" \
84
+ --body "Tracking the winget-pkgs submission for \`${PACKAGE_IDENTIFIER}\` version ${VERSION} (${TAG}). The microsoft/winget-pkgs PR will reference this issue.")
85
+
86
+ echo "Opened tracking issue: ${URL}"
87
+ echo "number=${URL##*/}" >> "$GITHUB_OUTPUT"
88
+
89
- name: Check whether the package is already in winget-pkgs
90
id: exists
91
shell: bash
@@ -123,38 +149,52 @@ jobs:
149
shell: pwsh
150
run: Invoke-WebRequest -Uri "https://aka.ms/wingetcreate/latest" -OutFile wingetcreate.exe
151
152
+ # Step 2 of the two-token flow: open the PR with WINGET_PR_TOKEN (mapped to
153
+ # WINGET_CREATE_GITHUB_TOKEN, the only env var wingetcreate reads). Both
154
+ # tokens are classic PATs under the same account; keeping them separate
155
+ # preserves the issue-then-PR ordering the issue number depends on.
156
- name: Submit new package
157
+ id: submit
158
if: steps.exists.outputs.found == 'false'
159
shell: pwsh
160
env:
161
# wingetcreate reads the token from this variable. Passing it as
162
# --token instead makes the tool warn that the token can end up in
163
# logs, which on a shared runner is not a warning worth ignoring.
133
- WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }}
164
+ WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
165
run: |
166
if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
136
- Write-Error "WINGET_TOKEN is not set. It needs a PAT with public_repo scope so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR."
167
+ Write-Error "WINGET_PR_TOKEN is not set. It needs a classic PAT with public_repo scope so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR."
168
}
169
170
$version = "${{ steps.release.outputs.version }}"
171
172
# --no-open because the runner has no browser to open the PR in.
142
- .\wingetcreate.exe submit manifests `
173
+ $output = .\wingetcreate.exe submit manifests `
174
--prtitle "New package: $env:PACKAGE_IDENTIFIER version $version" `
144
- --no-open
175
+ --no-open 2>&1 | Tee-Object -Variable raw
176
+ $raw | Out-String | Write-Host
177
178
if ($LASTEXITCODE -ne 0) {
179
Write-Error "wingetcreate submit failed with exit code $LASTEXITCODE"
180
}
181
182
+ # Capture the opened PR URL so the next step can link the tracking issue.
183
+ $prUrl = (($raw | Out-String) | Select-String -Pattern 'https://github.com/microsoft/winget-pkgs/pull/\d+').Matches.Value | Select-Object -First 1
184
+ if ($prUrl) {
185
+ "pr_url=$prUrl" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
186
+ }
187
+
188
+ # Step 2 of the two-token flow (update path): same WINGET_PR_TOKEN mapping.
189
- name: Submit manifest update
190
+ id: update
191
if: steps.exists.outputs.found == 'true'
192
shell: pwsh
193
env:
154
- WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }}
194
+ WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
195
run: |
196
if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
157
- Write-Error "WINGET_TOKEN is not set. It needs a PAT with public_repo scope so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR."
197
+ Write-Error "WINGET_PR_TOKEN is not set. It needs a classic PAT with public_repo scope so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR."
198
}
199
200
$tag = "${{ steps.release.outputs.tag }}"
@@ -164,15 +204,41 @@ jobs:
204
# The trailing |x64 and |arm64 tell wingetcreate which installer
205
# entry each URL replaces. Without them it guesses from the file
206
# name, and "onde-win-amd64.exe" is not a spelling it recognises.
167
- .\wingetcreate.exe update $env:PACKAGE_IDENTIFIER `
207
+ $output = .\wingetcreate.exe update $env:PACKAGE_IDENTIFIER `
208
--version $version `
209
--urls "$base/onde-win-amd64.exe|x64" "$base/onde-win-arm64.exe|arm64" `
210
--release-notes-url "https://github.com/${env:REPO}/releases/tag/$tag" `
211
--release-date "${{ steps.release.outputs.date }}" `
212
--prtitle "Update $env:PACKAGE_IDENTIFIER to version $version" `
213
--submit `
174
- --no-open
214
+ --no-open 2>&1 | Tee-Object -Variable raw
215
+ $raw | Out-String | Write-Host
216
217
if ($LASTEXITCODE -ne 0) {
218
Write-Error "wingetcreate failed with exit code $LASTEXITCODE"
219
}
220
+
221
+ # Capture the opened PR URL so the next step can link the tracking issue.
222
+ $prUrl = (($raw | Out-String) | Select-String -Pattern 'https://github.com/microsoft/winget-pkgs/pull/\d+').Matches.Value | Select-Object -First 1
223
+ if ($prUrl) {
224
+ "pr_url=$prUrl" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
225
+ }
226
+
227
+ # Close the issue-then-PR loop: comment the tracking issue onto the PR that
228
+ # step 2 opened. continue-on-error so a missed URL parse never fails a
229
+ # release that already shipped its manifest PR.
230
+ - name: Link tracking issue on the winget-pkgs PR
231
+ if: steps.issue.outputs.number != ''
232
+ continue-on-error: true
233
+ shell: bash
234
+ env:
235
+ GH_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
236
+ run: |
237
+ PR_URL="${{ steps.update.outputs.pr_url }}${{ steps.submit.outputs.pr_url }}"
238
+ if [ -z "$PR_URL" ]; then
239
+ echo "::warning::Could not determine the winget-pkgs PR URL; skipping the issue link. Link ${REPO}#${{ steps.issue.outputs.number }} by hand."
240
+ exit 0
241
+ fi
242
+
243
+ gh pr comment "$PR_URL" --repo microsoft/winget-pkgs \
244
+ --body "Related upstream issue: ${REPO}#${{ steps.issue.outputs.number }}"