私はWindowsでWSL Ubuntu環境を3つ動かしています。それぞれの環境に同じCLIツールをインストールする「バージョン管理ツール」をasdfからaquaに移行した記録を書き出しておきます。
asdfとaqua
asdfとaquaはともにCLIツールのバージョン管理ツールです。どちらもツールをインストールするまでの流れが似通っていますが、ツールの定義を誰が管理しているかが決定的な違いです。
簡単に両者を見てみましょう。
asdf
asdfは複数のランタイムバージョン管理、コマンドラインツールのバージョン管理ができるCLIです。asdfの特徴はプラグイン(asdf-vm/asdf-plugins)で、自分や誰かがCLIツールの定義をGitHubリポジトリに配置すると、asdf利用者はそれを参照して定義されたツールをインストールできます。
ツールのインストール例
例えば、Node.jsをインストールするには次のようにします。
# asdfのnode.jsプラグインを追加 asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git # node.jsをインストール asdf install nodejs latest
ツールのバージョン管理
ツールのバージョンは$HOME/.tool-versions
や$PWD/.tool-versions
に保存されています。これを使ってグローバルやパス単位にツールバージョンを制御できます。
# global version asdf global nodejs lastest # curent dir version asdf local nodejs latest
aqua
aquaはコマンドラインツールのバージョン管理をするCLIです。aquaはインストールするツールをYAML定義し、aquaproj/aqua-registryに配置されたツール定義を参照してCLIをインストールします。
ツールのインストール例
例えば、Node.jsをインストールするには次のようにします。
# yaml定義を作成 aqua init # yaml定義にnodejsを追加 aqua g -i nodejs/node # インストール aqua i
aquaは遅延インストール(Lazy Install)がデフォルトで有効になっているので、明示的にaqua -i
を実行せずともツール定義に記載されていればツール実行時に自動でインストールされます。便利ですが厄介な機能でもあります。
sed -i "s|cli/cli@.*|cli/cli@v2.1.0|" aqua.yaml $ gh version INFO[0000] download and unarchive the package aqua_version=2.16.4 env=linux/arm64 exe_name=gh package_name=cli/cli package_version=v2.1.0 program=aqua registry=standard gh version 2.1.0 (2021-10-14) https://github.com/cli/cli/releases/tag/v2.1.0
ツールのバージョン管理
ツールのバージョンは$AQUA_GLOBAL_CONFIGや$PWD/aqua.yaml
に保存されています。ツール定義のバージョンを変えることで、グローバルやパス単位にツールバージョンを制御できます。
グローバルなツールをインストールする際は-a
オプションが必要です、あまり見かけないコマンド体系ですね。
# global version mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/aquaproj-aqua" vi "${XDG_CONFIG_HOME:-$HOME/.config}/aquaproj-aqua/aqua.yaml" export AQUA_GLOBAL_CONFIG=${AQUA_GLOBAL_CONFIG:-}:${XDG_CONFIG_HOME:-$HOME/.config}/aquaproj-aqua/aqua.yaml aqua i -a # current dir version aqua i
なぜaquaなのか
asdfからaquaに移行したきっかけは、ここ数年で見かけることが増えた「悪意を持った第三者のOSSへの攻撃事例」でした。asdfはツール管理を3rdパーティから取得する仕組み上、潜在的なセキュリティリスクがあると考えています。一方aquaは公式レジストリがあり、多くのユーザーによるレジストリをwatch、自動化されたパッケージ更新があることから信頼性が高いと考えました。
検討ポイント | asdf | aqua |
---|---|---|
ツール定義の取得元 | 3rdパーティ | 公式レジストリ |
ツール定義の更新 | リポジトリによるが基本手動 | Renovateによる自動更新 |
チェックサム管理 | リポジトリによるがないことが多い | Renovateによる自動更新 |
asdfのプラグインは各自のリポジトリ(3rdパーティ)で管理されており、asdfから利用するときにプラグインの定義が今どうなっているか気にする機会はまずありません。asdfプラグインを使うときに定義が変なことをしていないか精査することはあっても、その後プラグインが更新されるたびにチェックすることは困難だなぁ、と気になっていました。1
aquaは公式レジストリがあり、ツールの定義はレジストリに集約されています。レジストリへのツール定義追加時はレビュー、ツール定義更新時はRenovateが自動実行しチェックサムも更新します。ツール定義が公式レジストリで提供され、ツール定義が自動化込みでメンテされているというのは使うときに安心できるポイントです。レジストリの作りの丁寧さは尊敬を覚えます。
自動化するメリットの1つは人の手が入り込みにくいことです。自動化の仕組み自体にアプローチされるとダメですが、自動化されたフローに対して人の手で介入するのは難しくなります。例えば、私が自動化対象のマニフェストを変更するPRを出しても、相応の理由がない限りリジェクトされるでしょう。2
asdfからaquaへの移行
私はAnsibleでWSL構築を自動化しており、asdfからaquaへの移行はAnsibleのプレイブックを修正するだけで済みました。具体的には、次の2点で対応しています。
- Linux用のdotfile管理にaqua定義を追加: HOME/.config/aquaproj-aqua/aqua.yaml - guitarrapc/dotfiles-linux)
- ツールインストールをasdfからaquaに変更: feat: switch asdf to aqua #51 - guitarrapc/local-provisioner
aquaの注意点
ローカル環境のCLI管理としてaquaを使っていくにあたり次の点に注意が必要です。GitHub Actionsなど環境が都度新しくなる場合は気にする必要はありません。
ツールのアンインストール
aquaのアンインストールaqua rm パッケージ
はpkgsディレクトリから削除されるだけで、ツールのバイナリはbinフォルダに残ります。binにファイルがあるとaqua-proxyがそのコマンドは存在すると返す上に、ツール実行時に遅延インストールでツールをPkgsに持ってきて実行します。ツールをアンインストールしたのにツールがまた実行できちゃうという不思議な体験ですね。
# パスはaquaのものを使っている $ which kubectl /home/guitarrapc/.local/share/aquaproj-aqua/bin/kubectl # 素直にアンインストールしてみる $ aqua rm kubectl INFO[0000] removing a package aqua_version=2.37.2 env=linux/amd64 exe_name=kubectl package_name=kubernetes/kubectl program=aqua # アンインストールしたのに残ってる!? (と思われても仕方ない) $ which kubectl /home/guitarrapc/.local/share/aquaproj-aqua/bin/kubectl # アンインストールしたはずなのに遅延インストールされて実行できる $ kubectl verison --client INFO[0000] download and unarchive the package aqua_version=2.37.2 env=linux/amd64 exe_name=kubectl package_name=kubernetes/kubectl package_version=v1.31.0 program=aqua registry=standard Client Version: v1.31.0 Kustomize Version: v5.4.2
対処は、アンインストール時にモードを-mode lp
や-m lp
で指定します。
aqua rm -m l kubectl # リンクしか削除しない (デフォルト) aqua rm -m lp kubectl # リンクとパッケージを削除
アンインストールされてコマンドが実行できなくなっていますね。
$ aqua rm -m lp kubectl INFO[0000] removing a link aqua_version=2.37.2 env=linux/amd64 exe_name=kubectl program=aqua INFO[0000] removing a package aqua_version=2.37.2 env=linux/amd64 exe_name=kubectl package_name=kubernetes/kubectl program=aqua $ kubectl Command 'kubectl' not found, but can be installed with: sudo snap install kubectl
ただ、定義にkubectlが残っているとaqua which コマンド
でパスが返って来ます。とはいえ定義から消してもパス取得エラーを返すのは違和感があるのですが...
$ aqua which kubectl /home/guitarrapc/.local/share/aquaproj-aqua/pkgs/http/dl.k8s.io/v1.31.0/bin/linux/amd64/kubectl/kubectl # kubectlの定義を消す $ vim $AQUA_GLOBAL_CONFIG # エラーに変わる。aquaの定義になくアンインストール済みなので、管理下として返すのは違和感がある $ aqua which kubectl FATA[0000] aqua failed aqua_version=2.37.2 doc="https://aquaproj.github.io/docs/reference/codes/004" env=linux/amd64 error="command is not found" exe_name=kubectl program=aqua # curlでインストールするとそっちが優先されるので困ることはない $ curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.31.2/2024-11-15/bin/linux/amd64/kubectl $ sudo chmod +x kubectl $ sudo mv kubectl /usr/local/bin/kubectl $ kubectl version --client Client Version: v1.31.0 Kustomize Version: v5.4.2
aquaのアンインストールモードは環境変数で設定できるので、基本的にバイナリからもファイルを消すようにするのが自然に感じます。
export AQUA_REMOVE_MODE=pl
まとめ
もともと手動でツールインストールするのに疲弊してasdfを使い始めました。長年asdfを使ってたのですが、昨今のOSSに対する攻撃を見ていてasdfを継続して使うリスクが気になり始めました。1年ほど手動に戻すかaquaで悩んだ末、aquaに移行しました。
Github Actionsのツール管理にもaquaを使っているのですが、3か月あまり使った今も手動インストールに戻す理由がないぐらいには満足しています。