一键导入
gettokens-release-governance
GetTokens macOS 发版流程:版本同步、提交 tag、Release workflow、Sparkle appcast、官方 DMG 下载验收、memory 写回。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
GetTokens macOS 发版流程:版本同步、提交 tag、Release workflow、Sparkle appcast、官方 DMG 下载验收、memory 写回。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
GetTokens 领域工程:账号池、quota、UI 体系、调试与 CLIProxyAPI fork。
GetTokens 流程治理:Wails 开发回路、spaces、文档记忆写回与 AGENTS 同步。
GetTokens Codex 账号列表:Codex Channel Routing、账号请求顺序、两模式路由、路由探测、模型映射、OAuth 透传语义、openai-compatible 映射保存与浏览器预览。
GetTokens CLIProxyAPI 上游版本 intake / reference-port 工作流:看新 tag、规划合并、拆子 space、在 gettokens/sidecar 内重实现、验证、重建 sidecar、dev App 验收与双仓提交。
Use when dxyer iOS verification, smoke testing, or debugging needs real API traffic evidence through Proxyman CLI: clear sessions, discover proxy host, export HAR/proxymansession logs, inspect request headers, verify DXY-TOD-SESSION user switching, or distinguish API data mismatch from UI/function failure.
GetTokens 性能治理:用于排查或预防内存占用、WebView/Wails 卡顿、React 渲染放大、大账号池请求放大、sidecar Go 热路径、日志/SQLite/本地文件膨胀、性能回归、perf budget 或 profiling 验收;当用户说性能、内存、卡顿、占用、峰值、泄漏、慢、回归、治理、budget、pprof、profiling,或改动触及账号池、quota、live sessions、session-management、usage attribution、sidecar management API、Wails bridge、日志热路径时使用。
| name | gettokens-release-governance |
| description | GetTokens macOS 发版流程:版本同步、提交 tag、Release workflow、Sparkle appcast、官方 DMG 下载验收、memory 写回。 |
当用户要求“提交 / 推送 / 发版 / release / 发布 / 走发版流程”,或需要确认某个 GetTokens macOS 版本是否已经可分发时,使用本 skill。它是 GetTokens 专用发布入口,执行时同时遵守 check 的 Ship / Release Follow-through 门禁和 gettokens-ops-governance 的文档、memory、AGENTS 边界。
vX.Y.Z,由 GitHub Actions Release workflow 触发。master,但不得移动、删除或重建已发布 tag。/Applications/GetTokens.app 正式版,不 kill 正式版进程,不替换正式版 sidecar 或配置。先读状态,不要假设干净:
git status --short --branch -uall
git fetch --tags origin
git tag --sort=-version:refname | head -20
git ls-remote --tags origin "refs/tags/vX.Y.Z"
判断并记录:
origin/<branch> 是否同步。frontend/wailsjs 若只有尾随空白漂移,优先运行 ./scripts/normalize-wailsjs.sh 收口,再重新读 diff。只同步 release 版本三件套:
npm --prefix frontend version X.Y.Z --no-git-tag-version --allow-same-version
md5 -q frontend/package.json
必须更新并核对:
frontend/package.jsonfrontend/package-lock.jsonfrontend/package.json.md5frontend/package.json.md5 必须等于 md5 -q frontend/package.json,不要手写猜值。
标准发布前至少运行:
go test ./...
npm --prefix frontend run test:unit
npm --prefix frontend run typecheck
npm --prefix frontend run build
docs-linhay/scripts/check-docs.sh
说明:
只 stage 发版相关文件:
git add frontend/package.json frontend/package-lock.json frontend/package.json.md5
git commit -m "chore: bump version to X.Y.Z"
git push origin <branch>
git tag vX.Y.Z
git push origin vX.Y.Z
推 tag 前再次确认:
git rev-parse HEAD 等于 git rev-parse origin/<branch>。git ls-remote --tags origin "refs/tags/vX.Y.Z" 为空。docs-linhay/references/CLIProxyAPI gitlink 构建 sidecar,先确认该 gitlink commit 已经在 AxApp/CLIProxyAPI#gettokens/sidecar 远端可达;不要只看本地 reference repo。推荐门禁:EXPECTED_CLI_PROXY_COMMIT="$(git ls-tree HEAD docs-linhay/references/CLIProxyAPI | awk '$2 == "commit" {print $3}')"
test -n "$EXPECTED_CLI_PROXY_COMMIT"
git ls-remote https://github.com/AxApp/CLIProxyAPI.git refs/heads/gettokens/sidecar | rg "$EXPECTED_CLI_PROXY_COMMIT"
若远端没有该 commit,先在 docs-linhay/references/CLIProxyAPI 内推送 gettokens/sidecar,再重新执行本地门禁和版本提交;不要推一个必然会在 CI 中 upload-pack: not our ref 的 tag。
tag 推送后监控 Release workflow:
gh run list --workflow Release --limit 5 --json databaseId,headBranch,headSha,status,conclusion,createdAt,url,displayTitle
gh run watch <run-id> --exit-status
成功标准:
Build H5 成功。Build macOS arm64 成功。Build macOS amd64 成功。Publish Sparkle appcast 成功。Publish Release 成功。失败处理:
gh run view <run-id> --log-failed。Release workflow 成功后读取官方 Release:
gh release view vX.Y.Z --json url,assets,publishedAt,tagName
必须存在七个资产:
GetTokens_macOS_AppleSilicon.dmgGetTokens_macOS_AppleSilicon.tar.gzGetTokens_darwin_arm64.tar.gzGetTokens_macOS_Intel.dmgGetTokens_macOS_Intel.tar.gzGetTokens_darwin_amd64.tar.gzchecksums.txt其中 GetTokens_macOS_* 是用户可读资产名,GetTokens_darwin_* 是 go-selfupdate 检测兼容资产名。若某个已安装旧版本因本地策略没有进入 Sparkle 原生 UI,它仍会依赖 go-selfupdate DetectLatest 判断是否有新版;缺少 darwin_<arch> 兼容资产时,旧客户端会把已发布版本误判为“当前已是最新版本”。
注意:Sparkle appcast 默认不是 GitHub Release asset,它发布到 sparkle-appcast 分支。
先从 CI job 确认发布动作:
gh run view <run-id> --json jobs,status,conclusion,url
git ls-remote origin refs/heads/sparkle-appcast
再验证远端 appcast 内容。推荐先 fetch,避免本地 origin/sparkle-appcast 缓存陈旧:
git fetch origin sparkle-appcast
git show origin/sparkle-appcast:appcast-arm64.xml | rg "vX.Y.Z|GetTokens_macOS_AppleSilicon\\.dmg|sparkle:version|shortVersionString"
git show origin/sparkle-appcast:appcast-amd64.xml | rg "vX.Y.Z|GetTokens_macOS_Intel\\.dmg|sparkle:version|shortVersionString"
可用网络直读补充验证:
curl --max-time 15 -fsSL https://raw.githubusercontent.com/AxApp/GetTokens/sparkle-appcast/appcast-arm64.xml
curl --max-time 15 -fsSL https://raw.githubusercontent.com/AxApp/GetTokens/sparkle-appcast/appcast-amd64.xml
判断口径:
Publish Sparkle appcast job 成功,并且 sparkle-appcast 分支两个 XML 顶部包含目标版本和对应 DMG URL,即为 Sparkle 已发布。appcast-*.xml,这是当前流程的正常状态,不代表 Sparkle 没发。raw.githubusercontent.com 超时,改用 git fetch + git show origin/sparkle-appcast:<file> 作为权威确认。如果线上已经出现“Sparkle 在线更新后 app 签名、公证静态校验都通过,但 GUI 启动被 AppleSystemPolicy / provenance runtime policy 拦截”的真实用户故障,必须额外执行一轮 server-side 止血判断:
sparkle-appcast 继续停留在上一安全版本;必要时把新版本视为“仅 GitHub Release 手动安装可用”。结论口径也要区分:
GitHub Release 已发布:官方 DMG 可手动下载安装。Sparkle rollout 已 hold:appcast 故意不暴露该版本,避免旧客户端继续触发已知坏更新链路。如果用户明确要求“检查更新能检测到新版”、或最终交付口径包含“在线更新 / 检查更新可用”,不得静默执行 Sparkle hold。此时必须让远端 appcast 顶部保留目标版本,或在 hold 前先明确说明“检查更新不会看到这个版本”并获得用户确认。
不要因为单次 macOS provenance/runtime policy 故障就在客户端永久禁用 Sparkle。默认策略是:macOS bundle 内 Sparkle 可用时,检查更新走 Sparkle 原生 UI;疑似系统策略拦截按独立故障保留证据、临时 hold appcast 或做端到端升级回归。
必须下载 GitHub Release 上的正式资产,不使用本地 dist/ 替代:
VERIFY_DIR="$(mktemp -d /tmp/gettokens-vX.Y.Z-verify.XXXXXX)"
EXPECTED_CLI_PROXY_COMMIT="$(git ls-tree HEAD docs-linhay/references/CLIProxyAPI | awk '$2 == "commit" {print $3}')"
test -n "$EXPECTED_CLI_PROXY_COMMIT"
gh release download vX.Y.Z --dir "$VERIFY_DIR"
cd "$VERIFY_DIR"
shasum -a 256 -c checksums.txt
DMG 级验收:
spctl -a -t open --context context:primary-signature -v GetTokens_macOS_AppleSilicon.dmg
xcrun stapler validate GetTokens_macOS_AppleSilicon.dmg
spctl -a -t open --context context:primary-signature -v GetTokens_macOS_Intel.dmg
xcrun stapler validate GetTokens_macOS_Intel.dmg
挂载并验包内 app:
mkdir -p /tmp/gettokens-vX.Y.Z-arm64 /tmp/gettokens-vX.Y.Z-amd64
hdiutil attach -nobrowse -readonly -mountpoint /tmp/gettokens-vX.Y.Z-arm64 GetTokens_macOS_AppleSilicon.dmg
hdiutil attach -nobrowse -readonly -mountpoint /tmp/gettokens-vX.Y.Z-amd64 GetTokens_macOS_Intel.dmg
codesign -dv --verbose=4 /tmp/gettokens-vX.Y.Z-arm64/GetTokens.app 2>&1 | rg "Authority=|Timestamp=|Notarization|TeamIdentifier="
codesign -dv --verbose=4 /tmp/gettokens-vX.Y.Z-amd64/GetTokens.app 2>&1 | rg "Authority=|Timestamp=|Notarization|TeamIdentifier="
file /tmp/gettokens-vX.Y.Z-arm64/GetTokens.app/Contents/MacOS/GetTokens
file /tmp/gettokens-vX.Y.Z-amd64/GetTokens.app/Contents/MacOS/GetTokens
/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" -c "Print :CFBundleVersion" -c "Print :SUFeedURL" /tmp/gettokens-vX.Y.Z-arm64/GetTokens.app/Contents/Info.plist
/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" -c "Print :CFBundleVersion" -c "Print :SUFeedURL" /tmp/gettokens-vX.Y.Z-amd64/GetTokens.app/Contents/Info.plist
cat /tmp/gettokens-vX.Y.Z-arm64/GetTokens.app/Contents/MacOS/cli-proxy-api.meta.json
cat /tmp/gettokens-vX.Y.Z-amd64/GetTokens.app/Contents/MacOS/cli-proxy-api.meta.json
EXPECTED_CLI_PROXY_COMMIT="$EXPECTED_CLI_PROXY_COMMIT" \
python3 - <<'PY'
import json
import os
from pathlib import Path
expected = os.environ["EXPECTED_CLI_PROXY_COMMIT"].strip()
for label, meta_path in {
"arm64": Path("/tmp/gettokens-vX.Y.Z-arm64/GetTokens.app/Contents/MacOS/cli-proxy-api.meta.json"),
"amd64": Path("/tmp/gettokens-vX.Y.Z-amd64/GetTokens.app/Contents/MacOS/cli-proxy-api.meta.json"),
}.items():
meta = json.loads(meta_path.read_text())
actual = str(meta.get("commit", "")).strip()
if actual != expected:
raise SystemExit(f"{label} sidecar metadata mismatch: {actual!r} != {expected!r}")
PY
hdiutil detach /tmp/gettokens-vX.Y.Z-arm64
hdiutil detach /tmp/gettokens-vX.Y.Z-amd64
验收标准:
accepted,来源包含 Notarized Developer ID。stapler validate 成功。.app 签名链为 Developer ID Application: HAN LIN (3L8RM3MDLS),有 timestamp 和 Notarization Ticket=stapled。arm64,Intel 可执行文件为 x86_64。CFBundleShortVersionString 与 CFBundleVersion 等于目标版本。SUFeedURL 分别指向 appcast-arm64.xml 和 appcast-amd64.xml。.app 内置 cli-proxy-api.meta.json 的 commit 必须等于 docs-linhay/references/CLIProxyAPI 当前提交;如果 release 修复依赖某个 sidecar 热路径字段、endpoint 或 JSON 字段,还必须用 strings 或只读 management API 验证该特征确实存在。只验证主程序版本、签名和 DMG 公证不足以声明 sidecar 修复已进入正式包。发布结论必须写入 docs-linhay/memory/YYYY-MM-DD.md,至少包含:
CI 发布完成、可分发 DMG 验收完成 或具体阻塞。写回后运行:
docs-linhay/scripts/check-docs.sh
qmd update && qmd embed
如果只产生 post-release memory/docs 变更,可以在 tag 之后单独提交并推送 master,但不要移动 release tag。
区分三种状态:
已推 tag,CI 进行中:还不能说 Release 已发布。CI 发布完成:workflow 成功,GitHub Release 和七个资产存在。可分发 DMG 验收完成:官方资产下载后,checksum、DMG Gatekeeper/stapler、app 签名/架构/版本/Sparkle 全部通过。最终回复应给出: