Bryan

Bryan

twitter
medium

複数のバージョンの pnpm を同時にインストールし、カスタム homebrew パッケージを公開する

Homebrew を利用して pnpm v7 をインストールする方法を探しているだけなら、brew install ImSingee/pnpm/pnpm@7 を実行すれば良いです。

pnpm v8 は 1 ヶ月前にリリースされ、大きなバージョンアップデートの一つとして、lock file V6 を導入し、V5 のサポートを停止しました。しかし、古いバージョンを使用しているプロジェクトにプルリクエストを送る場合、新しい依存関係を追加するためには lock file を更新する必要があり、これは受け入れられません。すべてのコラボレーターが pnpm をアップグレードすることを直接期待することはできません。

これにより、pnpm V7 と V8 の共存の問題が生じました。これは難しいことではなく、Corepackpnvm などのツールがあります。しかし、私にとってはそれらは重すぎます —— Homebrew だけで十分です。

Homebrew の問題の一つは、古いバージョンのインストールをサポートしていないことです。以前導入された homebrew/versions はすでに廃止されており、公式に推奨されている唯一の方法は自分でホスティングすることです —— もちろん、現在 pnpm は公式の古いバージョンのタップを維持していないため、自分で作業するしかありません。

タップの作成#

公式ガイド に従って、以下のコマンドを実行することで空のタップライブラリを作成できます。

brew tap-new ImSingee/homebrew-pnpm

注:ここで tap-new の後のパラメータ形式は <repo>/homebrew-<name> でなければならず、これにより brew tap <repo>/<name> でこのタップを有効にし、以降は 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 を設定してテストとボトルの構成を手伝ってくれるということです。

フォーミュラの追加#

pnpm@7 のフォーミュラを新たに追加します。ここでは公式の pnpm.rb を使用します。

class Pnpm < Formula
  require "language/node"

  desc "📦🚀 高速でディスクスペース効率の良いパッケージマネージャ"
  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: "両方が `pnpm` と `pnpx` のバイナリをインストールします"

  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 は機能するために Node のインストールを必要とします。以下のコマンドでインストールできます:
        brew install node
    EOS
  end

  test do
    system "#{bin}/pnpm", "init"
    assert_predicate testpath/"package.json", :exist?, "package.json は存在する必要があります"
  end
end

いくつかの小さな変更を行います。

  • ファイル名は公式の pnpm.rb ですが、ここでは pnpm@7.rb に変更して区別します。
  • クラス名 PnpmPnpmAT7 に変更します。
  • URL を必要なバージョンに変更します。現在の最新の v7 バージョンは 7.32.2 です。
  • sha256 を対応するバージョンのハッシュに変更します。現在の 7.32.2 に対応するのは f4b40caa0c6368da2f50b8ef891f225c24f14e7d60e42a703c84d3a9db8efede です。
  • livecheck セクションの URL を https://registry.npmjs.org/pnpm に変更し、regex を /["']latest-7["']:\s*?["']([^"']+)["']/i に変更します。
  • bottle を削除します。

最終的に修正されたバージョンは以下の通りです。

class PnpmAT7 < Formula
    require "language/node"
  
    desc "📦🚀 高速でディスクスペース効率の良いパッケージマネージャ"
    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: "両方が `pnpm` と `pnpx` のバイナリをインストールします"
  
    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 は機能するために Node のインストールを必要とします。以下のコマンドでインストールできます:
          brew install node
      EOS
    end
  
    test do
      system "#{bin}/pnpm", "init"
      assert_predicate testpath/"package.json", :exist?, "package.json は存在する必要があります"
    end
  end

変更点の小さな説明#

命名(ファイル名、フォーミュラ名)に関しては、公式の pnpm と区別する必要があり、pnpm@{VERSION} という形式を選択することで、公式の他の多バージョンパッケージの形式(例えば postgresql、llvm など)と一致させています。クラス名とファイル名は対応させる必要があり、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 公式のフォーミュラは latest を取得していますが、ここでは latest-7 を取得するように変更します。

bottle を削除します。これは自動構築の内容であり、新しいフォーミュラには含めるべきではありません。

公開#

公開前に、brew style --fix pnpm@7.rb を実行してスタイルが規範に合っているか確認する必要があります。

GitHub Action を利用してテストとボトルの構築をトリガーします。コードをコミットした後、直接メインブランチにプッシュするのではなく、PR を提出し、すべてのテストに合格した後pr-pull タグを追加します。

その後、誰でも直接 pnpm@7 をインストールできるようになります!

# ケース 1: タップを有効にする
brew tap ImSingee/pnpm
brew install pnpm@7

# ケース 2: 単体の簡単なインストール
brew install ImSingee/pnpm/pnpm@7 

公開後のリポジトリは https://github.com/ImSingee/homebrew-pnpm です。ぜひご利用ください、スターもお待ちしています :-)

インストール後の小さなヒント#

<name>@{VERSION} という命名の利点は、現在使用している環境を汚染しないことです。インストール後に以下のような提示が表示されます。

pnpm@7 は keg-only であり、/opt/homebrew にシンボリックリンクされていません。
これは別のフォーミュラの代替バージョンだからです。

pnpm@7 を PATH の最初に持ってくる必要がある場合は、以下を実行してください:
  echo 'export PATH="/opt/homebrew/opt/pnpm@7/bin:$PATH"' >> ~/.zshrc

一般的なフォーミュラとは異なり、このようなものは別のプログラムの異なるバージョンとして検出されるため、システムの PATH にインストールされず、追加のパスに独立して配置されます。

提示に従い、7 バージョンの pnpm を実行したい場合は、全パス /opt/homebrew/opt/pnpm@7/bin/pnpm を使用する必要があります。これをメインバージョンとして使用したい場合は、/opt/homebrew/opt/pnpm@7/bin を PATH に追加する必要があります。

もちろん、私のやり方はシンボリックリンクを作成することです。

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

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。