💧

aqua と asdf の違い

2021/12/09に公開
1

CLI ツールを YAML でバージョン管理できるツール aqua を開発しています。

https://aquaproj.github.io/

aqua で CLI ツールを YAML でバージョン管理 も参照してください。

似たようなユースケースで使われる有名なツールとして asdf があります。

https://asdf-vm.com/

そこで、 aqua と asdf の違いについて書きたいと思います。

ちなみに aqua と asdf のバージョンは以下のとおりです。

  • asdf: v0.9.0
  • aqua: v0.8.2
    • aqua-registry: v0.11.0

ただし、自分は asdf にはあまり詳しくありません。
最近 aqua と比較するためにちょっと触っているくらいです。
また、自分は aqua の開発者であるため、どうしても aqua に偏った比較をするでしょう。

asdf は非常に人気のあるツールで、様々なツールに対応しています。
また、 asdf には aqua にはない機能もあります。

共通する部分

  • ツールをグローバルにインストールできる
  • ツールを特定のプロジェクト用にインストールできる
  • 設定ファイルでツールとそのバージョンを管理できる
  • plugin 機構があり、様々なツールに対応できる
    • aqua の場合、 Registry という仕組みがある
  • GitHub Actions がある

asdf の introduction を読むと、やりたいことは割と親しいなと感じます。

you can check in to your project's Git repository to share with your team,
ensuring everyone is using the exact same versions of tools.

導入のしやすさ

aqua と asdf で共通するモチベーションとして、複数人で同じバージョンを使うようにするというのがあります。
aqua や asdf に限らず、チーム・プロジェクトにツールを導入すると言った場合、学習コストの低さは重要です。
みんながそのツールを使うモチベーションが高いとは限らないし、全員が英語のドキュメントをすらすら読めるとも限りません。

aqua のコマンドはシンプルで、多くの人は aqua i [-l] だけで良いはずです。

asdf は色々なコマンドがあり、 asdf install だけではなく、
.tool-versions から必要な plugin を調べて、 plugin を install する必要もあります。
plugin の install に関しては Make task のようなラッパーを用意するなど、簡略化する術はあるとは思いますが、
aqua を使ってるなら aqua i と実行すれば OK のような統一されたインタフェースが望ましいとは思います。

一方で知名度が高いほうが導入しやすいのは当然で、 asdf ほど人気のあるツールであれば導入しやすいでしょう。

Plugin 機構

asdf の plugin に対し、 aqua には Registry があります。
この 2 つは、本体とは独立して機能を拡張できる(様々なツールに対応できる) という共通点がある一方、
仕組み的には結構違います。

asdf の plugin

aqua の Registry

  • 実態は YAML のテキストファイルであり、複数のツールを管理する
  • Standard Regitsry という公式の Registry が一つ存在する
    • サードパーティの Registry も作ることはできるが、 private なツールのため以外で Registry を作る理由はあまりない気はします
    • 個別に plugin をインストールする手間がない
  • ツールのインストール手順は限定的であり、任意のコマンドを実行したりはできない

対応可能なツール

aqua のツールのインストールは基本的に download して unarchive するだけなので、 aqua では対応できないツールも少なからずあります。
aqua で対応できないツールを使いたい場合、 asdf のような他のツールを検討するしかないでしょう。

ただし、これだけでインストール可能なツールもかなり多く存在します。
Go で作られたツールは GitHub Releases で公開されていることが多く、こういったツールは aqua で簡単に対応できます。
インストール方法を限定している分、簡潔に記述できるとも言えます。シェルスクリプトをゴニョゴニョ書く必要はありません。

セキュリティ

また、任意のスクリプトを実行できるのはセキュリティ的な懸念もありますが、 aqua ではそのようなことはできないので、
ツールをインストール時に悪意のあるコードが実行されるようなことはありません。
asdf の plugin には security policy があり、 security に関しても気を配ってますし、不必要に不安を煽るつもりはありませんが、仕組み的に防ぐことは難しいでしょう。

plugin の管理

asdf はサードパーティの plugin が数多く存在し、なにかのツールに対応するとなったら基本的にサードパーティの plugin を作ることになるでしょう。
一方で aqua では Standard Registry という中央集権的な Registry が存在し、そこにツールを追加する形になります。

Standard Registry に追加するツールは、 star 数や人気とかで選別といったことは全くしてないので、気軽に PR を投げてください。

大きな違いは、どこで管理・メンテされるかということでしょう。

asdf のサードパーティの plugin は当然その plugin を作った人によってメンテされます。
場合によっては放置されてメンテされず、ツールや asdf 自体の更新とともに壊れたままなこともあるかもしれません。
品質もマチマチでしょう。
一方で asdf のメンテナーは個別のツール・ plugin に気を配る必要がないので asdf 自体の開発に専念でき、
plugin のメンテで消耗することはないので健全だし、スケールしやすいと言えます。

一方 aqua の Standard Registry は aqua のメンテナーによってメンテされるので、
サードーパーティの plugin に比べると安心して使えるし、放置されるリスクも低いと言えます。
一方で aqua のメンテナーは Registry にも気を配る必要があるので消耗するリスクはあります。
Standard Registry ではツールの更新を Renovate で検知して CI を実行することで、ツールの更新に伴い Registry が壊れても気付けるようにしています。
aqua の update に伴い Registry に修正が必要な場合、 Standard Registry のように一つのリポジトリに集約しているモデルだと、リポジトリが分散しているモデルに比べ、対応が遥かに容易です。

また、ツールのインストール時に外部コマンドを実行しないため、環境への依存が少なく(OS と CPU アーキテクチャには依存しますが)、再現性が高いと言えます。
そのため、メンテしやすいです。外部コマンドを実行する plugin を中央集権的に管理するとなると
「自分の環境では動かないんだけど」みたいな問い合わせで消耗してしまう懸念があります。

plugin (Registry) のバージョン管理

aqua は aqua.yaml で Registry のバージョンも固定することを強制しています。
これは高い再現性を保証します。ツールのバージョンが同じでも、 Registry のバージョンが違えば結果が変わりうるからです。
そして Renovate を使って簡単に継続的にアップデートできます。

一方で asdf にはそのような仕組みはないように思えます。
plugin の update はコマンド asdf plugin update でできるようですし、 plugin の version (ref) を指定することもできそうですが、
ツールのバージョン指定を強制するように、 plugin のバージョンの指定を強制するようにはなってなさそうです。

ツールの継続的 update

aqua は Renovate の Preset Config を提供しており、簡単に継続的なアップデートができます。

asdf では asdf 用の Manager を追加する issue はあるようです。 https://github.com/renovatebot/renovate/issues/4051
しかし、 Renovate の Preset はないでしょう。 aqua の package 名は GitHub Repository 名と一致させるようになっているため、 Renovate で更新がやりやすいのですが、
asdf の plugin 名はそうはなってないので、個別に Regex Manager 用のコードを書く必要があり、大変でしょう。
数が少なければそんな大変でもないでしょうが、汎用的な仕組み化は難しいでしょう。

書いてて思いついたのは、 GitHub Actions で定期的に asdf plugin update を実行して自動で PR を作るとかはできそうですね。
それでも Renovate に比べると自前で自動化の仕組みを作らないといけなくて大変です。

ツール実行時に、指定したバージョンがインストールされてなかった場合の挙動

aqua は自動でツールをインストールしたあとに、ツールを実行します。この機能は aqua では Lazy Install と呼んでいます。
一方、 asdf では自動ではインストールはせず、インストールを促すメッセージを出力しつつ失敗します。
個人的には aqua の挙動のほうがよりユーザーフレンドリーかなと思います。
特にスクリプトの中でそのツールが呼ばれているような場合、 aqua の挙動のほうが都合が良いでしょう。

.ruby-version などへの対応

asdf はこれらのファイルにも対応しています。 aqua はしていません。

インストール可能なバージョンのリストを調べる

asdf ではできますが、 aqua はできません。
実は古いバージョンではできたものの、消したという経緯があり、復活させるか少々迷っています。

aqua で対応している機能

WARNING: aqua と asdf を併用する場合

これは比較ではなくて、両方を同じ laptop で併用する場合の注意点です。

リポジトリ A で asdf で waypoint をインストールし、リポジトリ B で aqua で waypoint をインストールしており、 A, B 両方を同じ laptop で開発するということはあり得るかと思います。

aqua と asdf をインストールする場合、それぞれ PATH を更新しますが、
.bashrc などで asdf を load したあとに ~/.aqua/bin を PATH に追加したほうが良いです。

これは自分が aqua を推してるから、 aqua を優先して使ってほしいということではありません。

~/.aqua/bin を PATH に追加したあとに asdf を load したとしましょう。
上記の例で B で waypoint を呼び出した場合、 aqua.yaml が参照されなくなってしまいます。
asdf のほうが優先されるからです。

これは aqua に限った話ではなく、 asdf を使ってあるツールをインストールすると、 /usr/local/bin とか /usr/bin に同じツールがインストールされていても、そちらを参照しなくなるでしょう。
asdf で global にインストールすれば良い話ですが、あるリポジトリで asdf を使っているからと言って、そのリポジトリ以外でも asdf を使わないといけないというのは、あまり良い話ではないと思っています。
個人だけで使うならともかく、チーム、プロジェクトで使うとなれば尚更です。

一方、 ~/.aqua/bin をあとで PATH に追加したとして、 A で waypoint を呼び出した場合、
ちゃんと asdf が動作します。これは aqua が設定ファイルからツールを探索した結果、見つからなかった場合に aqua 以外の PATH からもツールを探索するという動作になっているからです。 aqua はあるプロジェクトであるツールを aqua でインストールしたとしても、
そのプロジェクト以外でも aqua でそのツールを管理することを強制しないような設計になっています。

そういう意味でも、 aqua は導入しやすいと言えるでしょう。

最後に

後半疲れてきて雑になってしまいましたが、 aqua と asdf を簡単に比較してみました。
少しでも aqua に興味の湧いた方は使ってみてください。

https://aquaproj.github.io/

Discussion