Files
pangolin/docs/superpowers/specs/2026-07-05-cicd-design.md
T
wangjia 7c26050cdb
Deploy Site / deploy-site (push) Has been cancelled
docs(spec): 官网部署改 Cloudflare Pages(架构变更说明)
节点 :443 被 VPN 占用 + CF 免费套餐改回源端口需 Enterprise → 官网改 CF Pages 托管
(纯静态/全程 HTTPS/CSP 生效/无 :443 冲突),已上线 pangolin.yanmeiai.com。
deploy-site.sh 用 wrangler,需 CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 10:08:20 +08:00

8.9 KiB

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(wrangler → CF Pages)/ deploy-server.sh(ssh pangolin1,复用 lib-ssh)
  • 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 serverrelease-server.shdeploy-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)—— Cloudflare Pages

架构变更(2026-07-06 实施): 原计划 rsync 到 pangolin1 的 nginx。但节点 :443 被 sing-box (VPN 数据面)占用,而 CF 免费套餐 proxied 回源只能打 :80/:443、改回源端口需 Enterprise —— 无法在同机同 IP 上让官网 HTTPS 与 VPN 共存。故官网改由 Cloudflare Pages 托管:纯静态、 全程 HTTPS、_headers/CSP 原生生效、不落 VPS,从根上无 :443 冲突,也不拖累 VPN 机器。

Astro npm ci && npm run build(SITE_URL=https://pangolin.yanmeiai.com)→ dist/npx wrangler pages deploy 发布到 CF Pages 项目 pangolin-site(自定义域 pangolin.yanmeiai.com,CNAME → pangolin-site.pages.dev,proxied)。 需 secret:CLOUDFLARE_API_TOKEN(带 Account>Pages>Edit)+ CLOUDFLARE_ACCOUNT_ID(账户级)。 deploy 步骤在 node:20 容器内跑 wrangler。灾备:构建产物仍是纯静态,可另 rsync 到任意镜像。

5. 下载链接闭环(30A)

web/website/src/config/site.tsdownloads: { 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,命名对齐 jiu 以共用)

Secret 作用域 说明
FORGEJO_TOKEN / FORGEJO_URL 账户级(wangjia) 建 release + 传产物,jiu 复用
MACOS_DEVELOPER_ID_CERT_P12_BASE64 / MACOS_DEVELOPER_ID_CERT_PASSWORD 账户级 Developer ID 证书(账号级),与 jiu 共用;续期改一处。证书在钥匙串,导出一次 .p12
APPSTORE_API_KEY_P8_BASE64 / APPSTORE_API_KEY_ID / APPSTORE_API_ISSUER_ID 账户级 公证 API key(KEY_ID=3PZTHR8YMJ),与 jiu 同一把,.p8 现成
DEPLOY_SSH_KEY pangolin 仓库级 授权到 pangolin1,最小权限
ANDROID_KEYSTORE_BASE64 / ANDROID_KEYSTORE_PASSWORD / ANDROID_KEY_ALIAS / ANDROID_KEY_PASSWORD pangolin 仓库级 Android app 级专属签名(pangolin 自己的 keystore,不复用 jiu)
MACOS_APP_PROVISION_PROFILE_BASE64 / MACOS_SYSEXT_PROVISION_PROFILE_BASE64 pangolin 仓库级 主 app + PacketTunnel sysext 描述文件(pangolin bundle 专属,签名期落盘嵌入)

命名对齐 jiu(MACOS_*/APPSTORE_*/ANDROID_*):Apple 那套放账户级 → jiu/pangolin 共用一份, compile-macos 脚本可复用 jiu 的;Android keystore 虽同命名规范但各 app 独立、不共享。 工作流用 secrets.XXX 引用,作用域对写法透明。

7. 实现顺序(单仓库内分阶段落地)

范围虽是 A~F,实现按风险/依赖递增:

  1. A 基座 + checks 迁移(ci.ymlchecks.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 代码签名(先不签)。