brew install 或 upgrade 下载缓慢时,手动下载安装包

摘要

  • brew install、brew upgrade 下载很慢或中途断开时,可以自己把同一个安装包下完,放到 Homebrew 这次要读的缓存文件上,再重新执行原来的命令。下面用本机升级 gh 2.102.0 作例子。Homebrew 7.0.8,缓存目录是 ~/Library/Caches/Homebrew。这次 GitHub 上的源码包下到一半就断了:curl: (18) Transferred a partial file。
  • 可以自己把同一个压缩包下完,再放到 Homebrew 这次要读的缓存文件上。放进去的必须是 formula 里那个 URL 的文件,SHA-256 也要一致。文件名以 brew --cache --build-from-source gh 的输出为准,不能自己编哈希,也不能拷进 $(brew --cellar)/gh。
  • gh 2.102.0 在当前 API 里没有 bottle,所以这次下的就是源码包 https://github.com/cli/cli/archive/refs/tags/v2.102.0.tar.gz,校验和是 08bf0ef8b4409893889175e0f5279d30d6edd96465816042c0d7c72eac598158。本机缓存文件名是 downloads/838ae966d01d820dbc2c41ab1f82d72064fe030ca19254fb2ff3aa993768b18d--cli-2.102.0.tar.gz。前半段是这条 URL 的 SHA-256,后半段来自下载响应里的文件名。
  • 校验通过后再执行原来的 brew upgrade gh 或 brew install gh。Homebrew 会接着做自己的校验和编译。缓存和 Cellar 的差别见 MacOS软件包管理器--brew。

失败时的日志

  • 下载停在源码包,formula 脚本本身已经拿到了:

1
2
3
4
✘ Formula gh (2.102.0)                                          Downloading    12.8MB/-------
Error: Failed to download resource "gh (2.102.0)"
Download failed: https://github.com/cli/cli/archive/refs/tags/v2.102.0.tar.gz
curl: (18) Transferred a partial file
  • curl: (18) 表示收到的长度和响应里声明的不一致。这次传到本地的是半截文件,不能拿去安装。浏览器、带断点续传的 curl,或者换一条网络,都可以把同一个地址下完整。

缓存文件名从哪来

  • 先看缓存目录,再看这个 formula 从源码构建时要用的那个文件:

1
2
brew --cache
brew --cache --build-from-source gh
  • 本机这两条的结果:

1
2
3
/Users/hanqf/Library/Caches/Homebrew

/Users/hanqf/Library/Caches/Homebrew/downloads/838ae966d01d820dbc2c41ab1f82d72064fe030ca19254fb2ff3aa993768b18d--cli-2.102.0.tar.gz
  • 若设置过 HOMEBREW_CACHE,目录会不同,以 brew --cache 为准。

  • 文件名分两段,中间是 --。838ae966d01d820dbc2c41ab1f82d72064fe030ca19254fb2ff3aa993768b18d 是 formula URL 的 SHA-256,不是压缩包内容的校验和。cli-2.102.0.tar.gz 是 GitHub 返回的文件名:仓库是 cli/cli,归档名用仓库名而不是 formula 名 gh。官方说明里写成 <url-hash>--<formula>-<version>,见 Pre-download a file for a formula。本机实际后缀是 cli-2.102.0.tar.gz。

  • gh 2.102.0 没有 bottle,所以不加 --build-from-source 时,brew --cache gh 打出来的也是这个源码包路径。有 bottle 的 formula 不一样:不加这个参数时路径指向 bottle,源码包必须加 --build-from-source。把源码包放到 bottle 的路径上,安装时对不上。

  • 中断的传输会留在同一目录,文件名多一个 .incomplete。那是半截文件。完整包要放在没有这个后缀的路径上。

自己下载

  • 地址就是日志里的那一条:

1
https://github.com/cli/cli/archive/refs/tags/v2.102.0.tar.gz
  • 浏览器下到 ~/Downloads 即可。用 curl 时打开断点续传:

1
2
3
4
5
mkdir -p ~/Downloads
curl -fL --retry 5 --retry-all-errors \
-C - \
-o ~/Downloads/gh-2.102.0.tar.gz \
'https://github.com/cli/cli/archive/refs/tags/v2.102.0.tar.gz'
  • -C - 从已有文件的末尾接着下,服务器要支持 Range。本地这个文件叫 gh-2.102.0.tar.gz 只是为了好认,和缓存文件名不必相同。下一步 mv 会按缓存路径改名。

放进缓存并核对校验和

  • 校验和从当前 API 里的 formula 读,版本以报错里的 2.102.0 为准:

1
brew info --json=v2 gh | python3 -c 'import json,sys; u=json.load(sys.stdin)["formulae"][0]["urls"]["stable"]; print(u["url"]); print(u["checksum"])'
  • 本机输出:

1
2
https://github.com/cli/cli/archive/refs/tags/v2.102.0.tar.gz
08bf0ef8b4409893889175e0f5279d30d6edd96465816042c0d7c72eac598158
  • brew cat gh 若还停在旧版本,里面的 sha256 对不上这次的包。安装走的是 API 里的 2.102.0。

  • 挪到缓存路径,再算内容的 SHA-256:

1
2
3
4
CACHE_PATH="$(brew --cache --build-from-source gh)"
rm -f "${CACHE_PATH}.incomplete"
mv ~/Downloads/gh-2.102.0.tar.gz "$CACHE_PATH"
shasum -a 256 "$CACHE_PATH"
  • shasum 的结果要和上面的 08bf0ef8b4409893889175e0f5279d30d6edd96465816042c0d7c72eac598158 一致。不一致就删掉 $CACHE_PATH,重新下载。Homebrew 自己校验失败时也会提示删掉这个文件。

继续安装

  • 还没装过:

1
brew install gh
  • 已安装、这次是升级:

1
brew upgrade gh
  • 缓存里的文件校验通过后,Homebrew 不再去下这个 tar.gz,接着解压、编译、装进 Cellar。这一步仍然要本地编译。Intel 上不再提供 bottle 的说明见 MacOS软件包管理器--brew 文末「从 4.5 到 7.0」。

其他 formula

  • 每次都先跑 brew --cache --build-from-source <formula>,把文件放到它打印的那一条路径上。

  • 日志里的 URL 若是 ghcr.io 上的 bottle,放进去的应是那个 bottle,路径用 brew --cache --force-bottle <formula>。源码包不能顶替 bottle。

  • brew --cache 只打印 formula 自己的那一个文件。formula 里若还有 resource,每个资源有自己的 URL、缓存路径和 SHA-256,要分别放下。gh 2.102.0 的 resource 是空的,这次只需要日志里的那一个 tar.gz。

  • 想先看 Homebrew 准备下的地址,可以执行 brew fetch --build-from-source gh。它下成功时会打出 SHA-256。下不完时,仍用上一节的 brew info --json=v2 读校验和,自己下完再放进缓存。