@ondeinference / cli / commits / d50cb1e

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 }}"