forked from QwenLM/qwen-code
-
Notifications
You must be signed in to change notification settings - Fork 0
322 lines (299 loc) · 13.3 KB
/
Copy pathlive-host-release.yml
File metadata and controls
322 lines (299 loc) · 13.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
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
name: 'Qwen Live Host Release'
run-name: "Qwen Live Host release ${{ inputs.version || format('PR #{0} dry run', github.event.pull_request.number) }}"
on:
pull_request:
paths:
- '.github/workflows/live-host-release.yml'
- 'packages/desktop/apps/live-host/**'
- 'packages/desktop/bun.lock'
- 'packages/desktop/package.json'
- 'packages/desktop/scripts/bump-live-host-version.ts'
workflow_dispatch:
inputs:
version:
description: 'Live Host version, for example 0.1.0 or v0.1.0.'
required: true
type: 'string'
dry_run:
description: 'Build unsigned packages without publishing.'
required: true
default: true
type: 'boolean'
draft:
description: 'Create a draft GitHub release.'
required: true
default: true
type: 'boolean'
prerelease:
description: 'Mark the release as a prerelease.'
required: true
default: false
type: 'boolean'
clobber:
description: 'Replace same-named assets in an existing release.'
required: true
default: false
type: 'boolean'
permissions:
contents: 'read'
concurrency:
group: 'live-host-release-${{ github.event.pull_request.number || inputs.version }}'
cancel-in-progress: false
env:
BUN_VERSION: '1.3.9'
NODE_VERSION: '22.20.0'
LIVE_HOST_FEED_TAG: 'live-host-latest'
jobs:
prepare:
name: 'Prepare Live Host release'
runs-on: 'ubuntu-latest'
timeout-minutes: 10
outputs:
tag: '${{ steps.version.outputs.tag }}'
version: '${{ steps.version.outputs.version }}'
steps:
- name: 'Resolve version'
id: 'version'
shell: 'bash'
env:
INPUT_VERSION: '${{ inputs.version }}'
PR_NUMBER: '${{ github.event.pull_request.number }}'
run: |
set -euo pipefail
if [ "$GITHUB_EVENT_NAME" = 'pull_request' ]; then
version="0.0.$PR_NUMBER"
else
version="${INPUT_VERSION#v}"
fi
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([+-][0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Live Host version must be valid SemVer: $INPUT_VERSION"
exit 1
fi
if [[ "$version" == *+* ]]; then
echo '::error::Live Host releases do not support SemVer build metadata.'
exit 1
fi
if [ "$GITHUB_EVENT_NAME" = 'workflow_dispatch' ] && [ "${{ inputs.dry_run }}" = 'false' ] && [ "$GITHUB_REF_NAME" != 'main' ]; then
echo '::error::Published Live Host releases must run from main.'
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "tag=live-host-v$version" >> "$GITHUB_OUTPUT"
build:
name: 'Build Qwen Live Host'
needs: 'prepare'
runs-on: 'macos-latest'
timeout-minutes: 60
steps:
- uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
- uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
with:
node-version: '${{ env.NODE_VERSION }}'
- uses: 'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6' # v2
with:
bun-version: '${{ env.BUN_VERSION }}'
- name: 'Install dependencies'
working-directory: 'packages/desktop'
run: 'bun install --frozen-lockfile'
- name: 'Set Live Host version'
working-directory: 'packages/desktop'
run: 'bun run bump-live-host-version "${{ needs.prepare.outputs.version }}"'
- name: 'Test Live Host'
working-directory: 'packages/desktop'
run: 'bun run live-host:typecheck && bun run live-host:test'
- name: 'Import macOS certificate'
if: "${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == false }}"
shell: 'bash'
env:
APPLE_CERTIFICATE: '${{ secrets.APPLE_CERTIFICATE }}'
APPLE_CERTIFICATE_PASSWORD: '${{ secrets.APPLE_CERTIFICATE_PASSWORD }}'
KEYCHAIN_PASSWORD: '${{ secrets.APPLE_KEYCHAIN_PASSWORD }}'
run: |
set -euo pipefail
for name in APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD KEYCHAIN_PASSWORD; do
if [ -z "${!name}" ]; then echo "::error::$name is required for published Qwen Live Host releases."; exit 1; fi
done
certificate="$RUNNER_TEMP/qwen-live-host.p12"
keychain="$RUNNER_TEMP/qwen-live-host.keychain-db"
printf '%s' "$APPLE_CERTIFICATE" | base64 --decode > "$certificate"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain"
security set-keychain-settings -lut 21600 "$keychain"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain"
security import "$certificate" -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$keychain"
security list-keychains -d user -s "$keychain" login.keychain-db
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$keychain"
identity="$(security find-identity -v -p codesigning "$keychain" | sed -n 's/.*"\(Developer ID Application:.*\)"/\1/p' | head -n 1)"
if [ -z "$identity" ]; then echo '::error::Developer ID Application identity was not found.'; exit 1; fi
{
echo "CSC_NAME=$identity"
echo 'CSC_IDENTITY_AUTO_DISCOVERY=true'
} >> "$GITHUB_ENV"
- name: 'Configure notarization'
if: "${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == false }}"
shell: 'bash'
env:
APPLE_API_ISSUER: '${{ secrets.APPLE_API_ISSUER }}'
APPLE_API_KEY_ID: '${{ secrets.APPLE_API_KEY }}'
APPLE_API_KEY_P8: '${{ secrets.APPLE_API_KEY_P8 }}'
APPLE_TEAM_ID: '${{ secrets.APPLE_TEAM_ID }}'
run: |
set -euo pipefail
for name in APPLE_API_ISSUER APPLE_API_KEY_ID APPLE_API_KEY_P8 APPLE_TEAM_ID; do
if [ -z "${!name}" ]; then echo "::error::$name is required for Qwen Live Host notarization."; exit 1; fi
done
key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8"
printf '%s' "$APPLE_API_KEY_P8" > "$key_path"
{
echo "APPLE_API_KEY=$key_path"
echo "APPLE_API_KEY_ID=$APPLE_API_KEY_ID"
echo "APPLE_API_ISSUER=$APPLE_API_ISSUER"
echo "APPLE_TEAM_ID=$APPLE_TEAM_ID"
} >> "$GITHUB_ENV"
- name: 'Build packages'
working-directory: 'packages/desktop'
env:
CSC_IDENTITY_AUTO_DISCOVERY: "${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == false }}"
run: 'bun run live-host:dist:mac:no-publish'
- name: 'Verify release assets'
working-directory: 'packages/desktop/apps/live-host'
env:
RELEASE_VERSION: '${{ needs.prepare.outputs.version }}'
shell: 'bash'
run: |
set -euo pipefail
for asset in \
Qwen-Live-Host-arm64.dmg \
Qwen-Live-Host-x64.dmg \
Qwen-Live-Host-arm64.zip \
Qwen-Live-Host-x64.zip \
Qwen-Live-Host-manifest.json; do
test -f "release/$asset"
done
node --input-type=module -e '
import { createHash } from "node:crypto";
import { readFileSync, statSync } from "node:fs";
const manifest = JSON.parse(readFileSync("release/Qwen-Live-Host-manifest.json", "utf8"));
if (manifest.version !== process.env.RELEASE_VERSION) {
throw new Error("Manifest version " + manifest.version + " does not match " + process.env.RELEASE_VERSION + ".");
}
for (const architecture of ["arm64", "x64"]) {
const name = "Qwen-Live-Host-" + architecture + ".zip";
const path = "release/" + name;
const bytes = readFileSync(path);
const asset = manifest.assets?.[architecture];
if (asset?.name !== name || asset.size !== statSync(path).size || asset.sha256 !== createHash("sha256").update(bytes).digest("hex")) {
throw new Error("Manifest asset verification failed for " + architecture + ".");
}
}
'
- name: 'Verify signing and notarization'
if: "${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == false }}"
working-directory: 'packages/desktop'
shell: 'bash'
run: |
set -euo pipefail
app_count=0
while IFS= read -r -d '' app; do
app_count=$((app_count + 1))
codesign --verify --deep --strict --verbose=2 "$app"
signature="$(codesign -dv --verbose=4 "$app" 2>&1)"
if ! grep -q '^Authority=Developer ID Application:' <<<"$signature" || ! grep -qx 'TeamIdentifier=NF4574S59H' <<<"$signature"; then
echo '::error::Qwen Live Host was not signed by the expected Developer ID team.'
exit 1
fi
spctl -a -vv -t exec "$app"
xcrun stapler validate "$app"
done < <(find apps/live-host/release -mindepth 2 -maxdepth 2 -type d -name '*.app' -print0)
if [ "$app_count" -eq 0 ]; then echo '::error::No packaged Qwen Live Host application was found.'; exit 1; fi
dmg_count=0
while IFS= read -r -d '' dmg; do
dmg_count=$((dmg_count + 1))
hdiutil verify "$dmg"
done < <(find apps/live-host/release -type f -name '*.dmg' -print0)
if [ "$dmg_count" -eq 0 ]; then echo '::error::No Qwen Live Host disk image was found.'; exit 1; fi
- uses: 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' # v7.0.1
with:
name: 'qwen-live-host-macos'
path: |
packages/desktop/apps/live-host/release/*.dmg
packages/desktop/apps/live-host/release/*-manifest.json
packages/desktop/apps/live-host/release/*.zip
if-no-files-found: 'error'
retention-days: 14
publish:
name: 'Publish Qwen Live Host release'
if: "${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == false && github.repository == 'QwenLM/qwen-code' }}"
needs:
- 'prepare'
- 'build'
runs-on: 'ubuntu-latest'
timeout-minutes: 20
permissions:
contents: 'write'
env:
GH_REPO: '${{ github.repository }}'
steps:
- uses: 'actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c' # v8.0.1
with:
path: 'release-assets'
merge-multiple: true
- name: 'Generate checksums'
working-directory: 'release-assets'
run: 'sha256sum -- * > SHA256SUMS.txt'
- name: 'Create GitHub release'
id: 'release'
env:
GH_TOKEN: '${{ github.token }}'
RELEASE_TAG: '${{ needs.prepare.outputs.tag }}'
RELEASE_VERSION: '${{ needs.prepare.outputs.version }}'
RELEASE_DRAFT: '${{ inputs.draft }}'
RELEASE_PRERELEASE: '${{ inputs.prerelease }}'
RELEASE_CLOBBER: '${{ inputs.clobber }}'
run: |
set -euo pipefail
args=("$RELEASE_TAG" release-assets/* --target "$GITHUB_SHA" --title "Qwen Live Host v$RELEASE_VERSION" --generate-notes --latest=false)
if [ "$RELEASE_DRAFT" = 'true' ]; then args+=(--draft); fi
if [ "$RELEASE_PRERELEASE" = 'true' ]; then args+=(--prerelease); fi
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
if [ "$RELEASE_CLOBBER" != 'true' ]; then
echo "::error::Release $RELEASE_TAG already exists. Re-run with clobber=true to replace its assets."
exit 1
fi
gh release upload "$RELEASE_TAG" release-assets/* --clobber
edit_args=()
if [ "$RELEASE_DRAFT" = 'true' ]; then edit_args+=(--draft); else edit_args+=(--draft=false); fi
if [ "$RELEASE_PRERELEASE" = 'true' ]; then edit_args+=(--prerelease); else edit_args+=(--prerelease=false); fi
gh release edit "$RELEASE_TAG" "${edit_args[@]}"
release_url="$(gh release view "$RELEASE_TAG" --json url --jq '.url')"
else
release_url="$(gh release create "${args[@]}")"
fi
echo "url=$release_url" >> "$GITHUB_OUTPUT"
- name: 'Update stable Live Host feed'
if: '${{ inputs.draft == false && inputs.prerelease == false }}'
env:
GH_TOKEN: '${{ github.token }}'
FEED_TAG: '${{ env.LIVE_HOST_FEED_TAG }}'
run: |
set -euo pipefail
stable_assets=(
release-assets/Qwen-Live-Host-manifest.json
release-assets/Qwen-Live-Host-arm64.zip
release-assets/Qwen-Live-Host-x64.zip
)
if gh release view "$FEED_TAG" >/dev/null 2>&1; then
gh release upload "$FEED_TAG" "${stable_assets[@]}" --clobber
else
gh release create "$FEED_TAG" "${stable_assets[@]}" --title 'Qwen Live Host latest' --notes 'Stable Qwen Live Host installer feed.' --latest=false
fi
- name: 'Publish release summary'
env:
RELEASE_URL: '${{ steps.release.outputs.url }}'
RELEASE_VERSION: '${{ needs.prepare.outputs.version }}'
run: |
{
echo '## Qwen Live Host release'
echo
echo "Version: $RELEASE_VERSION"
echo "Release: $RELEASE_URL"
} >> "$GITHUB_STEP_SUMMARY"