Repository navigation
328 lines (286 loc) · 14.2 KB
/
Copy pathrelease-winget.yml
File metadata and controls
328 lines (286 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
name: winget Release
# Dispatched by release-github.yml once the release and its checksums exist.
#
# winget manifests live in microsoft/winget-pkgs, and `wingetcreate update`
# only works on a package that is already there. So this workflow branches:
#
# - package already in winget-pkgs -> `wingetcreate update`, which rewrites
# the URLs and re-hashes the installers for us.
# - package not there yet -> render the manifests in packaging/winget/ and
# `wingetcreate submit` them as a new package.
#
# The second path only runs once, but keeping it here means a first release
# does not need someone to sit at a Windows box running `wingetcreate new`
# interactively.
on:
workflow_dispatch:
inputs:
tag:
description: "Release tag (e.g. v1.5.2)"
required: true
concurrency:
# A re-pushed or re-dispatched tag fires this a second time. Queue the
# duplicate behind the original instead of cancelling it: cancelling a run
# mid-publish can leave a channel half-uploaded, whereas a queued duplicate
# just hits the "already published" check and exits clean.
group: ${{ github.workflow }}-${{ github.event.inputs.tag || github.ref_name }}
cancel-in-progress: false
permissions:
contents: read
env:
REPO: getsigit/sigit
PACKAGE_IDENTIFIER: getSigit.siGitCode
jobs:
submit-winget-manifest:
name: Submit winget manifest
# wingetcreate is a Windows-only tool.
runs-on: windows-latest
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Resolve tag, version and release date
id: release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ github.event.inputs.tag }}"
VERSION="${TAG#v}"
# wingetcreate wants ReleaseDate as YYYY-MM-DD.
DATE=$(gh release view "$TAG" --repo "$REPO" --json publishedAt --jq '.publishedAt[0:10]')
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "date=${DATE}" >> "$GITHUB_OUTPUT"
- name: Check whether the package is already in winget-pkgs
id: exists
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
# manifests are filed under the lowercased first letter of the
# publisher, e.g. manifests/g/getSigit/siGitCode.
PUBLISHER="${PACKAGE_IDENTIFIER%%.*}"
NAME="${PACKAGE_IDENTIFIER#*.}"
FIRST=$(echo "${PUBLISHER:0:1}" | tr '[:upper:]' '[:lower:]')
PATH_IN_REPO="manifests/${FIRST}/${PUBLISHER}/${NAME}"
if gh api "repos/microsoft/winget-pkgs/contents/${PATH_IN_REPO}" >/dev/null 2>&1; then
echo "Found ${PACKAGE_IDENTIFIER} at ${PATH_IN_REPO}; submitting an update."
echo "found=true" >> "$GITHUB_OUTPUT"
else
echo "No ${PACKAGE_IDENTIFIER} at ${PATH_IN_REPO}; submitting it as a new package."
echo "found=false" >> "$GITHUB_OUTPUT"
fi
- name: Render manifests for a first submission
if: steps.exists.outputs.found == 'false'
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ steps.release.outputs.tag }}"
VERSION="${{ steps.release.outputs.version }}"
mkdir -p artifacts manifests
gh release download "$TAG" \
--repo "$REPO" \
--pattern "sigit-win-*.exe.sha256" \
--dir artifacts/
# The schema wants 64 hex characters; the community validation
# pipeline writes them uppercase, so match that.
SHA_AMD64=$(tr -d '[:space:]' < artifacts/sigit-win-amd64.exe.sha256 | tr '[:lower:]' '[:upper:]')
SHA_ARM64=$(tr -d '[:space:]' < artifacts/sigit-win-arm64.exe.sha256 | tr '[:lower:]' '[:upper:]')
RELEASE_DATE="${{ steps.release.outputs.date }}"
for template in packaging/winget/*.yaml.in; do
out="manifests/$(basename "$template" .in)"
sed \
-e "s|@VERSION@|${VERSION}|g" \
-e "s|@TAG@|${TAG}|g" \
-e "s|@REPO@|${REPO}|g" \
-e "s|@SHA_AMD64@|${SHA_AMD64}|g" \
-e "s|@SHA_ARM64@|${SHA_ARM64}|g" \
-e "s|@RELEASE_DATE@|${RELEASE_DATE}|g" \
"$template" > "$out"
echo "--- ${out} ---"
cat "$out"
done
- name: Download wingetcreate
shell: pwsh
run: Invoke-WebRequest -Uri "https://aka.ms/wingetcreate/latest" -OutFile wingetcreate.exe
# winget-pkgs convention: file the "please add/update this package"
# Package Request issue *first*, then open the manifest PR that fulfils
# it with "Resolves #N". Doing it the other way round (PR first, issue
# after) reads as "here's a request, and here's the PR that already did
# it", posted seconds apart.
#
# The issue and the PR are authored by two separate PATs, configured as
# the repo secrets WINGET_ISSUE_TOKEN (the Package Request issue) and
# WINGET_PR_TOKEN (the manifest PR). Both need public_repo scope on
# microsoft/winget-pkgs (fork + open issues/PRs). WINGET_PR_TOKEN also
# needs read:org and read:discussion: the "Set the winget PR body" step
# below calls `gh pr edit`, whose GraphQL query resolves org/team and
# discussion fields and fails with a scope error without them.
# WINGET_PR_TOKEN replaces the old single WINGET_TOKEN.
- name: File the winget Package Request issue
id: issue
shell: bash
env:
# Authored by the issue account for this repo.
GH_TOKEN: ${{ secrets.WINGET_ISSUE_TOKEN }}
run: |
set -euo pipefail
if [ -z "${GH_TOKEN:-}" ]; then
echo "WINGET_ISSUE_TOKEN is not set. It needs a PAT (public_repo) for the account that should author the Package Request issue." >&2
exit 1
fi
VERSION="${{ steps.release.outputs.version }}"
TAG="${{ steps.release.outputs.tag }}"
# The release assets, same URLs the update step feeds wingetcreate.
BASE="https://github.com/${REPO}/releases/download/${TAG}"
INSTALLER_AMD64="${BASE}/sigit-win-amd64.exe"
INSTALLER_ARM64="${BASE}/sigit-win-arm64.exe"
# winget-pkgs' Package Request is an issue *form*
# (.github/ISSUE_TEMPLATE/package_request.yml): the title must be
# "[Package Request]: ..." and the body has to carry the labelled
# fields below (Installer Url especially — without it a maintainer
# just replies "please provide the installer link"). A free-text
# body lands as Needs-Triage and can't be acted on.
ISSUE_BODY=$(cat <<EOF
Download Page Url: https://github.com/${REPO}/releases/tag/${TAG}
Publisher: getSigit
Package Name: siGit Code
Description: siGit Code, an AI coding agent that runs on your machine.
Package Version: ${VERSION}
Installer Url: ${INSTALLER_AMD64}
Installer Url: ${INSTALLER_ARM64}
EOF
)
ISSUE_URL=$(gh issue create \
--repo microsoft/winget-pkgs \
--title "[Package Request]: ${PACKAGE_IDENTIFIER} ${VERSION}" \
--body "$ISSUE_BODY")
echo "Opened issue ${ISSUE_URL}"
echo "issue_url=${ISSUE_URL}" >> "$GITHUB_OUTPUT"
echo "issue_number=${ISSUE_URL##*/}" >> "$GITHUB_OUTPUT"
- name: Sebat dulu ...
if: steps.issue.outputs.issue_number != ''
shell: bash
run: sleep $(( RANDOM % 61 + 150 ))
- name: Submit new package
id: submit_new
if: steps.exists.outputs.found == 'false'
shell: pwsh
env:
# wingetcreate reads the token from this variable. Passing it as
# --token instead makes the tool warn that the token can end up in
# logs, which on a shared runner is not a warning worth ignoring.
# This is the PR-author account for this repo (see mapping above).
WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
run: |
if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo, read:org, and read:discussion scopes so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR, and gh can edit the PR body."
}
$version = "${{ steps.release.outputs.version }}"
# Tee stdout so we can scrape the PR URL afterwards. wingetcreate
# prints "Pull request can be found here: <url>" on success, and
# that line is the only handle we get on the PR it just opened.
# --no-open because the runner has no browser to open the PR in.
.\wingetcreate.exe submit manifests `
--prtitle "New package: $env:PACKAGE_IDENTIFIER version $version" `
--no-open 2>&1 | Tee-Object -Variable output
if ($LASTEXITCODE -ne 0) {
Write-Error "wingetcreate submit failed with exit code $LASTEXITCODE"
}
$match = [regex]::Match(($output -join "`n"), 'https://github\.com/microsoft/winget-pkgs/pull/(\d+)')
if ($match.Success) {
"pr_url=$($match.Value)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"pr_number=$($match.Groups[1].Value)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
} else {
Write-Warning "Could not find the PR URL in wingetcreate output; skipping issue linking."
}
- name: Submit manifest update
id: submit_update
if: steps.exists.outputs.found == 'true'
shell: pwsh
env:
# PR-author account for this repo (see mapping above).
WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
run: |
if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo, read:org, and read:discussion scopes so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR, and gh can edit the PR body."
}
$tag = "${{ steps.release.outputs.tag }}"
$version = "${{ steps.release.outputs.version }}"
$base = "https://github.com/${env:REPO}/releases/download/$tag"
# The trailing |x64 and |arm64 tell wingetcreate which installer
# entry each URL replaces. Without them it guesses from the file
# name, and "sigit-win-amd64.exe" is not a spelling it recognises.
.\wingetcreate.exe update $env:PACKAGE_IDENTIFIER `
--version $version `
--urls "$base/sigit-win-amd64.exe|x64" "$base/sigit-win-arm64.exe|arm64" `
--release-notes-url "https://github.com/${env:REPO}/releases/tag/$tag" `
--release-date "${{ steps.release.outputs.date }}" `
--prtitle "Update $env:PACKAGE_IDENTIFIER to version $version" `
--submit `
--no-open 2>&1 | Tee-Object -Variable output
if ($LASTEXITCODE -ne 0) {
Write-Error "wingetcreate failed with exit code $LASTEXITCODE"
}
$match = [regex]::Match(($output -join "`n"), 'https://github\.com/microsoft/winget-pkgs/pull/(\d+)')
if ($match.Success) {
"pr_url=$($match.Value)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"pr_number=$($match.Groups[1].Value)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
} else {
Write-Warning "Could not find the PR URL in wingetcreate output; skipping issue linking."
}
# wingetcreate has no --prbody flag (only --prtitle), so it always opens
# the PR with the winget-pkgs template: an empty Description and an
# unticked "Linked to an issue" box next to a "Resolves #[Issue Number]"
# placeholder. The issue already exists (filed first, above), so this
# step just rewrites the body to point the PR at it with "Resolves #N".
#
# This must run as the PR author (WINGET_PR_TOKEN) — the account that
# opened the PR via wingetcreate — since only the author (or a repo
# maintainer) can edit it.
- name: Set the winget PR body and link the issue
id: link
if: >-
(steps.submit_new.outputs.pr_number != '') ||
(steps.submit_update.outputs.pr_number != '')
shell: bash
env:
GH_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
run: |
set -euo pipefail
PR_URL="${{ steps.submit_new.outputs.pr_url || steps.submit_update.outputs.pr_url }}"
ISSUE_NUMBER="${{ steps.issue.outputs.issue_number }}"
VERSION="${{ steps.release.outputs.version }}"
TAG="${{ steps.release.outputs.tag }}"
if [ -n "$ISSUE_NUMBER" ]; then
CHECKBOX="- [x] Linked to an issue (if applicable)"
RESOLVES="- Resolves #${ISSUE_NUMBER}"
else
CHECKBOX="- [ ] Linked to an issue (if applicable)"
RESOLVES=""
fi
if [ "${{ steps.exists.outputs.found }}" = "false" ]; then
DESC="Adds ${PACKAGE_IDENTIFIER} version ${VERSION} from ${TAG}."
else
DESC="Updates ${PACKAGE_IDENTIFIER} to version ${VERSION} from ${TAG}."
fi
# Mirrors the current .github/PULL_REQUEST_TEMPLATE.md; keep the
# checklist items in sync with it (e.g. the 1.12 schema line).
BODY=$(cat <<EOF
## 📖 Description
${DESC}
## ✅ Checklist
- [x] Signed the [Contributor License Agreement](https://cla.opensource.microsoft.com)
${CHECKBOX}
${RESOLVES}
## 📦 Manifest Checklist
- [x] Checked that there aren't other open [pull requests](https://github.com/microsoft/winget-pkgs/pulls) for the same manifest update/change
- [x] This PR only modifies one (1) manifest
- [x] Validated manifest locally with \`winget validate --manifest <directory>\`
- [x] Tested manifest locally with \`winget install --manifest <directory>\`
- [x] Manifest conforms to the [1.12 schema](https://github.com/microsoft/winget-pkgs/tree/master/doc/manifest/schema/1.12.0)
EOF
)
gh pr edit "$PR_URL" --body "$BODY"
echo "Linked ${PR_URL} to issue #${ISSUE_NUMBER:-<none>}"