homebrew 安裝 formula 的不同歷史版本——以安裝 node 為例
系統環境
- macOS Mojave 10.14
- Homebrew 1.8.0
- Homebrew/homebrew-core (git revision 586b0f; last commit 2018-10-27)
- Homebrew/homebrew-cask (git revision 76ddc; last commit 2018-10-27)
背景
最近 nodejs 釋出了 11.0.0 版本,而我是用 brew 安裝了名為 node 的這個 formula ,現在 brew upgrade
會自動將 node 更新到最新版本,於是我遇到了一些問題:
- 一些需要編譯安裝的依賴包還不支援 [email protected]
- 不知道如何自動切換兩個 formula ( node 和 [email protected] )之間的可執行檔案;
- 是否能夠設定某個 formula 不自動更新?
在經過一番折騰後,現在我刪掉了 node 和 [email protected] 兩個 formula ,包括之前安裝的歷史版本(所以算是多走了一點彎路),想要同時保留 10 和 11 兩個大版本,並可方便切換。
前置條件
若下列條件不滿足,可能導致需要額外的操作來解決環境問題,這些操作不在本文討論範圍內:
- 只通過 homebrew 來安裝 node ;
- 沒有使用如 n / nvm 等 node 版本管理工具;
- 某種意義下連線性良好的網路。
適用情況
本文覆蓋瞭如下四種情況:
- 某個 formula 跟隨著更新上來,沒有執行過 cleanup 或 force uninstall ,想切回舊版本的(見關鍵點三);
- 全新安裝且只想安裝某個 formula (如 node 而非 [email protected] ),但又想先用著舊版本的(見解決方案一);
- 某個 formula 在用的是舊版本,但想切換編譯選項(如
[email protected]
)重新安裝(同上); - 在兩個 formula 之間切換,如一個是 LTS 版的,一個是不穩定版的(見解決方案二)。
關鍵點
如何在 brew install / upgrade 時禁止自動更新
這一步很關鍵,否則我們總是會安裝到最新版本。只要在執行命令前,設定環境變數 HOMEBREW_NO_AUTO_UPDATE 為 1 就可以了,比如:
$ HOMEBREW_NO_AUTO_UPDATE=1 brew install node
也可以直接寫進 shell 配置檔案中(如 .bashrc / .zshrc 等):
export HOMEBREW_NO_AUTO_UPDATE=1
檢視某個 formula 本地已安裝的版本
有兩種方式可檢視,一是 brew list --versions
,輸出以空格分隔,簡潔明瞭,推薦使用:
$ brew list node --versions
node 11.0.0 10.12.0
$ brew list zsh --versions |tr ' ' '\n'
node
11.0.0
10.12.0
二是 brew info
在檢視 formula 資訊時也可看到本地已安裝的版本:
$ brew info node
$ brew info node |grep -i '\/cellar'
/usr/local/Cellar/node/10.12.0 (3,668 files, 41.7MB)
/usr/local/Cellar/node/11.0.0 (3,665 files, 42MB) *
後面帶有的星號 * 表示當前啟用的版本。
切換某個 formula 的版本
自有的 brew switch
命令即可,之前感覺 brew 較易上手,安裝後沒看文件直接用到現在,所以不知道這個命令。命令的說明也很直觀:
brew switch formula version:
Symlink all of the specific version of formula's install to Homebrew prefix.
直接鍵盤一頓敲就行了,如 brew switch node 10.12.0
。
解決方案
安裝某個 formula 的歷史版本
切換到 brew 的倉庫目錄下
$ cd "$(brew --repo homebrew/core)"
檢視某個 formula 的 git 歷史記錄
$ git log master -- Formula/node.rb
在裡面找到想要的歷史版本,比如這個就挺像我所需要的:
commit b801cc6b71e7c09448b4f823e493710665de68eb Author: BrewTestBot <[email protected]> Date: Thu Oct 11 00:12:43 2018 +0000 node: update 10.12.0 bottle.
檢出 git 歷史提交
$ git checkout b801cc6b71e7c09448b4f823e493710665de68eb
在禁止自動更新的前提下安裝這個 formula
$ HOMEBREW_NO_AUTO_UPDATE=1 brew install node [email protected]
由於剛才第 3 步後處於分離頭指標狀態下,記得要切回來
$ git checkout master
升級這個 formula 試試
$ brew upgrade node
檢查當前版本,應該是最新的,現在再把它切回來
$ node -v v11.0.0 $ brew switch node 10.12.0 Cleaning /usr/local/Cellar/node/10.12.0 Cleaning /usr/local/Cellar/node/11.0.0 7 links created for /usr/local/Cellar/node/10.12.0 $ node -v v10.12.0
在兩個 formula 之間切換
我們也可以在 node 和 [email protected] 兩個 formula 之間切換檔案連結。但是注意,這種方法可能會在執行 brew doctor
時產生警告,因為可能會有一個不是 keg-only 的包沒有被連結到 Cellar 中( 如為了使用 [email protected] 解除了 node 的連結),這個警告可以不去理會,但必須記住我們做過這樣的操作。
假定已經安裝了 node ,現在安裝 [email protected] ,鍵盤一頓亂敲
$ brew install [email protected]
通常像這樣名字後面帶有版本號的 formula 都是 keg-only 的,簡單來說就是 brew 不會自動幫我們連結檔案
先解除 node 的檔案連結
shell $ brew unlink node
連結 [email protected] 的檔案
$ brew link [email protected]
這時會提示這個包是 keg-only 的,需要加上 --force 選項。如果第 2 步沒有進行,還會提示你需要先解除 node 的連結,或者直接用 --overwrite 選項覆蓋原有的連結。由於 npm 目錄一定需要覆蓋,所以我們總是可以開啟 --overwrite 選項:
$ brew link [email protected] --force --overwrite
檢查當前版本,在解決方案一中提到過了,方法也有很多種,此處略過。
後續問題
全域性安裝的包支援的 node 版本可能不同 是的,這個問題一定會存在,建議通過切換 npm prefix 路徑解決,把不同版本 node 下安裝的全域性包隔離開來。當然 nvm 等方案也可以解決問題,但本文是不一樣的煙火。
$ mkdir -p ~/.npm-node10/npm-global $ npm config set prefix ~/.npm-node10/npm-global $ export PATH="$HOME/.npm-node10/npm-global/bin:$PATH"
手動切換確實比較繁瑣,可以寫個 shell 指令碼把切換 formula 和切換 npm prefix 結合起來。
如何固定某個 formula 的版本? 雖然說不執行
brew upgrade
就一直是固定的版本,但如果想只固定某個 formula 的版本而允許其他可以升級,也是極好的。自帶的 pin 、 unpin 命令就是用來解決這一問題,如:brew pin node
。如何刪除某個 formula 的舊版本? 儘管
brew cleanup
可以刪除一個 formula 的所有舊版本,但通常我們可能只想刪除某幾個太舊且多餘的版本而不是全部。 brew 沒有提供這樣的命令,我們可以直接從 Cellar 裡面刪除:$ brew info node |grep -i '\/cellar' /usr/local/Cellar/node/10.12.0 (3,668 files, 41.7MB) /usr/local/Cellar/node/11.0.0 (3,665 files, 42MB) * $ rm -rf /usr/local/Cellar/node/10.12.0
初步測試未發現有副作用,歡迎討論和建議。
如何在升級時自動刪除舊版本? 根據
brew upgrade
命令的說明,設定一個 HOMEBREW_UPGRADE_CLEANUP 環境變數就好了,比較簡單。