release: v0.1.10
This commit is contained in:
parent
f761a0fa69
commit
fcdab7f575
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -1,3 +1,5 @@
|
|||||||
|
# Tauri v2 Windows 自动构建 + 签名 + 阿里云 OSS 发布
|
||||||
|
# 完整部署教程与踩坑指南见 docs/tauri-cicd-guide.md
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
|||||||
407
docs/tauri-cicd-guide.md
Normal file
407
docs/tauri-cicd-guide.md
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
# Tauri v2 Windows 应用 CI/CD 部署指南
|
||||||
|
|
||||||
|
> **方案版本**: v1.0(2026-04-02)
|
||||||
|
> **适用范围**: Tauri v2 + GitHub Actions + 阿里云 OSS + Windows 平台
|
||||||
|
> **验证状态**: 已在 dev-manager-tauri 项目实际跑通(v0.1.9 构建成功)
|
||||||
|
>
|
||||||
|
> 本文档是一套完整可复用的部署方案,涵盖从密钥生成到客户端自动更新的全链路。
|
||||||
|
> 新 Tauri 项目可直接参照本方案配置 CI/CD,避免重复踩坑。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 整体架构
|
||||||
|
|
||||||
|
```
|
||||||
|
开发者 push tag (v*)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
GitHub Actions (windows-latest)
|
||||||
|
├── 1. 构建 Tauri 应用(NSIS 安装包 + 签名)
|
||||||
|
├── 2. 创建 GitHub Release 并上传安装包
|
||||||
|
└── 3. 上传到阿里云 OSS + 生成 latest.json
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
客户端 tauri-plugin-updater
|
||||||
|
└── 定期检查 OSS 上的 latest.json → 发现新版本 → 下载 .exe → 验证签名 → 安装更新
|
||||||
|
```
|
||||||
|
|
||||||
|
**为什么用阿里云 OSS?** GitHub Releases 在国内访问慢且不稳定,OSS 提供国内 CDN 加速。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、前置准备
|
||||||
|
|
||||||
|
### 1.1 生成签名密钥
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm tauri signer generate -w "$HOME\.tauri\your-app.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
输出示例:
|
||||||
|
```
|
||||||
|
Your keypair was generated successfully
|
||||||
|
Private: C:\Users\xxx\.tauri\your-app.key
|
||||||
|
Public: C:\Users\xxx\.tauri\your-app.key.pub
|
||||||
|
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY=dW50cnVzdGVkIGNvbW1lbnQ6IH...(一长串 base64)
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD=
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Windows 注意事项**:
|
||||||
|
> - 不要用 `~` 作为路径前缀,Windows 的 PowerShell 会把 `~` 当作字面量目录名创建
|
||||||
|
> - 使用 `$HOME` 或完整路径代替
|
||||||
|
|
||||||
|
### 1.2 获取公钥 base64(用于 tauri.conf.json)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$content = Get-Content "$HOME\.tauri\your-app.key.pub" -Raw
|
||||||
|
$content = $content -replace "`r`n", "`n"
|
||||||
|
$bytes = [System.Text.Encoding]::UTF8.GetBytes($content)
|
||||||
|
[Convert]::ToBase64String($bytes)
|
||||||
|
```
|
||||||
|
|
||||||
|
将输出的 base64 字符串填入 `tauri.conf.json` 的 `plugins.updater.pubkey`。
|
||||||
|
|
||||||
|
### 1.3 创建阿里云 OSS Bucket
|
||||||
|
|
||||||
|
- 创建 Bucket(如 `appforwin`),区域按需选择(如 `oss-cn-hangzhou`)
|
||||||
|
- 设置 Bucket 为**公共读**(否则客户端无法下载更新)
|
||||||
|
- 创建一个 RAM 用户,授予 OSS 读写权限,记录 AccessKey ID 和 Secret
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、配置 tauri.conf.json
|
||||||
|
|
||||||
|
关键配置项(**每一项都不能少**):
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"createUpdaterArtifacts": true, // ⚠️ 必须!Tauri v2 需要显式开启,否则不生成签名文件
|
||||||
|
"windows": {
|
||||||
|
"nsis": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"updater": {
|
||||||
|
"pubkey": "<公钥 base64,见 1.2 步骤>",
|
||||||
|
"endpoints": [
|
||||||
|
"https://<bucket>.<endpoint>/latest.json"
|
||||||
|
],
|
||||||
|
"dialog": false // 推荐 false,用代码控制更新 UI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 踩坑记录
|
||||||
|
|
||||||
|
| 问题 | 现象 | 原因 |
|
||||||
|
|------|------|------|
|
||||||
|
| **缺少 `createUpdaterArtifacts: true`** | 构建成功但只有 `.exe`,没有 `.exe.sig` | Tauri v2 默认不生成更新包,必须显式开启 |
|
||||||
|
| **pubkey 填成了私钥** | 构建报错 `failed to decode pubkey: Base64 conversion failed` | pubkey 是公钥的 base64,不是私钥 |
|
||||||
|
| **pubkey 与私钥不匹配** | 构建成功但客户端更新时签名验证失败 | 重新生成密钥后要同步更新两处 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、配置 GitHub Secrets
|
||||||
|
|
||||||
|
在仓库 Settings → Actions → Secrets and variables → Actions 中添加:
|
||||||
|
|
||||||
|
| Secret 名称 | 值 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| `TAURI_SIGNING_PRIVATE_KEY` | `pnpm tauri signer generate` 输出的那一长串 base64 | **不是** .key 文件内容,是终端输出的 base64 值 |
|
||||||
|
| `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | 密钥密码(无密码则留空) | 密码错误会导致签名静默失败 |
|
||||||
|
| `OSS_KEY_ID` | 阿里云 AccessKey ID | |
|
||||||
|
| `OSS_KEY_SECRET` | 阿里云 AccessKey Secret | |
|
||||||
|
| `OSS_BUCKET` | Bucket 名称(如 `appforwin`) | |
|
||||||
|
| `OSS_ENDPOINT` | Endpoint 域名(如 `oss-cn-hangzhou.aliyuncs.com`) | 不带 `https://` |
|
||||||
|
|
||||||
|
### 踩坑记录
|
||||||
|
|
||||||
|
| 问题 | 现象 | 原因 |
|
||||||
|
|------|------|------|
|
||||||
|
| **私钥格式错误** | 有 `.exe` 但没有 `.exe.sig`,无报错 | 存了 .key 文件原始内容(2行),应该存 base64 编码值(1行) |
|
||||||
|
| **密钥密码不匹配** | 同上,签名静默失败 | 密码为空时 Secret 也要创建但值留空 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、配置 GitHub Actions Workflow 权限
|
||||||
|
|
||||||
|
**必须操作**(否则无法创建 Release):
|
||||||
|
|
||||||
|
1. 仓库 → Settings → **Actions → General**(不是 Secrets 页面)
|
||||||
|
2. 滚到页面最底部 → **Workflow permissions**
|
||||||
|
3. 选择 **Read and write permissions**
|
||||||
|
4. 点 Save
|
||||||
|
|
||||||
|
### 踩坑记录
|
||||||
|
|
||||||
|
| 问题 | 现象 | 原因 |
|
||||||
|
|------|------|------|
|
||||||
|
| **未改 Workflow permissions** | `Error: Resource not accessible by integration` | GITHUB_TOKEN 默认只读,无法创建 Release |
|
||||||
|
| **改了权限但 Re-run 仍失败** | 同上错误 | Re-run 复用旧 token,需要 push 新 tag 触发全新 run |
|
||||||
|
| **tauri-action 创建 Release 仍失败** | 即使设了 Read and write 仍报错 | 用 `softprops/action-gh-release@v2` 先创建 Release 作为 workaround |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、Workflow 文件模板
|
||||||
|
|
||||||
|
`.github/workflows/release.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: windows-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Rust cache
|
||||||
|
uses: swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './src-tauri -> target'
|
||||||
|
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# 验证签名密钥(提前暴露配置问题)
|
||||||
|
- name: Verify signing key
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
if ([string]::IsNullOrEmpty($env:TAURI_SIGNING_PRIVATE_KEY)) {
|
||||||
|
Write-Error "TAURI_SIGNING_PRIVATE_KEY is EMPTY!"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host "TAURI_SIGNING_PRIVATE_KEY is set"
|
||||||
|
|
||||||
|
# 先创建 Release(workaround:tauri-action 创建 Release 可能遇到权限问题)
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
|
name: 'App Name ${{ github.ref_name }}'
|
||||||
|
body: |
|
||||||
|
## 更新内容
|
||||||
|
请查看提交记录了解详情。
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
# 构建 + 上传到已有 Release
|
||||||
|
- name: Build & Upload
|
||||||
|
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 }}
|
||||||
|
with:
|
||||||
|
tagName: ${{ github.ref_name }}
|
||||||
|
releaseName: 'App Name ${{ github.ref_name }}'
|
||||||
|
releaseDraft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
# 上传到阿里云 OSS + 生成 latest.json(国内用户更新源)
|
||||||
|
- name: Upload to OSS & generate latest.json
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
OSS_KEY_ID: ${{ secrets.OSS_KEY_ID }}
|
||||||
|
OSS_KEY_SECRET: ${{ secrets.OSS_KEY_SECRET }}
|
||||||
|
OSS_BUCKET: ${{ secrets.OSS_BUCKET }}
|
||||||
|
OSS_ENDPOINT: ${{ secrets.OSS_ENDPOINT }}
|
||||||
|
run: |
|
||||||
|
# 下载 ossutil
|
||||||
|
$oss = "$env:GITHUB_WORKSPACE\ossutil64.exe"
|
||||||
|
Invoke-WebRequest -Uri "https://gosspublic.alicdn.com/ossutil/1.7.17/ossutil-v1.7.17-windows-amd64.zip" -OutFile oss.zip
|
||||||
|
Expand-Archive oss.zip -DestinationPath oss_tmp
|
||||||
|
$exeFound = Get-ChildItem oss_tmp -Filter "ossutil*.exe" -Recurse | Select-Object -First 1
|
||||||
|
Copy-Item $exeFound.FullName $oss
|
||||||
|
|
||||||
|
$tag = "${{ github.ref_name }}"
|
||||||
|
$version = $tag.TrimStart('v')
|
||||||
|
$nsisDir = "$env:GITHUB_WORKSPACE\src-tauri\target\release\bundle\nsis"
|
||||||
|
|
||||||
|
$all = Get-ChildItem $nsisDir
|
||||||
|
# Tauri v2 更新格式:.exe + .exe.sig(不是 v1 的 .nsis.zip)
|
||||||
|
$installer = $all | Where-Object { $_.Name.EndsWith('-setup.exe') } | Select-Object -First 1
|
||||||
|
$sigFile = $all | Where-Object { $_.Name.EndsWith('-setup.exe.sig') } | Select-Object -First 1
|
||||||
|
|
||||||
|
if (-not $installer) { Write-Error "installer not found!"; exit 1 }
|
||||||
|
|
||||||
|
$bucket = "oss://$env:OSS_BUCKET"
|
||||||
|
$endpoint = "https://$env:OSS_ENDPOINT"
|
||||||
|
& $oss config -e $endpoint -i $env:OSS_KEY_ID -k $env:OSS_KEY_SECRET -L CH
|
||||||
|
|
||||||
|
& $oss cp $installer.FullName "$bucket/releases/$tag/$($installer.Name)" -f
|
||||||
|
|
||||||
|
if ($sigFile) {
|
||||||
|
& $oss cp $sigFile.FullName "$bucket/releases/$tag/$($sigFile.Name)" -f
|
||||||
|
|
||||||
|
$date = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')
|
||||||
|
$baseUrl = "https://$env:OSS_BUCKET.$env:OSS_ENDPOINT/releases/$tag"
|
||||||
|
$sig = (Get-Content $sigFile.FullName -Raw).Trim()
|
||||||
|
|
||||||
|
$json = [ordered]@{
|
||||||
|
version = $version
|
||||||
|
pub_date = $date
|
||||||
|
notes = "版本 $tag 已发布"
|
||||||
|
platforms = [ordered]@{
|
||||||
|
'windows-x86_64' = [ordered]@{
|
||||||
|
signature = $sig
|
||||||
|
url = "$baseUrl/$($installer.Name)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} | ConvertTo-Json -Depth 5
|
||||||
|
|
||||||
|
$json | Out-File -FilePath latest.json -Encoding utf8NoBOM
|
||||||
|
& $oss cp latest.json "$bucket/latest.json" -f
|
||||||
|
Write-Host "latest.json uploaded"
|
||||||
|
} else {
|
||||||
|
Write-Warning "sigFile not found - skipping updater upload"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、Tauri v2 更新包格式变化(vs v1)
|
||||||
|
|
||||||
|
这是最容易踩坑的地方:
|
||||||
|
|
||||||
|
| | Tauri v1 | Tauri v2 |
|
||||||
|
|---|---|---|
|
||||||
|
| **更新包格式** | `.nsis.zip` + `.nsis.zip.sig` | `.exe` + `.exe.sig` |
|
||||||
|
| **生成条件** | 设置 `TAURI_PRIVATE_KEY` | 设置 `TAURI_SIGNING_PRIVATE_KEY` **+** `createUpdaterArtifacts: true` |
|
||||||
|
| **latest.json url** | 指向 `.nsis.zip` | 指向 `-setup.exe` |
|
||||||
|
| **签名静默失败** | 会报错 | **不报错**,只是不生成 `.sig` 文件 |
|
||||||
|
|
||||||
|
> **关键**:Tauri v2 签名失败时**完全静默**——构建照常成功,只是少了 `.sig` 文件。
|
||||||
|
> 排查方法:检查 `bundle/nsis/` 目录下是否有 `.exe.sig` 文件。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、发版流程(日常操作)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 更新版本号
|
||||||
|
# 编辑 src-tauri/tauri.conf.json 中的 "version" 字段
|
||||||
|
# 编辑 src-tauri/Cargo.toml 中的 version(保持一致)
|
||||||
|
|
||||||
|
# 2. 提交代码
|
||||||
|
git add -A
|
||||||
|
git commit -m "release: v0.2.0"
|
||||||
|
|
||||||
|
# 3. 打 tag 并推送
|
||||||
|
git tag v0.2.0
|
||||||
|
git push
|
||||||
|
git push --tags
|
||||||
|
|
||||||
|
# 4. 等待 GitHub Actions 自动完成:
|
||||||
|
# - 构建 → 签名 → 创建 Release → 上传 OSS → 生成 latest.json
|
||||||
|
# 约 10 分钟完成
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、客户端更新检测(前端代码)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { check } from '@tauri-apps/plugin-updater'
|
||||||
|
import { relaunch } from '@tauri-apps/plugin-process'
|
||||||
|
|
||||||
|
async function checkForUpdate() {
|
||||||
|
const update = await check()
|
||||||
|
if (update) {
|
||||||
|
console.log(`发现新版本: ${update.version}`)
|
||||||
|
// 下载并安装
|
||||||
|
await update.downloadAndInstall()
|
||||||
|
// 重启应用
|
||||||
|
await relaunch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
需要在 `src-tauri/src/lib.rs` 中注册插件:
|
||||||
|
```rust
|
||||||
|
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||||
|
.plugin(tauri_plugin_process::init())
|
||||||
|
```
|
||||||
|
|
||||||
|
Cargo.toml 依赖:
|
||||||
|
```toml
|
||||||
|
tauri-plugin-updater = "2"
|
||||||
|
tauri-plugin-process = "2"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、完整踩坑清单速查
|
||||||
|
|
||||||
|
| # | 问题 | 解决方案 |
|
||||||
|
|---|------|---------|
|
||||||
|
| 1 | `createUpdaterArtifacts` 未设置 | `tauri.conf.json` → `bundle.createUpdaterArtifacts: true` |
|
||||||
|
| 2 | 私钥格式错误(存了原始内容而非 base64) | GitHub Secret 存 `tauri signer generate` 输出的 base64 值 |
|
||||||
|
| 3 | pubkey 填了私钥的 base64 | pubkey 要用公钥文件的 base64 编码 |
|
||||||
|
| 4 | pubkey 与私钥不配对 | 重新生成后两处都要更新 |
|
||||||
|
| 5 | `Resource not accessible by integration` | Settings → Actions → General → Workflow permissions → Read and write |
|
||||||
|
| 6 | 改了权限 Re-run 仍失败 | push 新 tag 触发全新 workflow run |
|
||||||
|
| 7 | tauri-action 创建 Release 权限问题 | 用 `softprops/action-gh-release@v2` 预创建 |
|
||||||
|
| 8 | ossutil 路径找不到 | 下载 zip → 解压 → 用绝对路径调用 |
|
||||||
|
| 9 | PowerShell `-Filter *.nsis.zip` 不匹配多后缀 | 用 `Where-Object { $_.Name.EndsWith(...) }` |
|
||||||
|
| 10 | Tauri v2 找 `.nsis.zip` 找不到 | v2 格式是 `.exe` + `.exe.sig`,不是 `.nsis.zip` |
|
||||||
|
| 11 | Windows 上 `~` 路径问题 | 用 `$HOME` 代替 `~` |
|
||||||
|
| 12 | PowerShell `&&` 语法错误 | PowerShell 5 不支持 `&&`,用 `;` 或分开执行 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、OSS 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
<bucket>/
|
||||||
|
├── latest.json ← 客户端检查更新的入口
|
||||||
|
└── releases/
|
||||||
|
├── v0.1.0/
|
||||||
|
│ ├── app_0.1.0_x64-setup.exe
|
||||||
|
│ └── app_0.1.0_x64-setup.exe.sig
|
||||||
|
├── v0.2.0/
|
||||||
|
│ ├── app_0.2.0_x64-setup.exe
|
||||||
|
│ └── app_0.2.0_x64-setup.exe.sig
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
`latest.json` 示例:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"pub_date": "2026-04-02T04:28:01Z",
|
||||||
|
"notes": "版本 v0.2.0 已发布",
|
||||||
|
"platforms": {
|
||||||
|
"windows-x86_64": {
|
||||||
|
"signature": "<.exe.sig 文件内容>",
|
||||||
|
"url": "https://<bucket>.<endpoint>/releases/v0.2.0/app_0.2.0_x64-setup.exe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "dev-manager-tauri",
|
"name": "dev-manager-tauri",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@ -825,7 +825,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dev-manager-tauri"
|
name = "dev-manager-tauri"
|
||||||
version = "0.1.0"
|
version = "0.1.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dev-manager-tauri"
|
name = "dev-manager-tauri"
|
||||||
version = "0.1.0"
|
version = "0.1.10"
|
||||||
description = "A Tauri App"
|
description = "A Tauri App"
|
||||||
authors = ["you"]
|
authors = ["you"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// CI/CD 配置管理与 Workflow 生成
|
||||||
|
// 完整部署教程与踩坑指南见 docs/tauri-cicd-guide.md
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::models::{CicdConfig, WorkflowResult};
|
use crate::models::{CicdConfig, WorkflowResult};
|
||||||
use rusqlite::params;
|
use rusqlite::params;
|
||||||
@ -161,7 +163,9 @@ fn build_tauri_oss_workflow(cfg: &CicdConfig) -> (String, String, Vec<String>) {
|
|||||||
.unwrap_or("oss-cn-hangzhou.aliyuncs.com");
|
.unwrap_or("oss-cn-hangzhou.aliyuncs.com");
|
||||||
|
|
||||||
let content = format!(
|
let content = format!(
|
||||||
r#"name: Release
|
r#"# Tauri v2 Windows 自动构建 + 签名 + 阿里云 OSS 发布
|
||||||
|
# 完整部署教程与踩坑指南见 docs/tauri-cicd-guide.md
|
||||||
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@ -171,6 +175,8 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -187,11 +193,40 @@ jobs:
|
|||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Rust cache
|
||||||
|
uses: swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './src-tauri -> target'
|
||||||
|
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build Tauri App
|
- name: Verify signing key
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{{{ secrets.TAURI_SIGNING_PRIVATE_KEY }}}}
|
||||||
|
run: |
|
||||||
|
if ([string]::IsNullOrEmpty($env:TAURI_SIGNING_PRIVATE_KEY)) {{
|
||||||
|
Write-Error "TAURI_SIGNING_PRIVATE_KEY is EMPTY - update bundles will not be generated!"
|
||||||
|
exit 1
|
||||||
|
}}
|
||||||
|
$lines = $env:TAURI_SIGNING_PRIVATE_KEY.Split("`n").Count
|
||||||
|
Write-Host "TAURI_SIGNING_PRIVATE_KEY is set ($lines lines)"
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{{{ github.ref_name }}}}
|
||||||
|
name: ${{{{ github.ref_name }}}}
|
||||||
|
body: |
|
||||||
|
## 更新内容
|
||||||
|
请查看提交记录了解详情。
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Build & Release
|
||||||
uses: tauri-apps/tauri-action@v0
|
uses: tauri-apps/tauri-action@v0
|
||||||
env:
|
env:
|
||||||
|
GITHUB_TOKEN: ${{{{ secrets.GITHUB_TOKEN }}}}
|
||||||
TAURI_SIGNING_PRIVATE_KEY: ${{{{ secrets.TAURI_SIGNING_PRIVATE_KEY }}}}
|
TAURI_SIGNING_PRIVATE_KEY: ${{{{ secrets.TAURI_SIGNING_PRIVATE_KEY }}}}
|
||||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{{{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}}}
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{{{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}}}
|
||||||
with:
|
with:
|
||||||
@ -199,52 +234,73 @@ jobs:
|
|||||||
releaseName: ${{{{ github.ref_name }}}}
|
releaseName: ${{{{ github.ref_name }}}}
|
||||||
releaseBody: |
|
releaseBody: |
|
||||||
## 更新内容
|
## 更新内容
|
||||||
请查看 CHANGELOG 了解详情。
|
请查看提交记录了解详情。
|
||||||
releaseDraft: false
|
releaseDraft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
args: --verbose
|
||||||
|
|
||||||
- name: Upload installer to 阿里云 OSS
|
- name: Upload to OSS & generate latest.json
|
||||||
uses: fangbinwei/aliyun-oss-website-action@v1
|
|
||||||
with:
|
|
||||||
accessKeyId: ${{{{ secrets.OSS_KEY_ID }}}}
|
|
||||||
accessKeySecret: ${{{{ secrets.OSS_KEY_SECRET }}}}
|
|
||||||
bucket: ${{{{ secrets.OSS_BUCKET }}}}
|
|
||||||
endpoint: {endpoint}
|
|
||||||
folder: releases/${{{{ github.ref_name }}}}/
|
|
||||||
include: |
|
|
||||||
src-tauri/target/release/bundle/**/*.exe
|
|
||||||
src-tauri/target/release/bundle/**/*.msi
|
|
||||||
|
|
||||||
- name: Generate and upload latest.json
|
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
env:
|
env:
|
||||||
OSS_BUCKET: ${{{{ secrets.OSS_BUCKET }}}}
|
|
||||||
OSS_KEY_ID: ${{{{ secrets.OSS_KEY_ID }}}}
|
OSS_KEY_ID: ${{{{ secrets.OSS_KEY_ID }}}}
|
||||||
OSS_KEY_SECRET: ${{{{ secrets.OSS_KEY_SECRET }}}}
|
OSS_KEY_SECRET: ${{{{ secrets.OSS_KEY_SECRET }}}}
|
||||||
|
OSS_BUCKET: ${{{{ secrets.OSS_BUCKET }}}}
|
||||||
|
OSS_ENDPOINT: ${{{{ secrets.OSS_ENDPOINT }}}}
|
||||||
run: |
|
run: |
|
||||||
|
# 下载 ossutil
|
||||||
|
$oss = "$env:GITHUB_WORKSPACE\ossutil64.exe"
|
||||||
|
Invoke-WebRequest -Uri "https://gosspublic.alicdn.com/ossutil/1.7.17/ossutil-v1.7.17-windows-amd64.zip" -OutFile oss.zip
|
||||||
|
Expand-Archive oss.zip -DestinationPath oss_tmp
|
||||||
|
$exeFound = Get-ChildItem oss_tmp -Filter "ossutil*.exe" -Recurse | Select-Object -First 1
|
||||||
|
Copy-Item $exeFound.FullName $oss
|
||||||
|
|
||||||
$tag = "${{{{ github.ref_name }}}}"
|
$tag = "${{{{ github.ref_name }}}}"
|
||||||
|
$version = $tag.TrimStart('v')
|
||||||
|
$nsisDir = "$env:GITHUB_WORKSPACE\src-tauri\target\release\bundle\nsis"
|
||||||
|
|
||||||
|
Write-Host "=== bundle dir ==="
|
||||||
|
Get-ChildItem "$env:GITHUB_WORKSPACE\src-tauri\target\release\bundle" -Recurse | ForEach-Object {{ Write-Host $_.FullName }}
|
||||||
|
|
||||||
|
$all = Get-ChildItem $nsisDir
|
||||||
|
$installer = $all | Where-Object {{ $_.Name.EndsWith('-setup.exe') }} | Select-Object -First 1
|
||||||
|
$sigFile = $all | Where-Object {{ $_.Name.EndsWith('-setup.exe.sig') }} | Select-Object -First 1
|
||||||
|
|
||||||
|
if (-not $installer) {{ Write-Error "installer not found!"; exit 1 }}
|
||||||
|
|
||||||
|
$bucket = "oss://$env:OSS_BUCKET"
|
||||||
|
$endpoint = "https://$env:OSS_ENDPOINT"
|
||||||
|
|
||||||
|
& $oss config -e $endpoint -i $env:OSS_KEY_ID -k $env:OSS_KEY_SECRET -L CH
|
||||||
|
& $oss cp $installer.FullName "$bucket/releases/$tag/$($installer.Name)" -f
|
||||||
|
|
||||||
|
if ($sigFile) {{
|
||||||
|
& $oss cp $sigFile.FullName "$bucket/releases/$tag/$($sigFile.Name)" -f
|
||||||
|
|
||||||
$date = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')
|
$date = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')
|
||||||
$url = "https://$env:OSS_BUCKET.{endpoint}/releases/$tag/setup.exe"
|
$baseUrl = "https://$env:OSS_BUCKET.{endpoint}/releases/$tag"
|
||||||
$sig = Get-Content -Path (Get-ChildItem 'src-tauri/target/release/bundle' -Filter '*.exe.sig' -Recurse | Select-Object -First 1 -ExpandProperty FullName) -Raw
|
$sig = (Get-Content $sigFile.FullName -Raw).Trim()
|
||||||
|
|
||||||
$json = [ordered]@{{
|
$json = [ordered]@{{
|
||||||
version = $tag.TrimStart('v')
|
version = $version
|
||||||
pub_date = $date
|
pub_date = $date
|
||||||
notes = "版本 $tag"
|
notes = "版本 $tag 已发布"
|
||||||
platforms = [ordered]@{{
|
platforms = [ordered]@{{
|
||||||
'windows-x86_64' = [ordered]@{{ url = $url; signature = $sig.Trim() }}
|
'windows-x86_64' = [ordered]@{{
|
||||||
|
signature = $sig
|
||||||
|
url = "$baseUrl/$($installer.Name)"
|
||||||
|
}}
|
||||||
}}
|
}}
|
||||||
}} | ConvertTo-Json -Depth 5
|
}} | ConvertTo-Json -Depth 5
|
||||||
$json | Out-File -FilePath latest.json -Encoding utf8NoBOM
|
|
||||||
|
|
||||||
- name: Upload latest.json to OSS
|
$json | Out-File -FilePath latest.json -Encoding utf8NoBOM
|
||||||
uses: fangbinwei/aliyun-oss-website-action@v1
|
Write-Host "=== latest.json ==="
|
||||||
with:
|
Get-Content latest.json
|
||||||
accessKeyId: ${{{{ secrets.OSS_KEY_ID }}}}
|
|
||||||
accessKeySecret: ${{{{ secrets.OSS_KEY_SECRET }}}}
|
& $oss cp latest.json "$bucket/latest.json" -f
|
||||||
bucket: ${{{{ secrets.OSS_BUCKET }}}}
|
Write-Host "latest.json uploaded"
|
||||||
endpoint: {endpoint}
|
}} else {{
|
||||||
folder: ''
|
Write-Warning "sigFile (.exe.sig) not found - skipping updater upload"
|
||||||
include: latest.json
|
}}
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -254,6 +310,7 @@ jobs:
|
|||||||
"OSS_KEY_ID".to_string(),
|
"OSS_KEY_ID".to_string(),
|
||||||
"OSS_KEY_SECRET".to_string(),
|
"OSS_KEY_SECRET".to_string(),
|
||||||
"OSS_BUCKET".to_string(),
|
"OSS_BUCKET".to_string(),
|
||||||
|
"OSS_ENDPOINT".to_string(),
|
||||||
];
|
];
|
||||||
(content, "release.yml".to_string(), secrets)
|
(content, "release.yml".to_string(), secrets)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "dev-manager-tauri",
|
"productName": "dev-manager-tauri",
|
||||||
"version": "0.1.0",
|
"version": "0.1.10",
|
||||||
"identifier": "com.yangzhixiang.dev-manager-tauri",
|
"identifier": "com.yangzhixiang.dev-manager-tauri",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
// CI/CD 部署配置与运行管理
|
||||||
|
// 完整部署教程与踩坑指南见 docs/tauri-cicd-guide.md
|
||||||
|
// 涵盖:签名密钥、GitHub Secrets、Workflow 模板、OSS 上传、Tauri v2 更新格式等
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@ -175,7 +175,7 @@ function WslSyncCard({ dbPath }: { dbPath: string }) {
|
|||||||
} finally { setLoading(false); }
|
} finally { setLoading(false); }
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => { refreshStatus(); }, []);
|
// 不再自动刷新 — WSL 状态检测较慢,由用户手动点击「刷新状态」触发
|
||||||
|
|
||||||
const handleSync = async () => {
|
const handleSync = async () => {
|
||||||
setSyncing(true);
|
setSyncing(true);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user