brew install 或 upgrade 下载缓慢时,手动下载安装包
摘要
brew install、brew upgrade下载很慢或中途断开时,可以自己把同一个安装包下完,放到 Homebrew 这次要读的缓存文件上,再重新执行原来的命令。下面用本机升级gh2.102.0 作例子。Homebrew7.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。 gh2.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。 - Cask 用同一套缓存目录。路径改成
brew --cache --cask <token>,放进去的是这个 cask 的url指向的那个文件。本机 Firefox 是 dmg,codex是 tar.gz,claude-code没有后缀。例子在文末。
失败时的日志
-
下载停在源码包,formula 脚本本身已经拿到了:
1 | ✘ Formula gh (2.102.0) Downloading 12.8MB/------- |
-
curl: (18)表示收到的长度和响应里声明的不一致。这次传到本地的是半截文件,不能拿去安装。浏览器、带断点续传的curl,或者换一条网络,都可以把同一个地址下完整。
缓存文件名从哪来
-
先看缓存目录,再看这个 formula 从源码构建时要用的那个文件:
1 | brew --cache |
-
本机这两条的结果:
1 | /Users/hanqf/Library/Caches/Homebrew |
-
若设置过
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。 -
gh2.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 | mkdir -p ~/Downloads |
-
-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 | https://github.com/cli/cli/archive/refs/tags/v2.102.0.tar.gz |
-
brew cat gh若还停在旧版本,里面的sha256对不上这次的包。安装走的是 API 里的 2.102.0。 -
挪到缓存路径,再算内容的 SHA-256:
1 | CACHE_PATH="$(brew --cache --build-from-source gh)" |
-
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,要分别放下。gh2.102.0 的resource是空的,这次只需要日志里的那一个 tar.gz。 -
想先看 Homebrew 准备下的地址,可以执行
brew fetch --build-from-source gh。它下成功时会打出 SHA-256。下不完时,仍用上一节的brew info --json=v2读校验和,自己下完再放进缓存。
Cask
-
缓存目录仍是
$(brew --cache)/downloads。文件名还是两段,中间是--。前半段是 cask 里那条 URL 的 SHA-256,后半段是安装包文件名。路径用下面这条。--cask与--build-from-source、--force-bottle互斥,formula 那两个参数在这里不能用:
1 | brew --cache --cask firefox |
-
本机结果:
1 | /Users/hanqf/Library/Caches/Homebrew/downloads/474e2ff1fe5964341f94193795bc51e051af0d70d4098fe5990dbef7e2adfcc2--Firefox 158.0.dmg |
-
474e2ff1fe5964341f94193795bc51e051af0d70d4098fe5990dbef7e2adfcc2是 URL 字符串https://download-installer.cdn.mozilla.net/pub/firefox/releases/158.0/mac/zh-CN/Firefox%20158.0.dmg的 SHA-256。跳转后的地址不参与这段哈希。Firefox 158.0.dmg来自 URL 路径,其中的%20在文件名里是空格。 -
后缀跟 URL 路径最后一段走。本机
codex0.162.1 是 tar.gz:
1 | brew --cache --cask codex |
-
本机结果:
1 | /Users/hanqf/Library/Caches/Homebrew/downloads/9be8bf161f612c15e0003d4c5b13944e869bbb3992e85437a43e38f1cd61d5c9--codex-package-x86_64-apple-darwin.tar.gz |
-
URL 是
https://github.com/openai/codex/releases/download/rust-v0.162.1/codex-package-x86_64-apple-darwin.tar.gz。前半段9be8bf161f612c15e0003d4c5b13944e869bbb3992e85437a43e38f1cd61d5c9是这条 URL 的 SHA-256。内容校验和是894ac20afe819d4fa2a4be8cd27c54fd606338b86036e8ed9b6c9978c2dc7aed。这台是 Intel,包名里是x86_64。缓存里的这份文件已对上该校验和。 -
本机
claude-code2.1.292 的 URL 末尾没有扩展名,缓存文件也没有后缀:
1 | brew --cache --cask claude-code |
-
本机结果:
1 | /Users/hanqf/Library/Caches/Homebrew/downloads/6cbbd982529e2b9557970f25aa883d5a16ee85318b6e298d3d2ab41983eb3984--claude |
-
URL 是
https://downloads.claude.ai/claude-code-releases/2.1.292/darwin-x64/claude。前半段6cbbd982529e2b9557970f25aa883d5a16ee85318b6e298d3d2ab41983eb3984是这条 URL 的 SHA-256。内容校验和是a9739a215728ce72435885fedb19d1317ee1ccec61e246fbfb3acaf01689c473。缓存里的这份文件已对上该校验和,文件类型是 Mach-O 可执行文件。文件名就叫claude,不要另加.zip、.dmg或.tar.gz。 -
地址和内容校验和从当前 API 读。语言和 CPU 已经按本机选好:系统语言是
zh-Hans-CN,所以 URL 里是zh-CN;这台是 Intel,Firefox 用的是mac目录下的包。换语言或换架构后,URL、SHA-256 和缓存路径都会变。
1 | brew info --json=v2 --cask firefox | python3 -c 'import json,sys; c=json.load(sys.stdin)["casks"][0]; print(c["url"]); print(c["sha256"])' |
-
本机输出:
1 | https://download-installer.cdn.mozilla.net/pub/firefox/releases/158.0/mac/zh-CN/Firefox%20158.0.dmg |
-
brew cat firefox若还停在旧版本,里面的sha256对不上这次的包。安装走的是 API 里的 158.0。 -
自己下载。
~/Downloads里的文件名可以自定,下一步mv会按缓存路径改名:
1 | mkdir -p ~/Downloads |
-
放进缓存并核对。路径里有空格,引用时要加引号。中断留下的半截文件仍是同一路径加上
.incomplete:
1 | CACHE_PATH="$(brew --cache --cask firefox)" |
-
shasum的结果要和d0daa923ac5962e1ef0f3f94700e52f2d4ccbba79ae42faa3d76e5bfe5b74370一致。不一致就删掉$CACHE_PATH,重新下载。 -
还没装过:
1 | brew install --cask firefox |
-
已安装、这次是升级:
1 | brew upgrade --cask firefox |
-
校验通过后,Homebrew 不再去下这个 dmg,接着挂载并装进 Caskroom,应用放到
/Applications。Firefox 的auto_updates为 true,装好之后由应用自己更新,brew upgrade默认不会再下新的 dmg。缓存和 Caskroom 的差别见 MacOS软件包管理器--brew。 -
放进去的是 cask
url指向的那一个文件。Firefox 是 dmg,codex是 tar.gz,claude-code是没有后缀的可执行文件。解压或安装之后的.app、以及已经链到$(brew --prefix)/bin的二进制,对不上这个缓存文件,也不要拷进$(brew --caskroom)。 -
JSON 里的
sha256若是no_check,这个 cask 没有内容校验和。官方 cask 会跳过校验。本机steam就是这样,缓存路径仍以brew --cache --cask steam为准。 -
第三方 tap 的 cask 同样先跑
brew --cache --cask <token>。本机已装的redis8.10.2 缓存文件是downloads/54bde208450678f731ccfb0d806ece50048b1cd3c3d416a648a14a359fc8f37b--redis-oss-8.10.2-x86_64.zip,内容 SHA-256 为08334ca85583bc81323be65d22455b0d36d21c3e149c588c911f80284cd8c6b4,与brew info --json=v2 --cask redis里的一致。 -
url_specs里若有 cookie、referer 或 header,自己下载时要带上同样的字段,否则拿到的可能是另一份内容。Firefox 这份url_specs是空的。 -
想让 Homebrew 自己下载并打印 SHA-256,可以执行
brew fetch --cask firefox。下不完时,仍用上面的brew info --json=v2 --cask读校验和,自己下完再放进缓存。