先测速,再下载。GitHub 打不开、龟速、断流?它自己会换一条路。
一个零依赖的 GitHub 下载器:并发探测直连与多个国内镜像 → 挑最快的一条 → 多线程分段并行下载 → 断点续传 → 校验完整性。
在国内直连 github.com 下载 release 资产,常见的结果是:连接超时、几十 KB/s 龟速爬行、传到 90% 断流,或者镜像站悄悄给你返回一个 2 KB 的 HTML 拦截页,被你当成 zip 存了下来。
github-download 把这件事变成一次「测量 → 决策 → 传输」:
| 你会遇到的坑 | 它的做法 |
|---|---|
| 直连 GitHub 超时 | 并发探测直连 + 9 个国内镜像,谁快用谁 |
| 镜像站龟速,把整个命令卡死 | 每次探测都有墙钟预算,慢的端点直接标记 too slow 淘汰 |
| 镜像限速,单线程跑不满带宽 | 默认 8 线程分段并行,Range 请求写进 .part 的不同偏移 |
| 传到一半断了,重来一次 | 块级续传(OUT.part.ghdl.json),重跑同一条命令接着传 |
| 镜像偷偷返回 HTML 拦截页 | 识别 challenge 页面,拒绝把网页存成 xxx.zip |
| 镜像谎报文件大小,静默截断 | 多数票共识尺寸 + 传输后长度校验,不一致就退出码 5 并保留 .part |
| 分不清文件是不是完整 | 自动打印 SHA256(≤256 MB),支持 --sha256 严格校验 |
| 能力 | |
|---|---|
| 🎯 | 测速后择优:并发抓 1 MB 样本,再对头部端点做「多线程深度测速」,直连只有在 ≥ --min-speed 时才被选中 |
| 🚀 | 多线程分段:默认 8 线程(-t auto 可按实测吞吐自动调参),支持 1 MB ~ 32 MB 的分段粒度规划 |
| ♻️ | 断点续传:分段级进度状态落盘,Ctrl+C 或断网后重跑原命令即可续传;--no-resume 可强制重来 |
| 🛡️ | 拒绝静默损坏:尺寸共识、长度校验、HTML 拦截页识别,任何一环不对都会报错而不是留个坏文件 |
| 🔁 | 自动故障转移:下载中某个镜像挂了或变慢,未完成的段自动切到备选端点 |
| 📦 | 仓库也能加速:clone 通过镜像 3 秒拉完的仓库,直连可能要 30 秒以上;支持 --depth / --branch / --blobless |
| 🪶 | 零依赖:只用 Python 标准库(urllib + threading),Python 3.8+ 直接跑,不需要 aria2/wget/requests |
| 🤖 | 可当 Codex 技能用:附带 SKILL.md,装进 ~/.codex/skills/ 后,Codex 会在需要下载 GitHub 内容时自动调用 |
| 📊 | 机器可读输出:--json 输出每个端点的测速结果、最终字节数、耗时与哈希;--dry-run 只测速不下载 |
方式一:作为 Codex 技能(推荐)
git clone https://github.com/AaronShawn/github-download.git ~/.codex/skills/github-downloadWindows PowerShell:
git clone https://github.com/AaronShawn/github-download.git "$env:USERPROFILE\.codex\skills\github-download"装好后 重启 Codex 或新开一个会话,技能才会被发现。之后直接说「帮我下载这个 GitHub release」它就会用上。
方式二:当作普通脚本用
git clone https://github.com/AaronShawn/github-download.git
python github-download/scripts/github_download.py get <URL>没有任何第三方依赖,Python 3.8+ 即可。
# 1. 下载一个 release 资产(默认 8 线程,自动择优镜像)
python scripts/github_download.py get \
https://github.com/neovim/neovim/releases/download/v0.10.4/nvim-win64.zip
# 2. 只看测速结果,什么都不下载
python scripts/github_download.py probe \
https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-x86_64-pc-windows-msvc.zip
# 3. 克隆仓库(走镜像,支持浅克隆)
python scripts/github_download.py clone BurntSushi/ripgrep ripgrep --depth 1
# 4. 查看 / 重新测速镜像清单
python scripts/github_download.py mirrors --test典型输出:
== probe: 9 endpoints, 1.0 MB sample, 8.0s timeout ==
gh-proxy.com 206 range=yes 1.0 MB 0.86s 1.16 MB/s
direct 206 range=yes 1.0 MB 1.09s 0.91 MB/s
ghfast.top 206 range=yes 1.0 MB 2.55s 0.39 MB/s
gh.ddlc.top 206 range=yes 1.0 MB 3.51s 0.28 MB/s
ghproxy.net FAIL 8.03s too slow: 367.1 KB in 8.0s (budget 8.0s)
ghproxy.link FAIL 1.31s HTML page, not the file
hk.gh-proxy.com FAIL 0.80s network error: CERTIFICATE_VERIFY_FAILED
== size: 12.2 MB (agreed by 5 endpoint(s)) ==
== deep probe: up to 2.0 MB per endpoint, 8 threads, serial ==
gh-proxy.com 206 range=yes 2.0 MB 0.88s 2.29 MB/s [2x]
direct 206 range=yes 2.0 MB 13.73s 0.15 MB/s [2x]
== chosen: gh-proxy.com (direct slow: 0.15 MB/s < 2.00 MB/s) ==
== downloading with segmented (8 threads) ==
9 segments of 1.5 MB across 8 connections
sha256: dceeb8301f64e244e3e2dffaedbb153bd01c0c6ecb5024a90e3172dc8e65555c
downloaded nvim-win64.zip (12.2 MB) via gh-proxy.com in 1.9s - 6.55 MB/s
下面全部是真实跑出来的数字,不是估算(2026-09,中国大陆家宽,直连 GitHub 未挂代理)。
单文件下载(neovim v0.10.4 / nvim-win64.zip,12.2 MB)
| 方式 | 耗时 | 平均速度 |
|---|---|---|
| 直连 GitHub | 经常超时 / 0.1–0.2 MB/s | ✗ |
| 镜像单线程 | ~5.2s | 2.33 MB/s |
| 8 线程分段(本工具) | 1.9s | 6.55 MB/s |
并发数对吞吐的影响(同一镜像、同一文件)
| 线程数 | 1 | 2 | 4 | 8 |
|---|---|---|---|---|
| 吞吐 | 2.3 MB/s | 4.6 MB/s | 7.9 MB/s | 6.5–8.1 MB/s |
单线程被镜像限速卡住时,分段并行能直接把带宽吃满;线程再往上加收益递减,所以默认值是 8 而不是越大越好。
git clone 对比(同一仓库、同一网络)
| 方式 | 耗时 |
|---|---|
直连 git ls-remote |
93s |
| 直连 clone | 32.8s |
| 镜像 clone(本工具) | 3.0s |
各镜像当前状态(同一次探测,1 MB 样本)
| 端点 | 状态 | 1 MB 样本 | 说明 |
|---|---|---|---|
gh-proxy.com |
✅ | 0.86s / 1.16 MB/s | 本地最快,8 线程可达 6.5–8.1 MB/s,支持 clone |
direct |
1.09s 样本,深度测速 0.15 MB/s | 小样本能过,真跑起来掉速 | |
ghfast.top |
✅ | 2.55s / 0.39 MB/s | 稳定但不算快 |
ghfile.geekertao.top |
✅ | 2.73s / 0.37 MB/s | 稳定 |
gh.ddlc.top |
✅ | 3.51s / 0.28 MB/s | 稳定 |
ghproxy.net |
❌ | 8.0s 才拿到 367 KB | 触发超时预算,被淘汰 |
ghproxy.link |
❌ | 返回 HTML 页 | 拦截页,直接拒绝 |
gitproxy.click |
❌ | 返回 HTML 页 | 拦截页,直接拒绝 |
hk.gh-proxy.com |
❌ | TLS 证书校验失败 | 需 --insecure,默认不信任 |
镜像可用性和速度随时间、地区、运营商变化。每次运行都会重新测速,不依赖上面这张表;也欢迎
mirrors --test后提交你的结果。
flowchart LR
A[输入 URL] --> B[生成候选端点<br/>直连 + 镜像列表]
B --> C[并发样本探测<br/>1 MB, 带墙钟预算]
C --> D{尺寸共识<br/>多数票}
D --> E[前 3 名 + 直连<br/>多线程深度测速]
E --> F[决策: 快且可信的胜出]
F --> G{支持 Range?}
G -- 是 --> H[8 线程分段下载<br/>写入 .part 的不同偏移]
G -- 否 --> I[单流下载<br/>+ 断点续传]
H --> J[尺寸 / HTML / SHA256 校验]
I --> J
J --> K[原子替换为最终文件]
- 候选生成 —— 把一个 GitHub URL 展开成一组端点:直连、前缀镜像(
https://gh-proxy.com/<原URL>)、git 模板、jsDelivr raw 模板。 - 样本探测 —— 并发对每个端点拉 1 MB(
--quick为 256 KB),带墙钟预算。读取用的是read1(),涓流式镜像没法用「每次 recv 都没超时」来蒙混过关,超预算即淘汰。 - 尺寸共识 —— 各端点上报的文件长度取多数票,直连用于打破平局。少数派被标记为「谎报长度」并排到队尾;如果它实际上能给出正确字节,就降级成单流下载。
- 深度测速 —— 前几名加上直连,用和真实下载相同的线程数串行测一遍(串行避免互相抢带宽导致数据失真),小文件则跳过(
--quick-below,默认 8 MB)。 - 决策 —— 直连只有在可用且 ≥
--min-speed(默认 2.0 MB/s)时才被选中;镜像比健康的直连快 1.3 倍以上也会胜出。 - 分段传输 —— 按大小规划 1 MB–32 MB 的段,多线程 Range 写入
OUT.part;每完成一段就把进度写进OUT.part.ghdl.json。某端点连续失败 2 次就轮换到下一名;谎报长度的端点直接拉黑。 - 收尾校验 —— 长度必须等于共识尺寸(否则退出码 5,保留
.part供续传),开头 512 字节不能像 HTML,--sha256严格校验,最后os.replace()原子落盘。
| 参数 | 默认 | 说明 |
|---|---|---|
-t, --threads N|auto |
8 |
分段线程数;auto 会按实测吞吐自动选择 |
--timeout SEC |
30 |
单次网络操作超时 |
--retries N |
3 |
每个段的尝试次数 |
--sha256 HASH |
— | 严格校验,失败退出码 4 |
--sha256-max MB |
256 |
超过此大小不自动算哈希 |
--mirrors PATH |
内置 | 自定义镜像清单(也可用 $GH_DOWNLOAD_MIRRORS) |
--add-mirror PREFIX |
— | 本次运行临时追加镜像前缀 |
--mirror-only / --no-mirror |
— | 只用镜像 / 完全不走镜像 |
--proxy URL / --no-env-proxy |
— | 指定代理 / 忽略环境变量里的代理 |
--insecure |
— | 跳过 TLS 校验(仅对付证书过期的镜像) |
--token TOKEN |
$GITHUB_TOKEN |
只发给 github.com,不会泄露给镜像 |
--json / --quiet / --progress / --dry-run |
— | 机器可读输出 / 静默 / 强制进度条 / 只测速不下载 |
--quick / --quick-below MB |
8 |
用小样本快速探测 / 小于该大小跳过深度测速 |
--no-resume |
— | 忽略已有的 .part,从头下载 |
python scripts/github_download.py get <URL> [-o OUT]python scripts/github_download.py clone <OWNER/REPO|URL> [DIR] [--depth N|--full] [--branch NAME] [--blobless] [--attempts N]--depth 1 是默认值(浅克隆最快);--full 拉完整历史。
| 码 | 含义 |
|---|---|
0 |
成功 |
2 |
参数 / 没有可用端点 |
3 |
所有端点都失败 |
4 |
SHA256 校验失败 |
5 |
尺寸校验失败或拿到 HTML 页(.part 会保留) |
130 |
被 Ctrl+C 中断(重跑可续传) |
内置清单放在 scripts/mirrors.json,每次运行都会读取,加镜像 / 调顺序不需要改代码:
{
"prefix_mirrors": [
{ "name": "gh-proxy.com", "url": "https://gh-proxy.com/", "git": true }
],
"git_templates": [
{ "name": "gitclone.com", "template": "https://gitclone.com/github.com/{owner}/{repo}.git" }
],
"raw_templates": [
{ "name": "jsdelivr-cdn", "template": "https://cdn.jsdelivr.net/gh/{owner}/{repo}@{ref}/{path}" }
]
}字段含义:git: true 表示该前缀也能用于 clone;range: false 表示它忽略 Range 请求,只能单流下载;insecure: true 表示证书过期需要 --insecure。
仓库根的 SKILL.md 就是技能说明书。把它放进 ~/.codex/skills/github-download/(Windows 为 %USERPROFILE%\.codex\skills\github-download\),新开一个会话后,Codex 在遇到「下载 GitHub release 很慢」「clone 一直卡住」这类需求时会自动调用本技能,并把测到的端点与速度汇报给你。
目录结构:
github-download/
├── SKILL.md # Codex 技能说明(决策规则、注意事项)
├── agents/openai.yaml # 技能展示信息
├── scripts/
│ ├── github_download.py # 全部实现,单文件,零依赖
│ └── mirrors.json # 镜像清单(可自行增删排序)
└── README.md
Q:会把我下载的私有文件内容传给镜像站吗?
会经过镜像站中转。私有仓库、带 token 的 URL、内部资产请加 --no-mirror。脚本里的 --token / GITHUB_TOKEN 只会发送给 github.com,不会发给任何镜像。
Q:下载完要不要自己核对哈希?
≤256 MB 的文件默认会打印 SHA256,用 --sha256 <官方哈希> 可以强制校验并在不匹配时退出码 4。镜像站是三方的,安全相关的产物建议务必核对。
Q:中断了要重新下载吗?
不用。重跑完全相同的命令即可,它会从 OUT.part + OUT.part.ghdl.json 接着下。想重来就加 --no-resume。
Q:为什么有时速度还是上不去?
单线程上限取决于镜像本身(比如 gh-proxy.com 单流约 2.3 MB/s),分段并行才能叠加。如果所有端点都被限速,就只能降低期望,或者配上代理用 --proxy。用 probe 看具体是谁快。
Q:小文件(几 KB)为什么测速都是 0.00 MB/s?
样本量太小,除以耗时后四舍五入到 0,排名在这种情况下基本是随机的,但正确性不受影响——尺寸共识和长度校验照常生效。
Q:Windows 上要不要装什么?
不用。有 Python 3.8+ 就行;脚本只用标准库,git 只在 clone 子命令里用到。
- 发现某个镜像挂了或变快变慢了 → 直接改
scripts/mirrors.json提 PR,并附上mirrors --test的输出。 - 报告问题时请附上
probe <URL> --json的结果,里面有每个端点的原始耗时。
MIT © github-download contributors