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

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。