-
Notifications
You must be signed in to change notification settings - Fork 3
220 lines (200 loc) · 9.3 KB
/
release.yml
File metadata and controls
220 lines (200 loc) · 9.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
name: Release
permissions:
contents: write
on:
push:
tags:
- 'v*' # 推送形如 v1.0.1 的 tag 时触发
jobs:
build-and-release:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup PNPM
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install deps
run: pnpm install --frozen-lockfile
- name: Preflight check for signing secrets
shell: bash
run: |
set -euo pipefail
if [[ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ]]; then
echo "::error::Missing TAURI_SIGNING_PRIVATE_KEY secret";
exit 1;
fi
if [[ -z "${TAURI_SIGNING_PRIVATE_KEY_PASSWORD:-}" ]]; then
echo "::warning::TAURI_SIGNING_PRIVATE_KEY_PASSWORD is empty (that's OK if your key has no password).";
fi
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
- name: Prepare updater endpoints (merge env & GitHub)
run: node scripts/prepare-updater-endpoints.cjs
env:
# 用户自定义的端点列表(可包含多个,用逗号分隔),来自仓库 Secrets
UPDATER_ENDPOINTS: ${{ secrets.UPDATER_ENDPOINTS != '' && format('{0},{1}', secrets.UPDATER_ENDPOINTS, env.CHANNEL_UPDATER_ENDPOINT) || env.CHANNEL_UPDATER_ENDPOINT }}
# 根据是否为预发布决定通道端点:pre 通道使用固定 tag `pre`,稳定通道使用 `latest`
CHANNEL_UPDATER_ENDPOINT: ${{ contains(github.ref_name, '-') && format('https://github.com/{0}/releases/download/pre/latest.json', github.repository) || format('https://github.com/{0}/releases/latest/download/latest.json', github.repository) }}
- name: Tauri Build & Release
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# 将端点注入到构建中(与上一步一致)。如果用户配置了 Secrets.UPDATER_ENDPOINTS,会与通道端点合并。
UPDATER_ENDPOINTS: ${{ secrets.UPDATER_ENDPOINTS != '' && format('{0},{1}', secrets.UPDATER_ENDPOINTS, env.CHANNEL_UPDATER_ENDPOINT) || env.CHANNEL_UPDATER_ENDPOINT }}
CHANNEL_UPDATER_ENDPOINT: ${{ contains(github.ref_name, '-') && format('https://github.com/{0}/releases/download/pre/latest.json', github.repository) || format('https://github.com/{0}/releases/latest/download/latest.json', github.repository) }}
# 注入前端通道变量:pre/stable
VITE_RELEASE_CHANNEL: ${{ contains(github.ref_name, '-') && 'pre' || 'stable' }}
with:
# 使用触发的 tag,避免额外创建 v__VERSION__ 的 tag/release
tagName: ${{ github.ref_name }}
releaseName: 'BPM Sniffer ${{ github.ref_name }}'
releaseDraft: false
prerelease: ${{ contains(github.ref_name, '-') }}
includeUpdaterJson: true
distPath: src-tauri/dist
args: --bundles nsis,updater
# 该 Action 会在发布中附带 latest.json(v2 插件使用),
# 如需显式开关,可按官方文档添加 includeUpdaterJson/updaterJson 选项。
# 预发布同步:按安装包进行自包含同步(而非 updater zip)
- name: 等待 Release 资产刷新
if: contains(github.ref_name, '-')
shell: bash
run: |
# GitHub Release 资产上传后存在短暂延迟,这里稍作等待避免下载步骤立即失败
sleep 20
- name: 检查资产是否已经可见
if: contains(github.ref_name, '-')
uses: actions/github-script@v7
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
let tag = context.ref_name || '';
if ((!tag || !tag.length) && context.ref && context.ref.startsWith('refs/tags/')) {
tag = context.ref.substring('refs/tags/'.length);
}
if (!tag || !tag.length) {
core.setFailed('无法解析当前构建对应的 tag 名称。');
return;
}
core.info(`正在检查 tag ${tag} 的资产...`);
const patterns = [
/latest\.json$/,
/_x64-setup.*\.exe$/,
/_x64-setup.*\.exe\.sig$/
];
for (let attempt = 1; attempt <= 10; attempt++) {
const { data: release } = await github.rest.repos.getReleaseByTag({ owner, repo, tag });
const assets = release.assets || [];
core.info(`第 ${attempt} 次轮询,共 ${assets.length} 个资产:${assets.map(a => a.name).join(', ') || '(空)'}`);
const allFound = patterns.every((pattern) => assets.some((asset) => pattern.test(asset.name)));
if (allFound) {
core.info(`Release assets ready on attempt ${attempt}`);
return;
}
if (attempt === 10) {
core.setFailed(`Assets not ready after ${attempt} attempts.`);
return;
}
core.info(`Assets missing on attempt ${attempt}, waiting 10s...`);
await new Promise((resolve) => setTimeout(resolve, 10000));
}
# 预发布:从“本次 tag 的 Release”下载 latest.json 到工作区
- name: Download latest.json from current release
if: contains(github.ref_name, '-')
uses: robinraju/release-downloader@v1
with:
repository: ${{ github.repository }}
tag: ${{ github.ref_name }}
fileName: latest.json
out-file-path: .
token: ${{ secrets.GITHUB_TOKEN }}
- name: Download NSIS installer from current release
if: contains(github.ref_name, '-')
uses: robinraju/release-downloader@v1
with:
repository: ${{ github.repository }}
tag: ${{ github.ref_name }}
fileName: "*_x64-setup*.exe*"
out-file-path: pre-assets
token: ${{ secrets.GITHUB_TOKEN }}
- name: Download NSIS installer signature from current release
if: contains(github.ref_name, '-')
uses: robinraju/release-downloader@v1
with:
repository: ${{ github.repository }}
tag: ${{ github.ref_name }}
fileName: "*_x64-setup*.exe.sig*"
out-file-path: pre-assets
token: ${{ secrets.GITHUB_TOKEN }}
- name: Repoint latest.json URLs to `pre`
if: contains(github.ref_name, '-')
shell: bash
run: |
set -euo pipefail
sed -E -i 's#/releases/download/[^/]+/#/releases/download/pre/#g' latest.json
- name: Cleanup assets on pre release
if: contains(github.ref_name, '-')
uses: actions/github-script@v7
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const tag = 'pre';
try {
const { data: release } = await github.rest.repos.getReleaseByTag({ owner, repo, tag });
if (release.assets && release.assets.length) {
for (const asset of release.assets) {
try {
await github.rest.repos.deleteReleaseAsset({ owner, repo, asset_id: asset.id });
} catch (e) {
core.warning(`delete asset failed: ${asset.name} -> ${e.message}`);
}
}
}
} catch (e) {
core.warning(`pre release not found or cannot be fetched: ${e.message}`);
}
- name: Sync pre-channel assets to tag `pre`
if: contains(github.ref_name, '-')
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: pre
prerelease: true
files: |
latest.json
pre-assets/*_x64-setup.exe
pre-assets/*_x64-setup.exe.sig
- name: 失败时回滚当前 tag Release
if: failure() && contains(github.ref_name, '-')
uses: actions/github-script@v7
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
let tag = context.ref_name || '';
if ((!tag || !tag.length) && context.ref && context.ref.startsWith('refs/tags/')) {
tag = context.ref.substring('refs/tags/'.length);
}
if (!tag || !tag.length) {
core.warning('未能解析 tag,跳过回滚');
return;
}
try {
const { data: release } = await github.rest.repos.getReleaseByTag({ owner, repo, tag });
core.info(`删除失败构建产生的 Release: ${release.name || release.tag_name}`);
await github.rest.repos.deleteRelease({ owner, repo, release_id: release.id });
} catch (err) {
core.warning(`回滚 Release 失败:${err.message}`);
}