1
+name: winget Release
2
+
3
+# Dispatched by release-github.yml once the release and its checksums exist.
4
+#
5
+# winget manifests live in microsoft/winget-pkgs, and `wingetcreate update`
6
+# only works on a package that is already there. So this workflow branches:
7
+#
8
+# - package already in winget-pkgs -> `wingetcreate update`, which rewrites
9
+# the URLs and re-hashes the installers for us.
10
+# - package not there yet -> render the manifests in packaging/winget/ and
11
+# `wingetcreate submit` them as a new package.
12
+#
13
+# The second path only runs once, but keeping it here means a first release
14
+# does not need someone to sit at a Windows box running `wingetcreate new`
15
+# interactively.
16
+on:
17
+ workflow_dispatch:
18
+ inputs:
19
+ tag:
20
+ description: "Release tag (e.g. v0.6.0)"
21
+ required: true
22
+
23
+concurrency:
24
+ # A re-pushed or re-dispatched tag fires this a second time. Queue the
25
+ # duplicate behind the original instead of cancelling it: cancelling a run
26
+ # mid-publish can leave a channel half-uploaded, whereas a queued duplicate
27
+ # just hits the "already published" check and exits clean.
28
+ group: ${{ github.workflow }}-${{ github.event.inputs.tag || github.ref_name }}
29
+ cancel-in-progress: false
30
+
31
+permissions:
32
+ contents: read
33
+
34
+env:
35
+ REPO: smbcloudXYZ/smbcloud-cli
36
+ PACKAGE_IDENTIFIER: smbCloudXYZ.smbcloud-cli
37
+
38
+jobs:
39
+ submit-winget-manifest:
40
+ name: Submit winget manifest
41
+ # wingetcreate is a Windows-only tool.
42
+ runs-on: windows-latest
43
+
44
+ steps:
45
+ - name: Check out repository
46
+ uses: actions/checkout@v6
47
+
48
+ - name: Resolve tag, version and release date
49
+ id: release
50
+ shell: bash
51
+ env:
52
+ GH_TOKEN: ${{ github.token }}
53
+ run: |
54
+ TAG="${{ github.event.inputs.tag }}"
55
+ VERSION="${TAG#v}"
56
+ # wingetcreate wants ReleaseDate as YYYY-MM-DD.
57
+ DATE=$(gh release view "$TAG" --repo "$REPO" --json publishedAt --jq '.publishedAt[0:10]')
58
+
59
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
60
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
61
+ echo "date=${DATE}" >> "$GITHUB_OUTPUT"
62
+
63
+ - name: Check whether the package is already in winget-pkgs
64
+ id: exists
65
+ shell: bash
66
+ env:
67
+ GH_TOKEN: ${{ github.token }}
68
+ run: |
69
+ # manifests are filed under the lowercased first letter of the
70
+ # publisher, e.g. manifests/s/smbCloudXYZ/smbcloud-cli.
71
+ PUBLISHER="${PACKAGE_IDENTIFIER%%.*}"
72
+ NAME="${PACKAGE_IDENTIFIER#*.}"
73
+ FIRST=$(echo "${PUBLISHER:0:1}" | tr '[:upper:]' '[:lower:]')
74
+ PATH_IN_REPO="manifests/${FIRST}/${PUBLISHER}/${NAME}"
75
+
76
+ if gh api "repos/microsoft/winget-pkgs/contents/${PATH_IN_REPO}" >/dev/null 2>&1; then
77
+ echo "Found ${PACKAGE_IDENTIFIER} at ${PATH_IN_REPO}; submitting an update."
78
+ echo "found=true" >> "$GITHUB_OUTPUT"
79
+ else
80
+ echo "No ${PACKAGE_IDENTIFIER} at ${PATH_IN_REPO}; submitting it as a new package."
81
+ echo "found=false" >> "$GITHUB_OUTPUT"
82
+ fi
83
+
84
+ - name: Render manifests for a first submission
85
+ if: steps.exists.outputs.found == 'false'
86
+ shell: bash
87
+ env:
88
+ GH_TOKEN: ${{ github.token }}
89
+ run: |
90
+ TAG="${{ steps.release.outputs.tag }}"
91
+ VERSION="${{ steps.release.outputs.version }}"
92
+
93
+ mkdir -p artifacts manifests
94
+
95
+ gh release download "$TAG" \
96
+ --repo "$REPO" \
97
+ --pattern "smb-win-*.exe.sha256" \
98
+ --dir artifacts/
99
+
100
+ # The schema wants 64 hex characters; the community validation
101
+ # pipeline writes them uppercase, so match that.
102
+ SHA_AMD64=$(tr -d '[:space:]' < artifacts/smb-win-amd64.exe.sha256 | tr '[:lower:]' '[:upper:]')
103
+ SHA_ARM64=$(tr -d '[:space:]' < artifacts/smb-win-arm64.exe.sha256 | tr '[:lower:]' '[:upper:]')
104
+
105
+ RELEASE_DATE="${{ steps.release.outputs.date }}"
106
+
107
+ for template in packaging/winget/*.yaml.in; do
108
+ out="manifests/$(basename "$template" .in)"
109
+ sed \
110
+ -e "s|@VERSION@|${VERSION}|g" \
111
+ -e "s|@TAG@|${TAG}|g" \
112
+ -e "s|@REPO@|${REPO}|g" \
113
+ -e "s|@SHA_AMD64@|${SHA_AMD64}|g" \
114
+ -e "s|@SHA_ARM64@|${SHA_ARM64}|g" \
115
+ -e "s|@RELEASE_DATE@|${RELEASE_DATE}|g" \
116
+ "$template" > "$out"
117
+
118
+ echo "--- ${out} ---"
119
+ cat "$out"
120
+ done
121
+
122
+ - name: Download wingetcreate
123
+ shell: pwsh
124
+ run: Invoke-WebRequest -Uri "https://aka.ms/wingetcreate/latest" -OutFile wingetcreate.exe
125
+
126
+ - name: Submit new package
127
+ if: steps.exists.outputs.found == 'false'
128
+ shell: pwsh
129
+ env:
130
+ # wingetcreate reads the token from this variable. Passing it as
131
+ # --token instead makes the tool warn that the token can end up in
132
+ # logs, which on a shared runner is not a warning worth ignoring.
133
+ WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }}
134
+ run: |
135
+ 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."
137
+ }
138
+
139
+ $version = "${{ steps.release.outputs.version }}"
140
+
141
+ # --no-open because the runner has no browser to open the PR in.
142
+ .\wingetcreate.exe submit manifests `
143
+ --prtitle "New package: $env:PACKAGE_IDENTIFIER version $version" `
144
+ --no-open
145
+
146
+ if ($LASTEXITCODE -ne 0) {
147
+ Write-Error "wingetcreate submit failed with exit code $LASTEXITCODE"
148
+ }
149
+
150
+ - name: Submit manifest update
151
+ if: steps.exists.outputs.found == 'true'
152
+ shell: pwsh
153
+ env:
154
+ WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }}
155
+ run: |
156
+ 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."
158
+ }
159
+
160
+ $tag = "${{ steps.release.outputs.tag }}"
161
+ $version = "${{ steps.release.outputs.version }}"
162
+ $base = "https://github.com/${env:REPO}/releases/download/$tag"
163
+
164
+ # The trailing |x64 and |arm64 tell wingetcreate which installer
165
+ # entry each URL replaces. Without them it guesses from the file
166
+ # name, and "smb-win-amd64.exe" is not a spelling it recognises.
167
+ .\wingetcreate.exe update $env:PACKAGE_IDENTIFIER `
168
+ --version $version `
169
+ --urls "$base/smb-win-amd64.exe|x64" "$base/smb-win-arm64.exe|arm64" `
170
+ --release-notes-url "https://github.com/${env:REPO}/releases/tag/$tag" `
171
+ --release-date "${{ steps.release.outputs.date }}" `
172
+ --prtitle "Update $env:PACKAGE_IDENTIFIER to version $version" `
173
+ --submit `
174
+ --no-open
175
+
176
+ if ($LASTEXITCODE -ne 0) {
177
+ Write-Error "wingetcreate failed with exit code $LASTEXITCODE"
178
+ }