-
Notifications
You must be signed in to change notification settings - Fork 0
241 lines (221 loc) · 11.3 KB
/
release.yml
File metadata and controls
241 lines (221 loc) · 11.3 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
name: Release
on:
push:
tags: ['v*']
jobs:
prepare:
runs-on: ubuntu-latest
permissions:
contents: read # Only needs to read the checked-out tag — no writes
outputs:
channel: ${{ steps.channel.outputs.channel }}
signing_mode: ${{ steps.signing.outputs.mode }}
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- id: channel
run: |
if [[ "${GITHUB_REF_NAME}" == *-beta* || "${GITHUB_REF_NAME}" == *-alpha* ]]; then
echo "channel=beta" >> "$GITHUB_OUTPUT"
else
echo "channel=stable" >> "$GITHUB_OUTPUT"
fi
- id: signing
env:
# Generic signing-mode gate. Set this secret when a code-signing path
# is wired (Phase 4 — Azure Trusted Signing). Matches ws-scrcpy-web's
# SIGNING_API_TOKEN pattern (renamed from SIGNPATH_API_TOKEN there
# after SignPath Foundation declined the OSS application; here it's
# a fresh slot for whatever signer Phase 4 selects).
SIGNING_API_TOKEN: ${{ secrets.SIGNING_API_TOKEN }}
run: |
if [ -n "$SIGNING_API_TOKEN" ]; then
echo "mode=signed" >> "$GITHUB_OUTPUT"
else
echo "mode=unsigned" >> "$GITHUB_OUTPUT"
fi
build-windows:
needs: prepare
runs-on: windows-latest
permissions:
id-token: write # Sigstore OIDC for attest-build-provenance
attestations: write # Write attestation to repo via Attestations API
contents: read # Explicit (per-job block replaces defaults)
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
with:
dotnet-version: 10.x
# Pin vpk to match the NuGet `Velopack` package version in
# ControlMenu.csproj + ControlMenuLauncher.csproj — the CLI and the
# runtime library share a release/serialization line and must stay in
# lockstep. 1.1.1 is the first stable line we ship on; `--msi` /
# `--instLocation` are standard post-1.0 (the earlier 0.0.1589 prerelease
# pin predated their stable availability). Mirrors ws-scrcpy-web's pin.
- name: Install vpk CLI
run: dotnet tool install -g vpk --version 1.1.1
# No .sln in this repo — restore + build per project directly.
# Mirrors the local-pack.ps1 approach (3x dotnet publish below).
- name: Restore
run: |
dotnet restore src/ControlMenu/ControlMenu.csproj
dotnet restore src/ControlMenuLauncher/ControlMenuLauncher.csproj
dotnet restore src/ControlMenuTray/ControlMenuTray.csproj
- name: Publish ControlMenu (Blazor Server host)
run: |
dotnet publish src/ControlMenu/ControlMenu.csproj `
-c Release -r win-x64 --self-contained true `
-o publish -p:PublishSingleFile=false `
/p:Version=${{ needs.prepare.outputs.version }}
- name: Publish ControlMenuLauncher (Velopack supervisor)
run: |
dotnet publish src/ControlMenuLauncher/ControlMenuLauncher.csproj `
-c Release -r win-x64 --self-contained true `
-o publish -p:PublishSingleFile=false `
/p:Version=${{ needs.prepare.outputs.version }}
- name: Publish ControlMenuTray (Phase 1 stub)
run: |
dotnet publish src/ControlMenuTray/ControlMenuTray.csproj `
-c Release -r win-x64 --self-contained true `
-o publish -p:PublishSingleFile=false `
/p:Version=${{ needs.prepare.outputs.version }}
# Pre-seed runtime dependencies (adb, scrcpy, sqlite3, go2rtc) into
# publish/seed/dependencies/<leaf>/. Each fetch-*.ps1 script downloads
# its pinned version, verifies SHA-256, and stages the binary tree the
# launcher's SeedHydrator copies into <dataRoot>/dependencies/ on first
# boot. Mirrors ws-scrcpy-web's stage-seed-node-pty.mjs + fetch-*.mjs
# pattern. Without this step the wizard launches into a fresh install
# with no adb -> device scan hangs forever (root cause of the
# v1.1.0-beta.1 fresh-VM smoke failure).
- name: Stage runtime dependencies into publish/seed/
shell: pwsh
run: pwsh -NoProfile -File scripts/stage-seed.ps1
- name: Upload unsigned inner exes (for Phase 4 signer)
if: needs.prepare.outputs.signing_mode == 'signed'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: unsigned-windows-exes
path: |
publish/ControlMenuLauncher.exe
publish/ControlMenuTray.exe
publish/ControlMenu.exe
# FUTURE SIGNER (Phase 4): wire inner-exes signing step here.
# Input — artifact `unsigned-windows-exes` (uploaded above when signing_mode == 'signed').
# Output — signed exes placed back into `publish/` before the `vpk pack`
# step below picks them up.
# Spec § "Phase 4 — v1.1.0 Phase 4 (CI + Azure Trusted Signing)" calls
# for Azure Trusted Signing via `azure/artifact-signing-action`
# (renamed from `Azure/trusted-signing-action` at v2.0.0 — pre-v2.0.0
# versions transitively `uses: actions/cache@v4` unpinned, which fails
# this repo's `sha_pinning_required` policy at action-resolution time;
# v2.0.0 internally pins `actions/cache@<sha> # v5.0.4`, clean).
# Pin to the commit SHA, not the annotated-tag-object SHA — see PR #15
# for the OpenSSF Scorecard webapp's "imposter commit" failure mode.
# At time of writing: v2.0.0 = c7ab2a863ab5f9a846ddb8265964877ef296ee82.
# Allowlist prereq: add `azure/artifact-signing-action@*` to repo
# `allowed_actions.patterns_allowed` before this step can resolve.
- name: vpk pack (PerMachine MSI)
shell: pwsh
run: |
# PerMachine MSI is the Windows install artifact. The MSI installs
# to C:\Program Files\ControlMenu\ and runs elevated; the launcher's
# InstallAcl.EnsureWritable grants the install-root ACL at first
# non-hook startup (Velopack PerMachine Gotchas 2 + 3). Setup.exe
# is still produced as a side effect of --msi; we publish both.
vpk pack `
--packId ControlMenu `
--packVersion ${{ needs.prepare.outputs.version }} `
--packDir publish `
--mainExe ControlMenuLauncher.exe `
--packTitle "Control Menu" `
--packAuthors "bilbospocketses" `
--channel ${{ needs.prepare.outputs.channel }} `
--icon src/ControlMenu/wwwroot/favicon.ico `
--msi `
--instLocation PerMachine `
-o Releases
- name: Upload unsigned MSI (for Phase 4 signer)
if: needs.prepare.outputs.signing_mode == 'signed'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: unsigned-windows-msi
path: Releases/*.msi
# FUTURE SIGNER (Phase 4): wire MSI signing step here.
# Input — artifact `unsigned-windows-msi` (uploaded above when signing_mode == 'signed').
# Output — signed *.msi placed back into Releases/ for the publish job.
# Generate a Sigstore-backed build-provenance attestation for the MSI.
# Links the artifact to its workflow + commit + actor. Verify with:
# gh attestation verify ControlMenu-*.msi --repo bilbospocketses/control-menu
# Phase 4 (Trusted Signing) inserts above this step; attestation then
# covers the signed MSI rather than the unsigned one.
- name: Attest MSI build provenance
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: 'Releases/*.msi'
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: windows-final
path: |
Releases/*.msi
Releases/*.nupkg
Releases/releases.${{ needs.prepare.outputs.channel }}.json
publish:
needs: [prepare, build-windows]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# Pin: `name: windows-final` + matching `path:` so files land at
# `artifacts/windows-final/`, where the `softprops/action-gh-release`
# glob below expects them. actions/download-artifact@v8 with no
# `name:` flattens the single artifact directly into `path:` (no
# auto-subdirectory), which broke v1.1.0's first publish run —
# the MSI + nupkg were downloaded into `artifacts/` but the globs
# below targeted `artifacts/windows-final/*` and matched zero files,
# leaving the release with only SHA256SUMS. Recovery: manual
# `gh release upload` to the v1.1.0 release. Going forward this
# explicit `name:` makes the path deterministic and shields against
# any future single-vs-multi-artifact behavior drift in
# actions/download-artifact.
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: windows-final
path: artifacts/windows-final/
- name: Generate SHA256SUMS
run: |
cd artifacts
find . -type f \( -name '*.msi' -o -name '*.nupkg' \) -exec sha256sum {} \; > ../SHA256SUMS
cd ..
cat SHA256SUMS
- name: Generate release notes
run: |
UNSIGNED_FLAG=""
if [ "${{ needs.prepare.outputs.signing_mode }}" = "unsigned" ]; then
UNSIGNED_FLAG=" (unsigned build — SmartScreen will warn; click 'More info' → 'Run anyway')"
fi
echo "## Control Menu ${{ needs.prepare.outputs.version }}${UNSIGNED_FLAG}" > release-notes.md
echo "" >> release-notes.md
echo "Channel: \`${{ needs.prepare.outputs.channel }}\`" >> release-notes.md
echo "" >> release-notes.md
echo "**Install:** Download \`ControlMenu-${{ needs.prepare.outputs.version }}-win-Setup.msi\` below and run on Windows 11. PerMachine install to \`C:\\Program Files\\ControlMenu\\\`." >> release-notes.md
echo "" >> release-notes.md
echo "See \`CHANGELOG.md\` for the full notes." >> release-notes.md
- name: Publish GitHub Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
# NO --prerelease flag, EVEN for alpha/beta tags. Velopack's
# GithubSource queries the GitHub /releases/latest endpoint to
# discover updates; that endpoint EXCLUDES prereleases. Channel
# separation is handled by the per-channel releases.<channel>.json
# feed file already in the artifacts. Setting --prerelease=true
# would break in-app update discovery for beta-channel users.
# See feedback_velopack_permachine_lessons.md Gotcha 5.
body_path: release-notes.md
files: |
artifacts/windows-final/*.msi
artifacts/windows-final/*.nupkg
artifacts/windows-final/releases.${{ needs.prepare.outputs.channel }}.json
SHA256SUMS