# 发版清单（桌面端 Release）

打 `vX.Y.Z` 标签即触发 [`.github/workflows/release.yml`](.github/workflows/release.yml)：
GitHub 用 macOS/Windows runner `wails3` 出包并发布到 **Releases**（mac 出 universal `.app` 的 zip、Windows 出 `.exe`）；
旧版用户的 App 启动时查 `releases/latest`，发现新版即弹横幅提示下载
（逻辑见 [`version.ts`](sundynix-desktop/frontend/src/lib/version.ts)）。

> 版本号一律用语义化 `主.次.补`（如 `0.1.1`）；git tag 带 `v` 前缀（`v0.1.1`），App 内不带。

---

## 一、发版前：改版本号（必须三处对齐）

| 文件 | 字段 | 作用 |
|---|---|---|
| `sundynix-desktop/frontend/src/lib/version.ts` | `APP_VERSION` | **功能性**：App 拿它和 GitHub 最新版比，决定是否提示更新 |
| `sundynix-desktop/frontend/package.json` | `version` | 前端包版本（保持一致，便于追溯） |
| `sundynix-desktop/build/config.yml` | `info.version` | 原生包元数据版本（macOS `Info.plist` 的 `CFBundleShortVersionString` 由它生成） |

> ⚠️ 改完 `build/config.yml` 必须跑 `wails3 task common:update:build-assets` 重新生成
> `build/darwin/Info.plist` 等资产，**光改 config.yml 不生效**——生成物是提交进仓库的。
> （这坑踩过：config.yml 填好了但没重新生成，`.app` 里还是脚手架模板值，直接起不来。）

⚠️ **`APP_VERSION` 必须等于即将打的 tag（去掉 v）**，否则更新提示会错乱：
新包内嵌的 APP_VERSION = 新版本；旧用户 App 内是旧版本，比对才会提示。

## 二、发版步骤

```bash
# 1. 改完上面三处版本号，提交
git add -A && git commit -m "release: v0.1.1"

# 2. 先 push 代码（release 从被打标签的提交构建；同时触发 CI 回归）
git push origin main          # 或当前分支 dev

# 3. 打标签 + push 标签 → 触发 release 构建
git tag -a v0.1.1 -m "v0.1.1：<一句话变更>"
git push origin v0.1.1
```

## 三、验证

1. GitHub → **Actions** → 看 `Release` 工作流 macOS / Windows 两个 job 是否绿。
2. GitHub → **Releases** → 确认 `v0.1.1` 下有两个资产：
   - `sundynix_desktop_macos_universal.zip`
   - `sundynix_desktop_windows_amd64.exe`
3. 用一台装了**旧版**的机器打开 App → 顶部应出现「新版本 v0.1.1 可用」横幅。

## 四、出错回滚 / 重发

```bash
# 删标签（本地 + 远程），改完重打
git tag -d v0.1.1
git push origin :refs/tags/v0.1.1
# 修复后重新执行「二、发版步骤」
```

## 五、注意事项

- **仓库需 public**：Release 资产要让终端用户直接下载；private 仓库的资产下载需 token，不适合分发。
- **代码签名未做**（待办）：
  - macOS 未公证 → 用户首次打开被 Gatekeeper 拦，需「右键 → 打开」或系统设置放行。正式分发需 Apple 开发者证书 + `notarize`。
  - Windows 未签名 → SmartScreen 警告。需代码签名证书。
- **GitHub API 限流**：更新检查未认证 60 次/小时/IP，按"每用户启动查一次"完全够用。
- CI（`ci.yml`）在 push 到 `main`/`dev` 或 PR 时自动跑回归；Release 仅在 push `v*` 标签时触发。
