docs(spec): CI/CD 全流程设计方案(#30)
范围 A~F(官网+服务端+Android/macOS/Windows,排除 iOS/#26/#25);runner 混合 (nas/mac/windows);tag 触发;镜像 jiu 的 scripts/ci+workflows 结构;密钥作用域 (Apple/token 账户级、部署key/keystore 仓库级);服务端部署固化 F3/F4 手动次序带回滚。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# Pangolin CI/CD 全流程 —— 设计方案(#30)
|
||||
|
||||
> 状态:设计定稿待审 · 日期 2026-07-05 · 范围 A~F(排除 iOS、备份#26、TLS#25)
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
pangolin 现有 CI 仅 `.gitea/workflows/ci.yml`(nas,只校验无部署)+ `web/website/.gitea/workflows/website.yml`。
|
||||
服务端部署靠手动(F3/F4 那次我手动 scp+ssh+migrate),客户端出包靠本地脚本,官网未部署,
|
||||
下载链接是死链。目标:**tag 触发的编译 → 测试 → 发版(Gitea release)→ 部署** 全自动,
|
||||
参考 jiu 的 `.gitea/workflows` + `scripts/ci/*.sh` 结构,适配 pangolin 的部署目标与产物。
|
||||
|
||||
## 2. 范围
|
||||
|
||||
| 子块 | 内容 |
|
||||
|---|---|
|
||||
| A 基座 | `scripts/ci/*`(env/provision/test/release/notify/lib-forgejo)+ checks 保留 |
|
||||
| B 官网 | Astro 构建 → 部署 `pangolin.yanmeiai.com` |
|
||||
| C 服务端 | 交叉编译 server/agent/migrate → release → ssh pangolin1(备份→migrate→换二进制→重启→健康检查) |
|
||||
| D Android | apk(arm64,release keystore 签名)→ release 资产 |
|
||||
| E macOS | 公证 dmg(Developer ID + notarytool)→ release 资产 |
|
||||
| F Windows | exe/installer(Inno Setup)→ release 资产 |
|
||||
|
||||
**排除**:iOS(G,未来)、SQLite 备份/容灾(#26)、控制面 TLS(#25)。
|
||||
|
||||
## 3. 已锁定决策
|
||||
|
||||
| 维度 | 决定 | 理由 |
|
||||
|---|---|---|
|
||||
| runner | nas=官网+服务端(容器化)· mac=Android+macOS · windows=Windows | nas 常在线且 Astro/Go 轻量(非 Flutter Web);mac/windows 做必须它们的活 |
|
||||
| 触发 | tag `site-v*` / `server-v*` / `client-v*` + `manual.yml` 手动派发 | 同 jiu,发版即部署,可手动重放 |
|
||||
| macOS 签名 | mac runner 自动 Developer ID 签名 + notarytool 公证 + staple | 凭据入 Gitea secret(见 §7) |
|
||||
| Android 签名 | 正式 release keystore | app 级专属签名身份 |
|
||||
| 下载链接 | 官网 href 指向 Gitea release 资产的稳定 URL | 发版即更新,见 §6 |
|
||||
| 镜像 | GOPROXY=goproxy.cn、PUB_HOSTED_URL/FLUTTER_STORAGE_BASE_URL=flutter-io.cn | 国内网络 |
|
||||
|
||||
## 4. 架构
|
||||
|
||||
### 4.1 共享基座 `scripts/ci/`(镜像 jiu)
|
||||
|
||||
- `_env.sh` —— 公共环境(镜像源、路径、版本号解析 `${tag#prefix-v}`)
|
||||
- `lib-forgejo.sh` —— Gitea/Forgejo release 建/查 + 资产上传(用 `FORGEJO_TOKEN`)
|
||||
- `provision-mac.sh` —— mac 幂等装 flutter / xcode-select / gomobile / Android NDK+JDK17
|
||||
- `test.sh <server|client>` —— `go test` / `flutter test`
|
||||
- `notify.sh` —— 成功/失败 Telegram 通知(可选,复用节点监控 bot)
|
||||
- `compile-site.sh` / `compile-backend.sh` / `compile-android.sh` / `compile-macos.sh` / `compile-windows.sh`
|
||||
- `deploy-site.sh` / `deploy-server.sh`(ssh pangolin1)
|
||||
- `release-<x>.sh` —— 建 release + 挂产物
|
||||
|
||||
> 每个 `compile-*` 内部封装该端已验证的构建命令(如 Android 走
|
||||
> `scripts/build-libbox.sh android` + `flutter build apk --split-per-abi`;macOS 走
|
||||
> Xcode Developer ID 签名 + `notarytool submit --wait` + `stapler`)。工作流只调脚本,
|
||||
> 逻辑在脚本里,便于本地复现。
|
||||
|
||||
### 4.2 工作流 `.gitea/workflows/`
|
||||
|
||||
| 工作流 | 触发 | runner | 步骤 |
|
||||
|---|---|---|---|
|
||||
| `checks.yml`(现 ci.yml) | push 分支 | nas | 保留:shellcheck / openapi / redline / flutter analyze+test / go test |
|
||||
| `deploy-site.yml` | `site-v*` | nas | `node:20` 容器构建 Astro(`SITE_URL` 注入)→ `deploy-site.sh` |
|
||||
| `deploy-server.yml` | `server-v*` | nas | `golang:1.25` 交叉编译 → `test.sh server` → `release-server.sh` → `deploy-server.sh` |
|
||||
| `build-android.yml` | `client-v*` | mac | provision → `compile-android.sh`(签名 apk)→ `release-client.sh` |
|
||||
| `build-macos.yml` | `client-v*` | mac | provision → `compile-macos.sh`(签名+公证 dmg)→ `release-client.sh` |
|
||||
| `build-windows.yml` | `client-v*` / `winbuild*` | windows | `compile-windows.sh`(exe/installer)→ `release-client.sh` |
|
||||
|
||||
并发组按 jiu:`deploy-site` / `deploy-server` / `deploy-client` 各自 `cancel-in-progress: false`。
|
||||
|
||||
### 4.3 服务端部署(deploy-server.sh)—— 把手动那套固化
|
||||
|
||||
复刻 F3/F4 手动部署的安全次序(带回滚):
|
||||
1. scp `pangolin-server` / `pangolin-agent` / `pangolin-migrate` 到 pangolin1 `/tmp`
|
||||
2. `systemctl stop pangolin-server`
|
||||
3. `sqlite3 wal_checkpoint(TRUNCATE)` → `cp` 备份 `pangolin.db.bak-pre-<tag>`
|
||||
4. `pangolin-migrate up`(以 pangolin 用户);**失败即恢复备份 + 重启旧 server + 退出非零**
|
||||
5. `install` 新二进制到 `/usr/local/bin`(旧的备份为 `.bak-<tag>`)
|
||||
6. `systemctl start pangolin-server` + `/healthz` 健康检查;agent 随连接自恢复
|
||||
|
||||
### 4.4 官网部署(deploy-site.sh)
|
||||
|
||||
Astro `npm ci && npm run build`(`SITE_URL=https://pangolin.yanmeiai.com`)→ `dist/` rsync 到
|
||||
服务器 web 根 → 由 nginx/Caddy 静态托管 `pangolin.yanmeiai.com`。**DNS**:CF 加 A/CNAME
|
||||
`pangolin.yanmeiai.com` → 服务器(首次部署时做,记 baize)。
|
||||
|
||||
## 5. 下载链接闭环(30A)
|
||||
|
||||
`web/website/src/config/site.ts` 增 `downloads: { android, macos, windows }`,值为 Gitea release 的
|
||||
**稳定 latest 资产 URL**(Forgejo 支持 `…/releases/latest/download/<asset>` 则直接用;
|
||||
不支持则 `deploy-site.sh` 构建期用 `FORGEJO_TOKEN` 查最新 `client-v*` release 版本、烘焙进 href)。
|
||||
`Download.astro` 各平台按钮读 `SITE.downloads.<platform>`。客户端发版后官网重部署即刷新
|
||||
(或 `build-*` 完成触发 `deploy-site`)。
|
||||
|
||||
## 6. 密钥与作用域(solo / wangjia)
|
||||
|
||||
| Secret | 作用域 | 说明 |
|
||||
|---|---|---|
|
||||
| `FORGEJO_TOKEN` | 账户级(wangjia) | 建 release,jiu 复用 |
|
||||
| `DEVELOPER_ID_P12` / `P12_PASSWORD` / `ASC_KEY_P8` / `ASC_KEY_ID` / `ASC_ISSUER_ID` | 账户级 | Apple 账号级签名+公证,所有 app 共用,续期改一处 |
|
||||
| `DEPLOY_SSH_KEY` | pangolin 仓库级 | 授权到 pangolin1,最小权限 |
|
||||
| `RELEASE_KEYSTORE` / `KEYSTORE_PASSWORD` / `KEY_ALIAS` / `KEY_PASSWORD` | pangolin 仓库级 | Android app 级专属签名,绝不共享 |
|
||||
|
||||
工作流用 `secrets.XXX` 引用,账户级/仓库级对写法透明。macOS 另需主 app + PacketTunnel sysext
|
||||
两个 `.provisionprofile`(base64 存 secret,签名期落盘嵌入)。
|
||||
|
||||
## 7. 实现顺序(单仓库内分阶段落地)
|
||||
|
||||
范围虽是 A~F,实现按风险/依赖递增:
|
||||
1. **A 基座** + `checks` 迁移(`ci.yml` → `checks.yml` 复用现有,抽 `scripts/ci` 骨架)
|
||||
2. **B 官网**(最简,验证 release/deploy 骨架跑通)
|
||||
3. **C 服务端**(固化手动部署,告别手动)
|
||||
4. **D Android**(解锁下载链接;需 keystore 就绪 + gradle 接签名)
|
||||
5. **E macOS**(最复杂:证书+2 描述文件+公证)
|
||||
6. **F Windows**(windows runner + Inno Setup)
|
||||
|
||||
每阶段独立可发、独立验收。
|
||||
|
||||
## 8. 验证
|
||||
|
||||
- 每条流水线先 `workflow_dispatch` 手动跑通、产物/部署核对,再依赖 tag。
|
||||
- 服务端:`server-v*` → 看 pangolin1 migrate 版本 + `/healthz` + 行数守恒(同 F3 部署核对)。
|
||||
- 官网:`site-v*` → `pangolin.yanmeiai.com` 可访问 + canonical 正确 + redline 扫描。
|
||||
- 客户端:release 资产可下载安装(Android 侧载 / macOS 公证校验 `spctl` / Windows 安装)。
|
||||
- 下载链接:官网按钮点击落到最新 release 资产。
|
||||
|
||||
## 9. 风险与缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|---|---|
|
||||
| nas 内存(3.8G)构建 OOM | 容器化单 job、Astro/Go 轻量;必要时该端移 mac |
|
||||
| migrate 在生产出错 | 部署前备份 + 失败自动回滚(§4.3),已在 F3/F4 手动验证 |
|
||||
| Android keystore 丢失 | 存 Bitwarden(文件+密码);终身签名身份 |
|
||||
| macOS 公证凭据泄露 | 账户级 secret,不落盘;`.p8`/`.p12` 用完即删临时文件 |
|
||||
| 客户端发版后下载链接不刷新 | `build-*` 成功触发 `deploy-site` 重烘焙,或用 latest-download 稳定 URL |
|
||||
|
||||
## 10. 不在本方案
|
||||
|
||||
iOS 流水线(G)、SQLite 备份/容灾(#26)、TLS(#25)、Android/上架 Play、Windows 代码签名(先不签)。
|
||||
Reference in New Issue
Block a user