Bryan

Bryan

twitter
medium

同時安裝多版本 pnpm & 發佈自訂 homebrew 包

如果你只是想找一個利用 Homebrew 安裝 pnpm v7 的方案,執行 brew install ImSingee/pnpm/pnpm@7 即可

pnpm v8 在一個月前發布了,作為大版本更新之一,它引入了 lock file V6,同時停止支持了 V5。然而在給一些使用舊版本的項目提 pr 的情況下,如果需要引入新的依賴就勢必需要更新 lock file —— 這是不可被接受的,不能直接期望所有協作者都升級其 pnpm。

這引入了一個 pnpm V7 和 V8 共存的問題。這本不是難事,有著 Corepack 或者 pnvm 等工具。然而其對我而言都太重了 —— 一個 Homebrew 似乎就夠了。

Homebrew 一個問題是,不支持安裝舊版本,曾經引入過的 homebrew/versions 也早已被棄用,官方唯一建議的方案是自行托管 —— 當然,目前 pnpm 是沒有維護官方舊版本的 tap 的,因此,只能自己動手喽。

創建 tap#

根據官方指引,執行下面的命令即可創建一個空的 tap 庫

brew tap-new ImSingee/homebrew-pnpm

注:這裡 tap-new 後面的參數格式必須為 <repo>/homebrew-<name> ,這樣可以通過 brew tap <repo>/<name> 來啟用這個 tag,後續可以通過 brew install <repo>/<name>/<formula> 來直接安裝相關包【另外實際上,如果 repo 名稱不以 homebrew- 開頭,這個命令會自動幫你加上這個前綴的】

然後會打印出類似下面的信息

Initialized empty Git repository in /opt/homebrew/Library/Taps/imsingee/homebrew-pnpm/.git/
[main (root-commit) 1b89b92] Create imsingee/pnpm tap
 3 files changed, 90 insertions(+)
 create mode 100644 .github/workflows/publish.yml
 create mode 100644 .github/workflows/tests.yml
 create mode 100644 README.md
==> Created imsingee/pnpm
/opt/homebrew/Library/Taps/imsingee/homebrew-pnpm

When a pull request making changes to a formula (or formulae) becomes green
(all checks passed), then you can publish the built bottles.
To do so, label your PR as `pr-pull` and the workflow will be triggered.

大體意思就是,幫你在 /opt/homebrew/Library/Taps/imsingee/homebrew-pnpm 下創建了一個項目,並配置好了 GitHub Action 幫你測試、配置 bottle

增加一個 formula#

我們新增一個 pnpm@7 的 Formula,這裡是官方的 pnpm.rb

class Pnpm < Formula
  require "language/node"

  desc "📦🚀 Fast, disk space efficient package manager"
  homepage "https://pnpm.io/"
  url "https://registry.npmjs.org/pnpm/-/pnpm-8.3.1.tgz"
  sha256 "ce038ba2617f7a93d0b1f24b733b9d64258b15c97a14c6f37673c8d49e033d9a"
  license "MIT"

  livecheck do
    url "https://registry.npmjs.org/pnpm/latest"
    regex(/["']version["']:\s*?["']([^"']+)["']/i)
  end

  bottle do
    sha256 cellar: :any_skip_relocation, arm64_ventura:  "78ecd13f60c3baf6913933c8494ca17fc4e5b9f93c46bbc131312ffe41fe7f88"
    sha256 cellar: :any_skip_relocation, arm64_monterey: "78ecd13f60c3baf6913933c8494ca17fc4e5b9f93c46bbc131312ffe41fe7f88"
    sha256 cellar: :any_skip_relocation, arm64_big_sur:  "78ecd13f60c3baf6913933c8494ca17fc4e5b9f93c46bbc131312ffe41fe7f88"
    sha256 cellar: :any_skip_relocation, ventura:        "2f4f18876a3e2823f86f5500b7c47c173695e7f21eba007c2b7689dd12301145"
    sha256 cellar: :any_skip_relocation, monterey:       "2f4f18876a3e2823f86f5500b7c47c173695e7f21eba007c2b7689dd12301145"
    sha256 cellar: :any_skip_relocation, big_sur:        "4be656f6ff04e145810fb6e19f08fb01030798cec610c9d618b1fb01121d9f64"
    sha256 cellar: :any_skip_relocation, x86_64_linux:   "78ecd13f60c3baf6913933c8494ca17fc4e5b9f93c46bbc131312ffe41fe7f88"
  end

  depends_on "node" => :test

  conflicts_with "corepack", because: "both installs `pnpm` and `pnpx` binaries"

  def install
    libexec.install buildpath.glob("*")
    bin.install_symlink "#{libexec}/bin/pnpm.cjs" => "pnpm"
    bin.install_symlink "#{libexec}/bin/pnpx.cjs" => "pnpx"
  end

  def caveats
    <<~EOS
      pnpm requires a Node installation to function. You can install one with:
        brew install node
    EOS
  end

  test do
    system "#{bin}/pnpm", "init"
    assert_predicate testpath/"package.json", :exist?, "package.json must exist"
  end
end

我們要進行幾個小的修改

  • 檔案名官方為 pnpm.rb,我們這裡要改名為 pnpm@7.rb 以示區分
  • class 名稱 Pnpm 我們需要修改成 PnpmAT7
  • url 這裡我們要修改成我們所需的版本,目前 7 最新的版本是 7.32.2
  • sha256 我們這裡需要修改成對應版本的 hash,目前 7.32.2 對應的是 f4b40caa0c6368da2f50b8ef891f225c24f14e7d60e42a703c84d3a9db8efede
  • livecheck 節下的 url 我們修改成 https://registry.npmjs.org/pnpm、regex 為 /["']latest-7["']:\s*?"'["']/i
  • 移除 bottle

最終修改好的版本是

class PnpmAT7 < Formula
    require "language/node"
  
    desc "📦🚀 Fast, disk space efficient package manager"
    homepage "https://pnpm.io/"
    url "https://registry.npmjs.org/pnpm/-/pnpm-7.32.2.tgz"
    sha256 "f4b40caa0c6368da2f50b8ef891f225c24f14e7d60e42a703c84d3a9db8efede"
    license "MIT"
  
    livecheck do
      url "https://registry.npmjs.org/pnpm"
      regex(/["']latest-7["']:\s*?["']([^"']+)["']/i)
    end
  
    depends_on "node" => :test
  
    conflicts_with "corepack", because: "both installs `pnpm` and `pnpx` binaries"
  
    def install
      libexec.install buildpath.glob("*")
      bin.install_symlink "#{libexec}/bin/pnpm.cjs" => "pnpm"
      bin.install_symlink "#{libexec}/bin/pnpx.cjs" => "pnpx"
    end
  
    def caveats
      <<~EOS
        pnpm requires a Node installation to function. You can install one with:
          brew install node
      EOS
    end
  
    test do
      system "#{bin}/pnpm", "init"
      assert_predicate testpath/"package.json", :exist?, "package.json must exist"
    end
  end

修改點的一些小解釋#

命名(檔案名、也是 formula 名字)方面,首先必須要和官方的 pnpm 做出區分,而選擇 pnpm@{VERSION} 這種格式則是和官方的其他多版本包格式保持一致(例如 postgresql、llvm 等均採用這種命名)。class 的名稱和檔名要保持對應,pnpm@7 所對應的是 PnpmAT7

另外,我們的 pnpm@7.rb 檔案可以選擇存儲在前面 tap-new 命令幫我們創建好的 Formula 目錄下,也可以直接放在根目錄下(還可以放在 HomebrewFormula 下)

url 和 sha256 是對應的包下載地址和對應包的下載檔案哈希,我們可以利用 NPM Registry API 來獲取最新的版本,並下載該版本對應的包來獲取哈希

# 獲取當前的 V7 最新版本
curl -s https://registry.npmjs.org/pnpm | jq '."dist-tags"."latest-7"'

# 獲取這一版本對應的哈希
curl -s https://registry.npmjs.org/pnpm/-/pnpm-7.32.2.tgz | sha256sum

livecheck 是用來檢測當前是否是最新版本的,pnpm 官方的 formula 是取的 latest,我們這裡修改成取 latest-7

移除 bottle,因為這是自動化構建的內容,新的 formula 不應該包括

發布#

在發布前,我們需要先執行下 brew style --fix pnpm@7.rb 來確認下 style 符合規範。

我們需要利用 Github Action 來觸發測試和 bottle 的構建。在提交代碼後不要直接 push 到主分支,而是提交一個 PR ,在通過所有測試後給其添加 pr-pull 標籤。

然後任何人就都可以直接安裝我們的 pnpm@7 啦!

# case 1: enable tap
brew tap ImSingee/pnpm
brew install pnpm@7

# case 2: simple install single
brew install ImSingee/pnpm/pnpm@7 

發布後的倉庫在 https://github.com/ImSingee/homebrew-pnpm,歡迎使用和 star :-)

安裝後的一個小 tips#

命名使用 <name>@{VERSION} 的一個好處在於,其不会污染我們目前在使用的環境,這可以在安裝後有一個提示看出

pnpm@7 is keg-only, which means it was not symlinked into /opt/homebrew,
because this is an alternate version of another formula.

If you need to have pnpm@7 first in your PATH, run:
  echo 'export PATH="/opt/homebrew/opt/pnpm@7/bin:$PATH"' >> ~/.zshrc

與一般的 formula buts,這種會被檢測到是另一個程序的不同版本,因此不會安裝到系統的 PATH 下,而是獨立的放在額外的路徑。

根據提示,如果我們想要執行 7 版本的 pnpm,則需要使用全路徑 /opt/homebrew/opt/pnpm@7/bin/pnpm ,如果想要讓這個作為主版本的話,可以將 /opt/homebrew/opt/pnpm@7/bin 加入至 PATH 中。

當然,我的做法是做一個 symlink

ln -s /opt/homebrew/opt/pnpm@7/bin/pnpm /usr/local/bin/pnpm7
ln -s /opt/homebrew/opt/pnpm@7/bin/pnpx /usr/local/bin/pnpx7

參考#

https://docs.brew.sh/How-to-Create-and-Maintain-a-Tap

https://stackoverflow.com/questions/3987683/homebrew-install-specific-version-of-formula

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。