自建 ZCode 内部 mirror:mirrors.changediff.top 与 apt 源落地记录
最近把 ZCode 的安装方式从「人工下载 + dpkg -i」改成了内部 apt 源。这篇记录整个方案和踩过的四个坑。
背景:为什么要自己搭 mirror
ZCode(智谱 Z.AI 的 IDE 产品)官方的分发方式对国内用户不太友好:
- 官网下载页 https://zcode.z.ai 只宣传 AppImage
- 真实 CDN 在
https://cdn-zcode.z.ai/zcode/electron/releases/<版本>/linux-x64/,同目录下其实有deb/rpm,但页面没写 - 国内直连 CDN 慢,且没有 apt 源
对 deb03 这台开发机来说,每次升级都要人工去 CDN 找版本号、下载、dpkg -i。既然 ali 上已经有 nginx + certbot + 博客站点,顺手加一个 mirror 是最自然的做法。
方案
在 ali(阿里云 ECS)上建内部 mirror,deb03 通过 apt 源安装/升级。
| 组件 | 位置 |
|---|---|
| 域名 | mirrors.changediff.top |
| 站点根 | /var/www/mirrors |
| 包目录 | /var/www/mirrors/zcode/deb、/var/www/mirrors/zcode/rpm |
| apt 源路径 | https://mirrors.changediff.top/zcode/deb/ |
| nginx 配置 | /etc/nginx/sites-available/mirrors.changediff.top.conf |
| 同步脚本 | /usr/local/bin/zcode-mirror-sync.sh(每日 03:17 cron) |
| 索引脚本 | /usr/local/bin/zcode-mirror-index.sh |
| 同步日志 | /var/log/zcode-mirror-sync.log |
| deb03 apt 源 | /etc/apt/sources.list.d/zcode-mirror.list |
全部由 Ansible playbook zcode_mirror.yml 管理,代码在 ccops 仓库。
保留策略
只保留官方最近 3 个版本的 deb + rpm,不存 AppImage。当前是 3.14.1 / 3.14.0 / 3.12.3,旧版本由同步脚本的 KEEP=3 自动清理。
版本发现方式是抓官方 changelog 页面,正则取版本号后按语义版本降序排前 3。
apt 源形式:Flat Repository
deb 目录直接当 flat repository 用,不需要 reprepro/dak 这类仓库工具:
1 | # /etc/apt/sources.list.d/zcode-mirror.list |
- 索引由
dpkg-scanpackages --multiversion生成Packages(同时产出.gz/.xz) Release由脚本生成;因为用了[trusted=yes],apt 不校验签名,但仍写入 checksum 条目- 单行 sources.list,不需要
signed-by、不需要分发 GPG key
源现在已经在线,可以直接看索引:https://mirrors.changediff.top/zcode/deb/Packages
同步流程
1 | cron 03:17 → zcode-mirror-sync.sh |
deb03 侧之后就是标准的 apt update && apt install zcode / apt upgrade。
四个坑
1. 证书签发顺序:先建 nginx 站点,再扩 SAN
首次 certbot certonly --expand 报 404:
1 | Detail: Invalid response from https://mirrors.changediff.top/.well-known/acme-challenge/xxx: 404 |
原因:mirrors.changediff.top 的 server 块还不存在时,这个域名的 HTTP 请求落到 nginx 的 default_server(return 444),ACME 挑战文件写下去了也取不到。
正确顺序:先建 nginx 站点并 reload,再跑 certbot。playbook 里用 meta: flush_handlers 保证 reload 先发生。
2. certbot 的 webroot 必须是 blog 的目录
换对顺序后仍然 404,因为用了 -w /var/www/mirrors(mirror 自己的 root)——mirror 站点没有 acme-challenge location,挑战文件放进去同样取不到。
最终生效的命令用了博客站点的 webroot:
1 | certbot certonly --webroot -w /var/www/changediff-blog \ |
mirrors.changediff.top 进 SAN,与 blog 共用同一张证书。
3. Flat Repository 必须生成 Packages 索引
一开始只把 deb 丢进目录就配 apt 源,结果 404:
1 | E: The repository 'https://mirrors.changediff.top/zcode/deb ./' does not have a Release file. |
deb 目录需要 dpkg-scanpackages 生成的 Packages 索引。装 dpkg-dev 提供该工具。
4. Release 的 checksum 段不能混算法
第一版脚本把 md5 和 sha256 都写进了 MD5Sum: 段,apt 报:
1 | W: 无法鉴别的 .../Release: 无哈希条目 |
正确写法:MD5Sum: 段只放 md5 行,SHA256: 段只放 sha256 行,各自独立。索引脚本里用 Python 分段生成。
小结
四个坑里有三个都指向同一个教训:nginx 站点结构(default_server、location、webroot)会直接影响 ACME 和 apt 客户端的行为,搭 mirror 时不能只想着”文件放进目录就行”。
现在 deb03 装 ZCode 就是一行命令,ali 上每天自动同步最新 3 版。playbook 和脚本都在 ccops 仓库,ansible/playbooks/zcode_mirror.yml,完整文档见 docs/zcode-mirror.md。