好学探索 · 打包发布操作手册
CI 全关(GitHub Actions 无额度),发版 100% 本地 + ptang 服务器,不占 Actions 额度。 单镜像多架构(amd64 + arm64)推 ghcr,NAS 用户
docker compose pull即更新。配套:release-local.sh(一键脚本)、update-manifest.mjs(OTA manifest 同步)。
前置(一次性)
- ptang 服务器:已装 docker + buildx。
~/.ssh/config配ptang别名(同 PTPatronus:HostName 152.53.91.46/User root/IdentityFile ~/.ssh/id_ed25519)。 - ghcr 包设 public:GitHub → 你的 Packages → tanqu → Package settings → Change visibility → Public(用户
docker pull免登录)。 - GH_CLIENT_ID:GitHub → Settings → Developer settings → OAuth Apps → New OAuth App(Authorization callback URL 随意填),拿到 client_id,填入项目根
.env:GH_CLIENT_ID=你的client_iddevice flow 的 client_id 是公开标识符,不是密钥;它只用于发起授权,token 用完即 logout,本机/服务器都不留 ghcr 凭据。
- CI 已关:发版不触发任何 GitHub Actions(
docker.yml仅 tag 触发且依赖额度,本流程不靠它)。
关键地址
| 项 | 值 |
|---|---|
| 主仓(public) | github.com/gntv456/tanqu |
| ghcr 镜像 | ghcr.io/gntv456/tanqu |
| OTA manifest raw | https://raw.githubusercontent.com/gntv456/tanqu/main/update-manifest.json |
| ptang 构建目录 | /root/tanqu-build |
每次发版(以 0.1.1 → 0.1.2 为例)
0. 改版本号(2 处源码)
cd /d/HX-Tanqu
sed -i 's/0\.1\.1/0.1.2/g' package.json frontend/src/views/me.js
update-manifest.json由 release-local.sh 自动同步,不用手改。
提交版本号:git add package.json frontend/src/views/me.js && git commit -m "chore: bump → 0.1.2"。
1. 一键发版
bash scripts/release-local.sh 0.1.2脚本自动完成:
- ptang buildx/binfmt 准备(首次自动装 qemu)
- tar 传代码到 ptang(排除 node_modules/data/.git)
- ghcr device flow:打印 user_code,浏览器开 https://github.com/login/device 输入授权,回车继续(15 分钟内有效)
- ptang
docker buildx build --platform linux/amd64,linux/arm64 --push推:0.1.2+:latest,logout - 本地
update-manifest.mjs同步 latestVersion + docker tag + publishedAt
2. 编辑 releaseNotes + 提交 manifest
# 手动编辑 update-manifest.json 的 releaseName / releaseNotes 后:
git add update-manifest.json
git commit -m "release: v0.1.2(manifest)"
git push origin mainpush 后 raw 即生效(CDN 缓存 ~5min)。
3. 验证
- 镜像:
docker pull ghcr.io/gntv456/tanqu:0.1.2→ 启动后关于页显示 version 0.1.2 - OTA:部署的 compose 已默认带
UPDATE_MANIFEST_URL→ 关于页「检查更新」提示 0.1.2 - raw:
curl -s https://raw.githubusercontent.com/gntv456/tanqu/main/update-manifest.json | grep latestVersion→ 0.1.2
OTA 两条更新机制(别混淆)
| 机制 | 触发 | 你要做的 |
|---|---|---|
| Web UI「检查更新」 | 后端读 UPDATE_MANIFEST_URL 对比 latestVersion 与当前版本 | compose 设 UPDATE_MANIFEST_URL(默认已带),重启容器 |
| docker 镜像更新 | :latest tag 被 pull | push 完 :latest 即可,用户 docker compose pull && docker compose up -d |
两者独立:没设 UPDATE_MANIFEST_URL 只是 Web UI 不提示,不影响 :latest 拉镜像更新。
手动分步(脚本失败时)
# a. 传码(Dockerfile multi-stage 自带 npm ci,不用传 node_modules/dist)
tar czf - --exclude='./node_modules' --exclude='./.git' --exclude='./data' \
--exclude='./scripts/_*' --exclude='./tmp_*' \
backend frontend Dockerfile entrypoint.sh package.json package-lock.json deploy docs \
| ssh ptang "rm -rf /root/tanqu-build && mkdir -p /root/tanqu-build && tar xzf - -C /root/tanqu-build"
# b. ptang buildx 多架构 build + push(需先 device flow login)
ssh ptang "cd /root/tanqu-build && docker buildx build --platform linux/amd64,linux/arm64 \
--build-arg VERSION=0.1.2 -t ghcr.io/gntv456/tanqu:0.1.2 -t ghcr.io/gntv456/tanqu:latest --push ."常见坑
- ARM 构建慢:ptang 是 x86_64,arm64 靠 qemu 交叉编译(better-sqlite3 native 需编译),首次约 5–10 分钟;后续 Docker 层缓存会快。
- device code 过期:15 分钟,过期重跑脚本即可(重新申请 code)。
- manifest CDN 缓存:push 后 raw ~5min 才更新,验证时等一下。
- ghcr 包未 public:用户
docker pull需先docker login ghcr.io,体验差——务必到 Package settings 设 Public。 .env别提交:.env在.gitignore内,GH_CLIENT_ID放那里不会入仓;commit 前确认git status无.env。
用 Claude Code(agent)一键发版
让 Claude 自动跑 release-local.sh(你只浏览器授权一次 device code),关键是授权话术要「点名 + 具体」——Claude 自动模式的安全校验要求明确说出服务器、操作、版本、目标,笼统的"你自己发"会被拦。
发版前发给 Claude 的授权话术(照抄,改版本号)
我明确授权 Claude 执行 HX-Tanqu vX.Y.Z 发版:ssh 到 ptang 服务器(152.53.91.46),用 docker buildx 构建多架构镜像(linux/amd64, linux/arm64),push 到 ghcr.io/gntv456/tanqu:X.Y.Z 和 :latest,并同步 update-manifest.json、commit push 到 main。
发完这句,Claude 自动:传码 → 申请 device code → 把 user_code 给你 → 你浏览器输一次 → buildx 构建推送 → 同步 manifest → commit push。全程你只动一次。
ghcr 包页面 & 可见性
- 包地址:https://github.com/users/gntv456/packages/container/tanqu (⚠️ 不是
/pkgs/tanqu,正确路径是/packages/container/tanqu;或从头像 → Your packages 进) - 可见性:关联 public repo 时通常继承 Public(用户
docker pull免登录);若显示 Private,进 Package settings → Danger Zone → Change visibility → Public。
踩过的坑(别再踩)
- 项目级
.claude/settings.local.json别加 broad allow(ssh */docker *之类)——会被 Claude 当"自我提权"全面拦截,反而盖掉全局放行。ssh/docker 走全局~/.claude/settings.json的Bash(ssh *)、Bash(docker *)即可(和 PTPatronus 一样)。 - 授权必须点名 + 具体:说清"ssh ptang(152.53.91.46)、docker buildx 构建、push ghcr.io/gntv456/tanqu:X.Y.Z",笼统的"你自己跑"达不到校验门槛。
- OAuth App 要启用 Device Flow:github.com/settings/developers → 你的 App → Device Flow → Enable(默认不开)。