自建 ZCode 内部 mirror:mirrors.changediff.top 与 apt 源落地记录
最近把 ZCode 的安装方式从「人工下载 + dpkg -i」改成了内部镜像源。这篇记录整个方案、踩过的四个坑,以及最后的使用配置指南。
背景:为什么要自己搭 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 源
对内部开发机来说,每次升级都要人工去 CDN 找版本号、下载、dpkg -i。既然公网服务器上已经有 nginx + certbot 和站点,顺手加一个 mirror 是最自然的做法。
方案
在一台公网服务器上用 nginx 搭内部镜像,内网开发机通过 apt 源安装/升级。
| 组件 | 说明 |
|---|---|
| 域名 | mirrors.changediff.top |
| 站点根 | 独立静态目录,与博客站点同级 |
| 包目录 | zcode/deb、zcode/rpm |
| 索引 | deb 用 Flat Repository(Packages + Release);rpm 仅存文件,无 repodata |
| 更新 | 每日定时同步官方最近 3 个版本 |
| 管理 | 全部由 Ansible playbook 管理(部署、同步脚本、nginx 模板、证书 SAN) |
保留策略
只保留官方最近 3 个版本的 deb + rpm,不存 AppImage。当前是 3.14.1 / 3.14.0 / 3.12.3,旧版本由同步脚本的 KEEP=3 自动清理。
版本发现方式是抓官方 changelog 页面,正则取版本号后按语义版本降序排前 3。
索引页
访问 https://mirrors.changediff.top/zcode/ 会看到一个自动生成的 HTML 索引页,列出当前所有可下载文件、最新版本号和最近同步时间:
1 | ZCode Mirror (mirrors.changediff.top/zcode) |
注意:rpm 目录只存放原始文件,没有生成 repodata,所以 yum/dnf 暂时不能把它当仓库用,只能 curl -O 直接下载后 rpm -ivh 或 dnf install ./xxx.rpm。deb 则是完整的 flat repository,可以直接 apt install。
四个坑
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 必须是主站目录
换对顺序后仍然 404,因为用了 mirror 自己的 root 作 webroot——mirror 站点没有 acme-challenge location,挑战文件放进去同样取不到。
最终生效的命令用了主站点的 webroot:
1 | certbot certonly --webroot -w /var/www/<主站目录> \ |
mirrors.changediff.top 进 SAN,与主站共用同一张证书。
3. Flat Repository 必须生成 Packages 索引
一开始只把 deb 丢进目录就配 apt 源,结果报错:
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 分段生成。
线上现在的 Release 长这样,两种算法各自独立成段:
1 | Origin: mirrors.changediff.top |
小结
四个坑里有三个都指向同一个教训:nginx 站点结构(default_server、location、webroot)会直接影响 ACME 和 apt 客户端的行为,搭 mirror 时不能只想着「文件放进目录就行」。
📖 mirrors.changediff.top 使用配置指南
这一节是给使用者的:怎么在自己的机器上把 ZCode 的安装和升级接到这个 mirror 上。
一、Ubuntu / Debian(apt 源)
实测状态(2026-09-22):在客户端上完整验证过——
apt update正常拉取索引,apt install --only-upgrade将 zcode 从3.12.3-7463成功升级到3.14.1-7714,162 MB 安装包下载速度约 430 KB/s,升级后二进制可正常启动并注册zcode://协议。下面的步骤是照抄可用的。
1. 添加软件源
一条命令,写进 /etc/apt/sources.list.d/zcode-mirror.list:
1 | echo "deb [trusted=yes] https://mirrors.changediff.top/zcode/deb ./" \ |
这里用的是 Flat Repository 形式(./ 指向源根目录下的 Packages),不需要 signed-by、不需要导入 GPG key。[trusted=yes] 表示跳过签名校验——内网自用场景可以接受,但如果这个源将来对公网开放,务必换成 GPG 签名。
2. 更新索引并安装
1 | sudo apt update |
第一次装完验证一下版本:
1 | zcode --version |
3. 升级
mirror 每天自动同步官方最新版,所以你只需要:
1 | sudo apt update && sudo apt upgrade |
4. 常见问题
Q:apt update 后看不到 zcode?
检查索引是否真的拉下来了:
1 | curl -s https://mirrors.changediff.top/zcode/deb/Packages | grep -E "^(Package|Version):" |
有输出说明源是好的,问题在客户端(apt update 没成功/域名解析)。没有输出则是服务端同步挂了。
Q:依赖装不上?
ZCode 的 deb 依赖 GTK 相关库,都是 Ubuntu 官方源里有的包,不需要额外配置:
1 | libgtk-3-0, libnotify4, libnss3, libxss1, libxtst6, xdg-utils, |
Q:想锁定某个版本不升级?
1 | sudo apt-mark hold zcode # 锁定 |
二、RHEL / Rocky / Fedora(rpm)
目前 rpm 目录没有 repodata,只能直接下载安装:
1 | # 下载 |
升级时把版本号换成新的,重新走一遍即可。如果后续需要 yum/dnf 仓库形式,服务端补一个 createrepo_c 生成 repodata 就行——目前没做的原因是内部只有 deb 客户端。
三、直接下载(任何平台)
不想配源,直接拿文件也完全可以。索引页 https://mirrors.changediff.top/zcode/ 会列出当前所有可用文件和版本:
1 | # 最新版 deb |
四、列出当前所有可用版本
1 | # deb 源索引(机器可读) |
目前固定保留最近 3 个版本,所以任何时候 Packages 里最多三条记录。需要更老版本的话,回官方 CDN 下载:
1 | curl -LO https://cdn-zcode.z.ai/zcode/electron/releases/<版本>/linux-x64/ZCode-<版本>-linux-x64.deb |
🔬 实测记录(2026-09-22)
所有结论都在一台真实客户端上跑过,不是纸上谈兵。
环境
- 客户端:Debian GNU/Linux 13,amd64
- 安装前版本:
zcode 3.12.3-7463 - mirror 端当时版本:
3.14.1/3.14.0/3.12.3
1. apt update 拉取索引
1 | Ign:3 https://mirrors.changediff.top/zcode/deb ./ InRelease |
InRelease 和 Release.gpg 显示 Ign 是预期的——flat repository 没有 GPG 签名,配合 [trusted=yes] 使用。看到 Hit + 618 字节的 Release 被拉下来,索引链路就是通的。
2. 版本解析
1 | $ apt-cache policy zcode |
三个版本全部可见,Candidate 正确指向最新版 3.14.1。
3. 真实升级
1 | $ sudo apt-get install -y --only-upgrade zcode |
162 MB 的包完整下载、解包、配置,触发器全部正常。
4. 二进制启动验证
升级不是终点,能跑起来才算数:
1 | [2026-09-22 16:17:28.281] [main] [crash-capture] configured remoteCrashReporterEnabled=true |
zcode:// 协议注册成功,说明 Electron 应用的启动流程完整走通了。
5. 下载速度
从客户端 Range 请求 5 MB 测得:
| 文件 | HTTP | 速度 |
|---|---|---|
deb/ZCode-3.14.1-linux-x64.deb |
206 Partial Content | ~432 KB/s |
rpm/ZCode-3.14.1-linux-x64.rpm |
206 Partial Content | ~405 KB/s |
Range(206)正常工作意味着 apt 的断点续传能力可用——162 MB 的包中途断线可以续传,不用重下。
6. 顺手修掉的两个问题
这次复查还发现并修了两处:
- 索引页泄漏内部主机名:
mirrors.changediff.top/zcode/的页面文案里印死了客户端主机名(用法: ...之前写的是具体主机名)。这个页面是公网可访问的,已改为中性的「用法」。 - 文件属主不一致:早期从 root 下载的 rpm 包属
root:root,而 deb 包和索引是www-data:www-data。已统一修正,同步脚本增加chown -R兜底防止复发。
结论
deb 链路(添加源 → apt update → 版本解析 → 下载升级 → 二进制启动)全流程通过。rpm 文件可直接下载安装,但因没有 repodata,dnf/yum 暂不能当仓库用。