1. 程式人生 > >homebrew 安裝 formula 的不同歷史版本——以安裝 node 為例

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 更新到最新版本,於是我遇到了一些問題:

  1. 一些需要編譯安裝的依賴包還不支援 [email protected]
  2. 不知道如何自動切換兩個 formula ( node 和 [email protected] )之間的可執行檔案;
  3. 是否能夠設定某個 formula 不自動更新?

在經過一番折騰後,現在我刪掉了 node 和 [email protected] 兩個 formula ,包括之前安裝的歷史版本(所以算是多走了一點彎路),想要同時保留 10 和 11 兩個大版本,並可方便切換。

前置條件

若下列條件不滿足,可能導致需要額外的操作來解決環境問題,這些操作不在本文討論範圍內:

  1. 只通過 homebrew 來安裝 node ;
  2. 沒有使用如 n / nvm 等 node 版本管理工具;
  3. 某種意義下連線性良好的網路。

適用情況

本文覆蓋瞭如下四種情況:

  1. 某個 formula 跟隨著更新上來,沒有執行過 cleanup 或 force uninstall ,想切回舊版本的(見關鍵點三);
  2. 全新安裝且只想安裝某個 formula (如 node 而非 [email protected] ),但又想先用著舊版本的(見解決方案一);
  3. 某個 formula 在用的是舊版本,但想切換編譯選項(如 [email protected] )重新安裝(同上);
  4. 在兩個 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 的歷史版本

  1. 切換到 brew 的倉庫目錄下

    $ cd "$(brew --repo homebrew/core)"
  2. 檢視某個 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.
  3. 檢出 git 歷史提交

    $ git checkout b801cc6b71e7c09448b4f823e493710665de68eb
  4. 在禁止自動更新的前提下安裝這個 formula

    $ HOMEBREW_NO_AUTO_UPDATE=1 brew install node [email protected]
  5. 由於剛才第 3 步後處於分離頭指標狀態下,記得要切回來

    $ git checkout master
  6. 升級這個 formula 試試

    $ brew upgrade node
  7. 檢查當前版本,應該是最新的,現在再把它切回來

    $ 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 的連結),這個警告可以不去理會,但必須記住我們做過這樣的操作。

  1. 假定已經安裝了 node ,現在安裝 [email protected] ,鍵盤一頓亂敲

    $ brew install [email protected]

    通常像這樣名字後面帶有版本號的 formula 都是 keg-only 的,簡單來說就是 brew 不會自動幫我們連結檔案

  2. 先解除 node 的檔案連結 shell $ brew unlink node

  3. 連結 [email protected] 的檔案

    $ brew link [email protected]

    這時會提示這個包是 keg-only 的,需要加上 --force 選項。如果第 2 步沒有進行,還會提示你需要先解除 node 的連結,或者直接用 --overwrite 選項覆蓋原有的連結。由於 npm 目錄一定需要覆蓋,所以我們總是可以開啟 --overwrite 選項:

    $ brew link [email protected] --force --overwrite
  4. 檢查當前版本,在解決方案一中提到過了,方法也有很多種,此處略過。

後續問題

  1. 全域性安裝的包支援的 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 結合起來。

  2. 如何固定某個 formula 的版本? 雖然說不執行 brew upgrade 就一直是固定的版本,但如果想只固定某個 formula 的版本而允許其他可以升級,也是極好的。自帶的 pin 、 unpin 命令就是用來解決這一問題,如: brew pin node

  3. 如何刪除某個 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

    初步測試未發現有副作用,歡迎討論和建議。

  4. 如何在升級時自動刪除舊版本? 根據 brew upgrade 命令的說明,設定一個 HOMEBREW_UPGRADE_CLEANUP 環境變數就好了,比較簡單。

參考資料