-
Notifications
You must be signed in to change notification settings - Fork 0
312 lines (283 loc) · 12.4 KB
/
Copy pathrelease.yml
File metadata and controls
312 lines (283 loc) · 12.4 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
# Release pipeline (TASK-M10-04): tag-triggered GitHub Release with the
# three-platform installer set, release notes from CHANGELOG.md and the
# latest.json updater manifest consumed by the in-app updater (TASK-M8-09).
#
# Flow:
# v* tag (or manual workflow_dispatch) ->
# version-check: resolve vX.Y.Z, assert package.json / tauri.conf.json /
# Cargo.toml all match (scripts/check-version.mjs), extract
# the CHANGELOG section (scripts/release-notes.mjs) and
# create the PUBLISHED release with it
# build matrix: macOS universal (app+dmg), Windows x64 (nsis+msi),
# Linux x64 (deb+AppImage) — the same signing conditions as
# desktop.yml; every job uploads its artifacts to the
# release via tauri-apps/tauri-action@v0 (idempotent per
# tag: the release created above is found by tag, never
# modified, and each job just adds its assets)
# finalize: download the updater assets from the release and assemble
# latest.json (scripts/gen-latest-json.mjs) with PUBLIC
# download URLs, attach it to the release
#
# Design decisions (recorded in docs/tasks/M10.md, TASK-M10-04):
# - The release is PUBLISHED immediately on tag push: no manual publish
# step, matching the previous debug-channel behavior that users relied on
# for three-platform installers.
# - latest.json is generated by scripts/gen-latest-json.mjs because
# tauri-action's native includeUpdaterJson writes api.github.com asset
# URLs (unauthenticated rate-limited, auth-dependent); public
# releases/download/<tag>/ URLs are passed to the updater instead
# (includeUpdaterJson: false).
# - Updater signatures must match the public key baked into the app
# (tauri.conf.json plugins.updater.pubkey). A tag build without
# TAURI_SIGNING_PRIVATE_KEY FAILS fast instead of shipping a release
# whose updates would be rejected; only dry-runs fall back to a
# throwaway CI key (its artifacts never ship).
# - Manual workflow_dispatch defaults to dry-run: the full build matrix
# runs green but no GitHub Release is touched — this is the CI-side half
# of the M10-04 acceptance (no real tag push is possible locally).
# - Build steps deliberately duplicate desktop.yml: GitHub Actions cannot
# reuse steps across workflow files without composite actions, and the
# release matrix uploads to a release instead of artifacts.
name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
dry-run:
description: >-
Build everything but do not create or upload to a GitHub Release.
type: boolean
default: true
permissions:
contents: write
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true
env:
# Updater artifact signing (TASK-M8-09). Empty when the secret is absent:
# tag builds fail fast, dry-runs generate a throwaway CI key below.
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# Apple signing/notarization secrets are intentionally NOT set at the
# workflow level: when a secret is absent GitHub passes an empty string,
# and tauri-cli treats a present-but-empty APPLE_CERTIFICATE as "import
# this certificate" -> keychain import fails. The certificate is scoped to
# the `Import Apple certificate` step instead. `if` conditions cannot
# read the `secrets` context, so the presence flag is materialized here
# and checked via env.
HAS_APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE != '' }}
jobs:
version-check:
name: Version sync check
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.resolve.outputs.version }}
tag: ${{ steps.resolve.outputs.tag }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
# Tag runs use the pushed tag; manual runs derive the tag from
# package.json (dry-run or not).
- name: Resolve version
id: resolve
run: |
if [[ "${{ github.ref_type }}" == "tag" ]]; then
TAG="${{ github.ref_name }}"
else
TAG="v$(node -p "require('./package.json').version")"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
- name: Check version sync
run: node scripts/check-version.mjs ${{ steps.resolve.outputs.version }}
- name: Extract release notes
run: |
node scripts/release-notes.mjs ${{ steps.resolve.outputs.version }} \
--out release-notes.md
- name: Create release
if: ${{ !inputs.dry-run }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.resolve.outputs.tag }}
run: |
if ! gh release view "$TAG" >/dev/null 2>&1; then
gh release create "$TAG" \
--title "opencoder $TAG" \
--notes-file release-notes.md
fi
build:
name: Build and upload (${{ matrix.platform }})
needs: version-check
strategy:
fail-fast: false
matrix:
include:
- platform: macos-14
target: universal-apple-darwin
rust-targets: aarch64-apple-darwin x86_64-apple-darwin
bundles: app,dmg
- platform: windows-latest
target: x86_64-pc-windows-msvc
rust-targets: x86_64-pc-windows-msvc
bundles: nsis,msi
- platform: ubuntu-22.04
target: x86_64-unknown-linux-gnu
rust-targets: x86_64-unknown-linux-gnu
bundles: deb,appimage
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust-targets }}
- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
# System dependencies required by Tauri v2 bundling on Linux
# (webkit2gtk-4.1, appindicator, rsvg for icons, patchelf for AppImage)
- name: Install Linux dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
build-essential \
curl \
wget \
file \
libxdo-dev \
libssl-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf
# macOS signing needs the Developer ID certificate imported into a
# throwaway keychain before tauri-cli runs (docs/release-signing.md).
# The secrets are referenced here (not at the workflow level) so the
# step only runs when APPLE_CERTIFICATE actually has a value.
- name: Import Apple certificate
if: runner.os == 'macOS' && env.HAS_APPLE_CERTIFICATE == 'true'
env:
KEYCHAIN: ${{ runner.temp }}/app-signing.keychain-db
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
echo "$APPLE_CERTIFICATE" | base64 --decode > /tmp/app.p12
security create-keychain -p "" "$KEYCHAIN"
security default-keychain -s "$KEYCHAIN"
security unlock-keychain -p "" "$KEYCHAIN"
security import /tmp/app.p12 -P "$APPLE_CERTIFICATE_PASSWORD" \
-A -t cert -f pkcs12 -k "$KEYCHAIN"
security set-key-partition-list -S apple-tool:,apple: -s -k "" \
"$KEYCHAIN"
rm /tmp/app.p12
# A release whose updater signatures do not match the public key baked
# into the app (tauri.conf.json plugins.updater.pubkey) would ship
# rejected updates: fail fast instead of building one.
- name: Require updater signing key for releases
if: ${{ !inputs.dry-run && env.TAURI_SIGNING_PRIVATE_KEY == '' }}
run: |
echo "::error::Tag builds need TAURI_SIGNING_PRIVATE_KEY (updater signatures must match the configured public key, see docs/release-signing.md)."
exit 1
# Dry-runs only: without a real secret, generate a throwaway CI key so
# the unsigned verification build stays green (same as desktop.yml;
# these artifacts never ship).
- name: Generate CI updater signing key (dry-run only)
if: ${{ inputs.dry-run && env.TAURI_SIGNING_PRIVATE_KEY == '' }}
run: |
pnpm tauri signer generate --ci -w "$RUNNER_TEMP/ci-signing.key"
echo "TAURI_SIGNING_PRIVATE_KEY=$RUNNER_TEMP/ci-signing.key" \
>> "$GITHUB_ENV"
# Build + upload every artifact to the release created in
# version-check. tauri-action finds the existing release by tag and
# adds this job's assets; includeUpdaterJson stays off (see file header).
- name: Build and upload to release
if: ${{ !inputs.dry-run }}
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tagName: ${{ needs.version-check.outputs.tag }}
releaseName: opencoder ${{ needs.version-check.outputs.tag }}
releaseDraft: false
prerelease: false
includeUpdaterJson: false
args: --ci --target ${{ matrix.target }} --bundles ${{ matrix.bundles }}
- name: Upload build artifacts (dry-run)
if: ${{ inputs.dry-run }}
uses: actions/upload-artifact@v4
with:
name: opencoder-${{ matrix.platform }}-${{ matrix.target }}
if-no-files-found: error
path: |
src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg
src-tauri/target/${{ matrix.target }}/release/bundle/macos/*
src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*
src-tauri/target/${{ matrix.target }}/release/bundle/msi/*
src-tauri/target/${{ matrix.target }}/release/bundle/deb/*
src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*
finalize:
name: Assemble updater manifest
if: ${{ !inputs.dry-run }}
needs: [version-check, build]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
# Only macOS ships a zipped updater artifact (app.tar.gz): in Tauri v2
# (createUpdaterArtifacts: true, i.e. NOT v1Compatible) the Windows
# .exe/.msi and the Linux .AppImage/.deb installers are self contained
# and get signed directly — no .nsis.zip / .msi.zip is ever produced.
# The patterns below must therefore cover those installers too, or
# gen-latest-json.mjs finds a .sig with no matching asset and silently
# drops the platform.
- name: Download updater assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ needs.version-check.outputs.tag }}
run: |
mkdir -p assets
gh release download "$TAG" --dir assets --clobber \
--pattern '*.sig' \
--pattern '*.tar.gz' \
--pattern '*.zip' \
--pattern '*.AppImage' \
--pattern '*.deb' \
--pattern '*.exe' \
--pattern '*.msi'
- name: Extract release notes
run: |
node scripts/release-notes.mjs ${{ needs.version-check.outputs.version }} \
--out release-notes.md
# Assembles latest.json from the release's updater assets and fails if
# any expected platform is missing (universal macOS covers both darwin
# keys).
- name: Generate latest.json
run: |
node scripts/gen-latest-json.mjs assets \
${{ needs.version-check.outputs.tag }} \
--notes-file release-notes.md \
--require darwin-aarch64,darwin-x86_64,windows-x86_64,linux-x86_64
- name: Upload latest.json
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ needs.version-check.outputs.tag }}
run: gh release upload "$TAG" latest.json --clobber