ci(winget): file Package Request issue first, split into two tokens
Restructure the winget release workflow so the Package Request issue is filed before the manifest PR, then referenced from the PR body with "Resolves #N" (wingetcreate has no --prbody, so a post-submit `gh pr edit` injects it). Split the single WINGET_TOKEN into two role-based secrets so the issue and the PR are authored by two distinct GitHub accounts: - WINGET_ISSUE_TOKEN authors the Package Request issue - WINGET_PR_TOKEN forks winget-pkgs, opens the manifest PR, and edits its body to link the issue (replaces WINGET_TOKEN) Role-based naming keeps this YAML identical across getsigit/sigit and smbcloudXYZ/smbcloud-cli; only the secret values differ per repo. Co-Authored-By: siGit Code v1.6.7-acp <noreply@sigit.si>
Seto Elkahfi committed
Oct 12, 2026 at 14:07 UTC
e5582cd8b1427918e7258c2746472364e812e8ab
1 file changed
+93
-35
.github/workflows/release-winget.yml
+93
-35
@@ -123,6 +123,72 @@ jobs:
123
shell: pwsh
124
run: Invoke-WebRequest -Uri "https://aka.ms/wingetcreate/latest" -OutFile wingetcreate.exe
125
126
+ # winget-pkgs convention: file the "please add/update this package"
127
+ # Package Request issue *first*, then open the manifest PR that fulfils
128
+ # it with "Resolves #N". Doing it the other way round (PR first, issue
129
+ # after) reads as "here's a request, and here's the PR that already did
130
+ # it", posted seconds apart.
131
+ #
132
+ # The issue and the PR are deliberately authored by *two different*
133
+ # GitHub accounts, because the author is decided by which PAT creates
134
+ # each one. The role-to-account mapping lives in the repo secrets, not
135
+ # here, so this YAML is identical in getsigit/sigit and smbcloud-cli;
136
+ # only the secret values are swapped:
137
+ #
138
+ # repo WINGET_ISSUE_TOKEN WINGET_PR_TOKEN
139
+ # getsigit/sigit setoelkahfi paydii
140
+ # smbcloud-cli paydii setoelkahfi
141
+ #
142
+ # Both PATs need public_repo scope on microsoft/winget-pkgs (fork + open
143
+ # issues/PRs). WINGET_PR_TOKEN replaces the old single WINGET_TOKEN.
144
+ - name: File the winget Package Request issue
145
+ id: issue
146
+ shell: bash
147
+ env:
148
+ # Authored by the issue account for this repo.
149
+ GH_TOKEN: ${{ secrets.WINGET_ISSUE_TOKEN }}
150
+ run: |
151
+ set -euo pipefail
152
+
153
+ if [ -z "${GH_TOKEN:-}" ]; then
154
+ echo "WINGET_ISSUE_TOKEN is not set. It needs a PAT (public_repo) for the account that should author the Package Request issue." >&2
155
+ exit 1
156
+ fi
157
+
158
+ VERSION="${{ steps.release.outputs.version }}"
159
+ TAG="${{ steps.release.outputs.tag }}"
160
+
161
+ # The release assets, same URLs the update step feeds wingetcreate.
162
+ BASE="https://github.com/${REPO}/releases/download/${TAG}"
163
+ INSTALLER_AMD64="${BASE}/sigit-win-amd64.exe"
164
+ INSTALLER_ARM64="${BASE}/sigit-win-arm64.exe"
165
+
166
+ # winget-pkgs' Package Request is an issue *form*
167
+ # (.github/ISSUE_TEMPLATE/package_request.yml): the title must be
168
+ # "[Package Request]: ..." and the body has to carry the labelled
169
+ # fields below (Installer Url especially — without it a maintainer
170
+ # just replies "please provide the installer link"). A free-text
171
+ # body lands as Needs-Triage and can't be acted on.
172
+ ISSUE_BODY=$(cat <<EOF
173
+ Download Page Url: https://github.com/${REPO}/releases/tag/${TAG}
174
+ Publisher: getSigit
175
+ Package Name: siGit Code
176
+ Description: siGit Code, an AI coding agent that runs on your machine.
177
+ Package Version: ${VERSION}
178
+ Installer Url: ${INSTALLER_AMD64}
179
+ Installer Url: ${INSTALLER_ARM64}
180
+ EOF
181
+ )
182
+
183
+ ISSUE_URL=$(gh issue create \
184
+ --repo microsoft/winget-pkgs \
185
+ --title "[Package Request]: ${PACKAGE_IDENTIFIER} ${VERSION}" \
186
+ --body "$ISSUE_BODY")
187
+
188
+ echo "Opened issue ${ISSUE_URL}"
189
+ echo "issue_url=${ISSUE_URL}" >> "$GITHUB_OUTPUT"
190
+ echo "issue_number=${ISSUE_URL##*/}" >> "$GITHUB_OUTPUT"
191
+
192
- name: Submit new package
193
id: submit_new
194
if: steps.exists.outputs.found == 'false'
@@ -131,10 +197,11 @@ jobs:
197
# wingetcreate reads the token from this variable. Passing it as
198
# --token instead makes the tool warn that the token can end up in
199
# logs, which on a shared runner is not a warning worth ignoring.
134
- WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }}
200
+ # This is the PR-author account for this repo (see mapping above).
201
+ WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
202
run: |
203
if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
137
- 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."
204
+ Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo scope so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR."
205
}
206
207
$version = "${{ steps.release.outputs.version }}"
@@ -164,10 +231,11 @@ jobs:
231
if: steps.exists.outputs.found == 'true'
232
shell: pwsh
233
env:
167
- WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }}
234
+ # PR-author account for this repo (see mapping above).
235
+ WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
236
run: |
237
if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
170
- 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."
238
+ Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo scope so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR."
239
}
240
241
$tag = "${{ steps.release.outputs.tag }}"
@@ -198,58 +266,47 @@ jobs:
266
Write-Warning "Could not find the PR URL in wingetcreate output; skipping issue linking."
267
}
268
201
- # wingetcreate has no --prbody flag, so it always opens the PR with the
202
- # winget-pkgs template: an empty Description and an unticked
203
- # "Linked to an issue" box next to a "Resolves #[Issue Number]"
204
- # placeholder. We used to tick that box and fill in the issue number by
205
- # hand after every release. This step does it instead.
269
+ # wingetcreate has no --prbody flag (only --prtitle), so it always opens
270
+ # the PR with the winget-pkgs template: an empty Description and an
271
+ # unticked "Linked to an issue" box next to a "Resolves #[Issue Number]"
272
+ # placeholder. The issue already exists (filed first, above), so this
273
+ # step just rewrites the body to point the PR at it with "Resolves #N".
274
#
207
- # On a first submission it also files the Package Request issue the
208
- # linkable number comes from. Updates to an existing package don't need
209
- # one (the checkbox is "if applicable"), so there we just fill the
210
- # Description.
211
- - name: Link the winget PR to a Package Request issue
275
+ # This must run as the PR author (WINGET_PR_TOKEN) — the account that
276
+ # opened the PR via wingetcreate — since only the author (or a repo
277
+ # maintainer) can edit it.
278
+ - name: Set the winget PR body and link the issue
279
id: link
280
if: >-
281
(steps.submit_new.outputs.pr_number != '') ||
282
(steps.submit_update.outputs.pr_number != '')
283
shell: bash
284
env:
218
- # The PR and the issue both live in microsoft/winget-pkgs, which the
219
- # default GITHUB_TOKEN can't write to. Reuse the wingetcreate PAT,
220
- # which already has the scope to open PRs there.
221
- GH_TOKEN: ${{ secrets.WINGET_TOKEN }}
285
+ GH_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
286
run: |
287
set -euo pipefail
288
289
PR_URL="${{ steps.submit_new.outputs.pr_url || steps.submit_update.outputs.pr_url }}"
226
- NEW_PACKAGE="${{ steps.exists.outputs.found == 'false' }}"
290
+ ISSUE_NUMBER="${{ steps.issue.outputs.issue_number }}"
291
VERSION="${{ steps.release.outputs.version }}"
292
TAG="${{ steps.release.outputs.tag }}"
293
230
- ISSUE_NUMBER=""
231
- if [ "$NEW_PACKAGE" = "true" ]; then
232
- ISSUE_URL=$(gh issue create \
233
- --repo microsoft/winget-pkgs \
234
- --title "Package Request: ${PACKAGE_IDENTIFIER} ${VERSION}" \
235
- --body "Request to add ${PACKAGE_IDENTIFIER} version ${VERSION} to the Windows Package Manager Community Repository. Installers come from ${TAG} of ${REPO}.")
236
- echo "Opened issue ${ISSUE_URL}"
237
- ISSUE_NUMBER="${ISSUE_URL##*/}"
238
- fi
239
-
240
- # Rewrite the template body: fill the Description, tick the issue
241
- # box when we have an issue, and drop in the real number (or remove
242
- # the placeholder line on updates that aren't linked to anything).
294
if [ -n "$ISSUE_NUMBER" ]; then
295
CHECKBOX="- [x] Linked to an issue (if applicable)"
296
RESOLVES="- Resolves #${ISSUE_NUMBER}"
246
- DESC="Adds ${PACKAGE_IDENTIFIER} version ${VERSION} from ${TAG}."
297
else
298
CHECKBOX="- [ ] Linked to an issue (if applicable)"
299
RESOLVES=""
300
+ fi
301
+
302
+ if [ "${{ steps.exists.outputs.found }}" = "false" ]; then
303
+ DESC="Adds ${PACKAGE_IDENTIFIER} version ${VERSION} from ${TAG}."
304
+ else
305
DESC="Updates ${PACKAGE_IDENTIFIER} to version ${VERSION} from ${TAG}."
306
fi
307
308
+ # Mirrors the current .github/PULL_REQUEST_TEMPLATE.md; keep the
309
+ # checklist items in sync with it (e.g. the 1.12 schema line).
310
BODY=$(cat <<EOF
311
## 📖 Description
312
${DESC}
@@ -260,8 +317,9 @@ jobs:
317
## 📦 Manifest Checklist
318
- [x] Checked that there aren't other open [pull requests](https://github.com/microsoft/winget-pkgs/pulls) for the same manifest update/change
319
- [x] This PR only modifies one (1) manifest
263
- - [x] Validated manifest locally with \`winget validate\`
264
- - [x] Tested manifest locally with \`winget install\`
320
+ - [x] Validated manifest locally with \`winget validate --manifest <directory>\`
321
+ - [x] Tested manifest locally with \`winget install --manifest <directory>\`
322
+ - [x] Manifest conforms to the [1.12 schema](https://github.com/microsoft/winget-pkgs/tree/master/doc/manifest/schema/1.12.0)
323
EOF
324
)
325