From fcdab7f575e276671e73e5a7dbbde79b8f8f0661 Mon Sep 17 00:00:00 2001 From: lanrtop Date: Thu, 2 Apr 2026 14:19:41 +0900 Subject: [PATCH] release: v0.1.10 --- .github/workflows/release.yml | 2 + docs/tauri-cicd-guide.md | 407 +++++++++++++++++++++++++ package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/src/commands/cicd.rs | 135 +++++--- src-tauri/tauri.conf.json | 2 +- src/components/dashboard/CicdModal.tsx | 3 + src/components/proxy/ProxyPage.tsx | 2 +- 9 files changed, 513 insertions(+), 44 deletions(-) create mode 100644 docs/tauri-cicd-guide.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1d48c2e..5f1f42f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,5 @@ +# Tauri v2 Windows 自动构建 + 签名 + 阿里云 OSS 发布 +# 完整部署教程与踩坑指南见 docs/tauri-cicd-guide.md name: Release on: diff --git a/docs/tauri-cicd-guide.md b/docs/tauri-cicd-guide.md new file mode 100644 index 0000000..158e7ca --- /dev/null +++ b/docs/tauri-cicd-guide.md @@ -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://./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 目录结构 + +``` +/ +├── 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://./releases/v0.2.0/app_0.2.0_x64-setup.exe" + } + } +} +``` diff --git a/package.json b/package.json index 60d36ab..ae991ad 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dev-manager-tauri", "private": true, - "version": "0.1.0", + "version": "0.1.10", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 8b71d68..d0ed6d9 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -825,7 +825,7 @@ dependencies = [ [[package]] name = "dev-manager-tauri" -version = "0.1.0" +version = "0.1.10" dependencies = [ "base64 0.22.1", "chrono", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 1ae155b..97f0ebf 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dev-manager-tauri" -version = "0.1.0" +version = "0.1.10" description = "A Tauri App" authors = ["you"] edition = "2021" diff --git a/src-tauri/src/commands/cicd.rs b/src-tauri/src/commands/cicd.rs index 56fe352..1ddfdc8 100644 --- a/src-tauri/src/commands/cicd.rs +++ b/src-tauri/src/commands/cicd.rs @@ -1,3 +1,5 @@ +// CI/CD 配置管理与 Workflow 生成 +// 完整部署教程与踩坑指南见 docs/tauri-cicd-guide.md use crate::db; use crate::models::{CicdConfig, WorkflowResult}; use rusqlite::params; @@ -161,7 +163,9 @@ fn build_tauri_oss_workflow(cfg: &CicdConfig) -> (String, String, Vec) { .unwrap_or("oss-cn-hangzhou.aliyuncs.com"); let content = format!( - r#"name: Release + r#"# Tauri v2 Windows 自动构建 + 签名 + 阿里云 OSS 发布 +# 完整部署教程与踩坑指南见 docs/tauri-cicd-guide.md +name: Release on: push: @@ -171,6 +175,8 @@ on: jobs: release: runs-on: windows-latest + permissions: + contents: write steps: - uses: actions/checkout@v4 @@ -187,11 +193,40 @@ jobs: - 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: 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 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: @@ -199,52 +234,73 @@ jobs: releaseName: ${{{{ github.ref_name }}}} releaseBody: | ## 更新内容 - 请查看 CHANGELOG 了解详情。 + 请查看提交记录了解详情。 releaseDraft: false prerelease: false + args: --verbose - - name: Upload installer to 阿里云 OSS - 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 + - name: Upload to OSS & generate latest.json shell: pwsh env: - OSS_BUCKET: ${{{{ secrets.OSS_BUCKET }}}} 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: | - $tag = "${{{{ github.ref_name }}}}" - $date = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ') - $url = "https://$env:OSS_BUCKET.{endpoint}/releases/$tag/setup.exe" - $sig = Get-Content -Path (Get-ChildItem 'src-tauri/target/release/bundle' -Filter '*.exe.sig' -Recurse | Select-Object -First 1 -ExpandProperty FullName) -Raw - $json = [ordered]@{{ - version = $tag.TrimStart('v') - pub_date = $date - notes = "版本 $tag" - platforms = [ordered]@{{ - 'windows-x86_64' = [ordered]@{{ url = $url; signature = $sig.Trim() }} - }} - }} | ConvertTo-Json -Depth 5 - $json | Out-File -FilePath latest.json -Encoding utf8NoBOM + # 下载 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 - - name: Upload latest.json to OSS - 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: '' - include: latest.json + $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') + $baseUrl = "https://$env:OSS_BUCKET.{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 + Write-Host "=== latest.json ===" + Get-Content latest.json + + & $oss cp latest.json "$bucket/latest.json" -f + Write-Host "latest.json uploaded" + }} else {{ + Write-Warning "sigFile (.exe.sig) not found - skipping updater upload" + }} "# ); @@ -254,6 +310,7 @@ jobs: "OSS_KEY_ID".to_string(), "OSS_KEY_SECRET".to_string(), "OSS_BUCKET".to_string(), + "OSS_ENDPOINT".to_string(), ]; (content, "release.yml".to_string(), secrets) } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b590dff..bced77e 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "dev-manager-tauri", - "version": "0.1.0", + "version": "0.1.10", "identifier": "com.yangzhixiang.dev-manager-tauri", "build": { "beforeDevCommand": "pnpm dev", diff --git a/src/components/dashboard/CicdModal.tsx b/src/components/dashboard/CicdModal.tsx index 9e1340d..adf2658 100644 --- a/src/components/dashboard/CicdModal.tsx +++ b/src/components/dashboard/CicdModal.tsx @@ -1,3 +1,6 @@ +// CI/CD 部署配置与运行管理 +// 完整部署教程与踩坑指南见 docs/tauri-cicd-guide.md +// 涵盖:签名密钥、GitHub Secrets、Workflow 模板、OSS 上传、Tauri v2 更新格式等 import { useState, useEffect } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { diff --git a/src/components/proxy/ProxyPage.tsx b/src/components/proxy/ProxyPage.tsx index 4db6438..72900c4 100644 --- a/src/components/proxy/ProxyPage.tsx +++ b/src/components/proxy/ProxyPage.tsx @@ -175,7 +175,7 @@ function WslSyncCard({ dbPath }: { dbPath: string }) { } finally { setLoading(false); } }; - useEffect(() => { refreshStatus(); }, []); + // 不再自动刷新 — WSL 状态检测较慢,由用户手动点击「刷新状态」触发 const handleSync = async () => { setSyncing(true);