7c26050cdb
Deploy Site / deploy-site (push) Has been cancelled
节点 :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>
174 lines
13 KiB
HTML
174 lines
13 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Pangolin CI/CD 全流程 · 设计方案(#30)</title>
|
||
<style>
|
||
:root{
|
||
--bg:#0f1117; --panel:#171a22; --panel2:#1d2129; --fg:#e6e8ee; --fg2:#a8afbd;
|
||
--accent:#e0884f; --accent2:#5fb0c9; --ok:#5ec27a; --bad:#e06a6a; --warn:#e0b84f;
|
||
--border:#272c36; --mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
|
||
--sans:-apple-system,"PingFang SC","Helvetica Neue",Arial,sans-serif;
|
||
}
|
||
*{box-sizing:border-box}
|
||
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.7;font-size:15px}
|
||
.wrap{max-width:960px;margin:0 auto;padding:48px 24px 96px}
|
||
h1{font-size:30px;line-height:1.3;margin:0 0 8px;letter-spacing:-.01em}
|
||
.sub{color:var(--fg2);font-size:15px;margin:0 0 32px}
|
||
h2{font-size:21px;margin:44px 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border)}
|
||
h3{font-size:16px;margin:26px 0 8px;color:var(--accent2)}
|
||
p{margin:10px 0}
|
||
code{font-family:var(--mono);font-size:.88em;background:var(--panel2);padding:1px 6px;border-radius:5px;color:#f0d9c4}
|
||
pre{background:#0a0c11;border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;font-family:var(--mono);font-size:13px;line-height:1.55;color:#cdd3df}
|
||
.tag{display:inline-block;font-size:12px;font-weight:600;padding:2px 9px;border-radius:999px;vertical-align:middle}
|
||
.tag.ok{background:rgba(94,194,122,.16);color:var(--ok)}
|
||
.tag.warn{background:rgba(224,184,79,.16);color:var(--warn)}
|
||
.tag.bad{background:rgba(224,106,106,.16);color:var(--bad)}
|
||
.tag.info{background:rgba(95,176,201,.16);color:var(--accent2)}
|
||
.card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:16px 0}
|
||
.card h3{margin-top:0;color:var(--fg)}
|
||
table{width:100%;border-collapse:collapse;margin:16px 0;font-size:14px}
|
||
th,td{text-align:left;padding:9px 12px;border-bottom:1px solid var(--border);vertical-align:top}
|
||
th{color:var(--fg2);font-weight:600;font-size:13px}
|
||
td code{font-size:.85em}
|
||
ul,ol{padding-left:22px;margin:10px 0}
|
||
li{margin:5px 0}
|
||
.lead{background:linear-gradient(180deg,rgba(224,136,79,.10),transparent);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:0 0 8px}
|
||
.small{color:var(--fg2);font-size:13px}
|
||
a{color:var(--accent2)}
|
||
.back{display:inline-block;margin-bottom:24px;font-size:13px}
|
||
b{color:#fff}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
<a class="back" href="index.html">← 文档索引</a>
|
||
<h1>Pangolin CI/CD 全流程 · 设计方案</h1>
|
||
<p class="sub">#30 · 2026-07-05 · <span class="tag warn">设计定稿待审</span> · 范围 A~F(排除 iOS、备份#26、TLS#25)· 真相源 <code>docs/superpowers/specs/2026-07-05-cicd-design.md</code></p>
|
||
|
||
<div class="lead">
|
||
<b>目标:</b>tag 触发的 <b>编译 → 测试 → 发版(Gitea release)→ 部署</b> 全自动。参考 jiu 的
|
||
<code>.gitea/workflows</code> + <code>scripts/ci/*.sh</code> 结构,适配 pangolin 的部署目标(pangolin1 /
|
||
pangolin.yanmeiai.com)与多端产物。现状:仅 <code>ci.yml</code> 校验无部署,服务端手动部署、官网未部署、下载死链。
|
||
</div>
|
||
|
||
<h2>1. 范围</h2>
|
||
<table>
|
||
<tr><th>子块</th><th>内容</th></tr>
|
||
<tr><td><b>A 基座</b></td><td><code>scripts/ci/*</code>(env/provision/test/release/notify/lib-forgejo)+ checks 保留</td></tr>
|
||
<tr><td><b>B 官网</b></td><td>Astro 构建 → 部署 <code>pangolin.yanmeiai.com</code></td></tr>
|
||
<tr><td><b>C 服务端</b></td><td>交叉编译 server/agent/migrate → release → ssh pangolin1(备份→migrate→换二进制→重启→健康检查)</td></tr>
|
||
<tr><td><b>D Android</b></td><td>apk(arm64,release keystore 签名)→ release 资产</td></tr>
|
||
<tr><td><b>E macOS</b></td><td>公证 dmg(Developer ID + notarytool)→ release 资产</td></tr>
|
||
<tr><td><b>F Windows</b></td><td>exe/installer(Inno Setup)→ release 资产</td></tr>
|
||
</table>
|
||
<p class="small"><b>排除:</b>iOS(G,未来)、SQLite 备份/容灾(#26)、控制面 TLS(#25)。</p>
|
||
|
||
<h2>2. 已锁定决策</h2>
|
||
<table>
|
||
<tr><th>维度</th><th>决定</th><th>理由</th></tr>
|
||
<tr><td>runner</td><td>nas=官网+服务端(容器化)· mac=Android+macOS · windows=Windows</td><td>nas 常在线且 Astro/Go 轻量(非 Flutter Web);mac/windows 做必须它们的活</td></tr>
|
||
<tr><td>触发</td><td>tag <code>site-v*</code> / <code>server-v*</code> / <code>client-v*</code> + <code>manual.yml</code> 手动派发</td><td>同 jiu,发版即部署,可手动重放</td></tr>
|
||
<tr><td>macOS 签名</td><td>mac 自动 Developer ID 签名 + notarytool 公证 + staple</td><td>凭据入 secret(见 §6)</td></tr>
|
||
<tr><td>Android 签名</td><td>正式 release keystore</td><td>app 级专属签名身份</td></tr>
|
||
<tr><td>下载链接</td><td>官网 href 指向 Gitea release 稳定资产 URL</td><td>发版即更新,见 §5</td></tr>
|
||
<tr><td>镜像</td><td>goproxy.cn / flutter-io.cn</td><td>国内网络</td></tr>
|
||
</table>
|
||
|
||
<h2>3. 架构</h2>
|
||
<h3>3.1 共享基座 <code>scripts/ci/</code>(镜像 jiu)</h3>
|
||
<ul>
|
||
<li><code>_env.sh</code> —— 公共环境(镜像源、路径、版本号解析 <code>${tag#prefix-v}</code>)</li>
|
||
<li><code>lib-forgejo.sh</code> —— release 建/查 + 资产上传(用 <code>FORGEJO_TOKEN</code>)</li>
|
||
<li><code>provision-mac.sh</code> —— mac 幂等装 flutter / xcode-select / gomobile / NDK+JDK17</li>
|
||
<li><code>test.sh</code>、<code>notify.sh</code>、<code>compile-*.sh</code>、<code>deploy-*.sh</code>、<code>release-*.sh</code></li>
|
||
</ul>
|
||
<p class="small">每个 <code>compile-*</code> 封装该端已验证的构建命令(Android 走 <code>build-libbox.sh android</code> +
|
||
<code>flutter build apk --split-per-abi</code>;macOS 走 Developer ID 签名 + <code>notarytool submit --wait</code> +
|
||
<code>stapler</code>)。工作流只调脚本,逻辑在脚本里、便于本地复现。</p>
|
||
|
||
<h3>3.2 工作流 <code>.gitea/workflows/</code></h3>
|
||
<table>
|
||
<tr><th>工作流</th><th>触发</th><th>runner</th><th>步骤</th></tr>
|
||
<tr><td><code>checks.yml</code>(现 ci.yml)</td><td>push 分支</td><td>nas</td><td>保留:shellcheck / openapi / redline / flutter analyze+test / go test</td></tr>
|
||
<tr><td><code>deploy-site.yml</code></td><td><code>site-v*</code></td><td>nas</td><td><code>node:20</code> 构建 Astro(注入 SITE_URL)→ <code>deploy-site.sh</code></td></tr>
|
||
<tr><td><code>deploy-server.yml</code></td><td><code>server-v*</code></td><td>nas</td><td><code>golang:1.25</code> 交叉编译 → test → release → <code>deploy-server.sh</code></td></tr>
|
||
<tr><td><code>build-android.yml</code></td><td><code>client-v*</code></td><td>mac</td><td>provision → <code>compile-android.sh</code>(签名 apk)→ release</td></tr>
|
||
<tr><td><code>build-macos.yml</code></td><td><code>client-v*</code></td><td>mac</td><td>provision → <code>compile-macos.sh</code>(签名+公证 dmg)→ release</td></tr>
|
||
<tr><td><code>build-windows.yml</code></td><td><code>client-v*</code></td><td>windows</td><td><code>compile-windows.sh</code>(exe/installer)→ release</td></tr>
|
||
</table>
|
||
|
||
<h3>3.3 服务端部署(固化 F3/F4 手动那套,带回滚)</h3>
|
||
<div class="card">
|
||
<ol>
|
||
<li>scp <code>pangolin-{server,agent,migrate}</code> 到 pangolin1 <code>/tmp</code></li>
|
||
<li><code>systemctl stop pangolin-server</code></li>
|
||
<li><code>wal_checkpoint(TRUNCATE)</code> → <code>cp</code> 备份 <code>pangolin.db.bak-pre-<tag></code></li>
|
||
<li><code>pangolin-migrate up</code>(pangolin 用户);<b>失败即恢复备份 + 重启旧 server + 退出非零</b></li>
|
||
<li><code>install</code> 新二进制到 <code>/usr/local/bin</code>(旧的备份为 <code>.bak-<tag></code>)</li>
|
||
<li><code>systemctl start</code> + <code>/healthz</code> 健康检查;agent 随连接自恢复</li>
|
||
</ol>
|
||
</div>
|
||
|
||
<h2>4. 官网部署 —— Cloudflare Pages</h2>
|
||
<div class="card" style="border-left:3px solid var(--warn)">
|
||
<b>架构变更(2026-07-06 实施):</b>原计划 rsync 到 pangolin1 的 nginx。但节点 <code>:443</code> 被 sing-box(VPN 数据面)占用,而 CF 免费套餐 proxied 回源只能打 :80/:443、改回源端口需 Enterprise —— 同机同 IP 上官网 HTTPS 与 VPN 无法共存。<b>故官网改由 Cloudflare Pages 托管</b>:纯静态、全程 HTTPS、<code>_headers</code>/CSP 原生生效、不落 VPS,从根上无 :443 冲突,也不拖累 VPN 机器。<b>已上线</b> <code>https://pangolin.yanmeiai.com</code>。
|
||
</div>
|
||
<p>Astro <code>npm ci && npm run build</code>(<code>SITE_URL=https://pangolin.yanmeiai.com</code>)→ <code>dist/</code>
|
||
经 <code>npx wrangler pages deploy</code> 发布到 Pages 项目 <b>pangolin-site</b>(自定义域 <code>pangolin.yanmeiai.com</code>,CNAME → <code>pangolin-site.pages.dev</code>,proxied)。需 secret <code>CLOUDFLARE_API_TOKEN</code>(带 Account>Pages>Edit)+ <code>CLOUDFLARE_ACCOUNT_ID</code>;deploy 步骤在 <code>node:20</code> 容器内跑 wrangler。灾备:产物仍纯静态,可另 rsync 到镜像。</p>
|
||
|
||
<h2>5. 下载链接闭环(30A)</h2>
|
||
<p><code>web/website/src/config/site.ts</code> 增 <code>downloads:{ android, macos, windows }</code>,值为 Gitea release
|
||
稳定 latest 资产 URL(Forgejo 支持 <code>…/releases/latest/download/<asset></code> 则直用;否则构建期用
|
||
<code>FORGEJO_TOKEN</code> 查最新 <code>client-v*</code> 版本烘焙进 href)。<code>Download.astro</code> 各平台按钮读
|
||
<code>SITE.downloads.<platform></code>。客户端发版后官网重部署即刷新(或 build-* 完成触发 deploy-site)。</p>
|
||
|
||
<h2>6. 密钥与作用域(solo / wangjia,命名对齐 jiu 以共用)</h2>
|
||
<table>
|
||
<tr><th>Secret</th><th>作用域</th><th>说明</th></tr>
|
||
<tr><td><code>FORGEJO_TOKEN</code> / <code>FORGEJO_URL</code></td><td>账户级</td><td>建 release + 传产物,jiu 复用</td></tr>
|
||
<tr><td><code>MACOS_DEVELOPER_ID_CERT_P12_BASE64</code> / <code>MACOS_DEVELOPER_ID_CERT_PASSWORD</code></td><td>账户级</td><td>Developer ID 证书(账号级),与 jiu 共用;续期改一处</td></tr>
|
||
<tr><td><code>APPSTORE_API_KEY_P8_BASE64</code> / <code>APPSTORE_API_KEY_ID</code> / <code>APPSTORE_API_ISSUER_ID</code></td><td>账户级</td><td>公证 API key(KEY_ID=<code>3PZTHR8YMJ</code>),与 jiu 同一把</td></tr>
|
||
<tr><td><code>DEPLOY_SSH_KEY</code></td><td>pangolin 仓库级</td><td>授权到 pangolin1,最小权限</td></tr>
|
||
<tr><td><code>ANDROID_KEYSTORE_BASE64</code> / <code>ANDROID_KEYSTORE_PASSWORD</code> / <code>ANDROID_KEY_ALIAS</code> / <code>ANDROID_KEY_PASSWORD</code></td><td>pangolin 仓库级</td><td>Android app 级专属签名(<b>pangolin 自己的 keystore,不复用 jiu</b>)</td></tr>
|
||
<tr><td><code>MACOS_APP_PROVISION_PROFILE_BASE64</code> / <code>MACOS_SYSEXT_PROVISION_PROFILE_BASE64</code></td><td>pangolin 仓库级</td><td>主 app + PacketTunnel sysext 描述文件(pangolin bundle 专属)</td></tr>
|
||
</table>
|
||
<p class="small">命名对齐 jiu(<code>MACOS_*</code>/<code>APPSTORE_*</code>/<code>ANDROID_*</code>):<b>Apple 那套放账户级 → jiu/pangolin 共用一份</b>,compile-macos 脚本可复用 jiu 的;Android keystore 虽同命名规范但<b>各 app 独立、不共享</b>。工作流用 <code>secrets.XXX</code> 引用,作用域对写法透明。</p>
|
||
|
||
<h2>7. 实现顺序</h2>
|
||
<p>范围虽 A~F,按风险/依赖递增落地,每阶段独立可发、独立验收:</p>
|
||
<ol>
|
||
<li><b>A 基座</b> + <code>checks</code> 迁移(抽 <code>scripts/ci</code> 骨架)</li>
|
||
<li><b>B 官网</b>(最简,验证 release/deploy 骨架)</li>
|
||
<li><b>C 服务端</b>(固化手动部署)</li>
|
||
<li><b>D Android</b>(解锁下载链接;需 keystore 就绪 + gradle 接签名)</li>
|
||
<li><b>E macOS</b>(最复杂:证书 + 2 描述文件 + 公证)</li>
|
||
<li><b>F Windows</b>(windows runner + Inno Setup)</li>
|
||
</ol>
|
||
|
||
<h2>8. 验证</h2>
|
||
<ul>
|
||
<li>每条流水线先 <code>workflow_dispatch</code> 手动跑通、核对产物/部署,再依赖 tag。</li>
|
||
<li>服务端:<code>server-v*</code> → migrate 版本 + <code>/healthz</code> + 行数守恒(同 F3 核对)。</li>
|
||
<li>官网:<code>site-v*</code> → 站点可访问 + canonical 正确 + redline 扫描。</li>
|
||
<li>客户端:release 资产可下载安装(Android 侧载 / macOS <code>spctl</code> / Windows 安装)。</li>
|
||
<li>下载链接:官网按钮落到最新 release 资产。</li>
|
||
</ul>
|
||
|
||
<h2>9. 风险与缓解</h2>
|
||
<table>
|
||
<tr><th>风险</th><th>缓解</th></tr>
|
||
<tr><td>nas 内存(3.8G)构建 OOM</td><td>容器化单 job、Astro/Go 轻量;必要时该端移 mac</td></tr>
|
||
<tr><td>migrate 在生产出错</td><td>部署前备份 + 失败自动回滚(§3.3),已在 F3/F4 手动验证</td></tr>
|
||
<tr><td>Android keystore 丢失</td><td>存 Bitwarden(文件+密码);终身签名身份</td></tr>
|
||
<tr><td>macOS 公证凭据泄露</td><td>账户级 secret,不落盘;<code>.p8</code>/<code>.p12</code> 用完即删临时文件</td></tr>
|
||
<tr><td>发版后下载链接不刷新</td><td>build-* 成功触发 deploy-site 重烘焙,或用 latest-download 稳定 URL</td></tr>
|
||
</table>
|
||
|
||
<h2>10. 不在本方案</h2>
|
||
<p class="small">iOS 流水线(G)、SQLite 备份/容灾(#26)、TLS(#25)、上架 Play、Windows 代码签名(先不签)。</p>
|
||
</div>
|
||
</body>
|
||
</html>
|