Retty Tech Blog

実名口コミグルメサービスRettyのエンジニアによるTech Blogです。プロダクト開発にまつわるナレッジをアウトプットして、世の中がHappyになっていくようなコンテンツを発信します。

CLI バージョンマネージャー aqua のススメ

この記事は Retty Advent Calendar 2022 Part1 の14日目の記事です。昨日は今井さんの『ストーリーポイント定規を作ってみた』でした。

今年も Part2 があるのでこちらもよろしくお願いします。自分は Part2 の16日にも記事を書きます。

はじめに

Retty インフラチームの幸田です。締め切りの仕事が増えて年末を感じています。

日々の業務で様々な CLI ツールを利用していると思いますが、それらのツールはどのようにインストールし、管理していますか?管理方法は色々ありますが、brew や apt などのパッケージマネージャーを利用している方が多いのではないでしょうか。

自分も様々なツールを brew で入れることが多かったのですが、最近は aqua という CLI Version Manager に寄せていて、対応していないツールのみを brew で管理することにしています。

github.com

今回の記事では aqua を布教するべく、主要機能や推しポイントを紹介しようと思います。

aqua について

aquaYAML ファイルを用いてツールとバージョンを宣言的に記述できる CLI Version Manager です。CLI Version Manager というと聞き慣れないかもしれないですが、有名なツールとして asdf-vm/asdf が挙げられます。また特定の言語やツールでよくある XXenv (tfenv, rbenv, pyenv..) 系のツールも広義ではその仲間だと思っています。

aqua はどちらかというと asdf に近いツールで、公式ドキュメントでもその違いや Pros / Cons が言及されています。

aquaproj.github.io

チュートリアル

説明するよりも実際に見たほうが早いと思うのでどのように動作するのかを紹介したいと思います。概ね Quick Start の通りです。

1. aqua のインストール

まずは aqua 自体のインストールが必要です。Mac ユーザであれば brew で入れてしまうのが早いかと思います。

brew install aquaproj/aqua/aqua

続いて PATH の設定です。
この $XDG_DATA_HOME/.local/share/aquaproj-aqua/bin 配下に様々なコマンドが足されていくイメージです。*1

export PATH="${AQUA_ROOT_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua}/bin:$PATH"

インストールはこれで完了します。

$ aqua -v
aqua version 1.25.0 (9a544d686c6b7898596dd4a44cd04bd3a2782473)

docker を使ったチュートリアル も紹介されているので、手元の環境を汚したくないという方はこちらがオススメです。

2. ツールの追加

aqua コマンドの準備が完了したので、実際にツールのインストールをしてみたいと思います。

2.1 Registry とは

aqua で利用可能なツールは Registry によって管理されます。Registry は自分で作ることもできますが、公式から Standard Registry と呼ばれるものが公開されています。
公開されていない社内ツールを利用したいなどの特殊なケースを除いて、基本的にはこの Standard Registry を利用する形で問題ないと思います。

github.com

記事執筆時点での最新バージョンである Standard Registry v1.102.0 では 967 種類の CLI ツールが利用可能です。

2.2 aqua.yaml の生成

aqua で利用するツールは aqua.yaml で管理します。aqua init を実行することでファイルを自動生成することができます。

$ aqua init
$ cat aqua.yaml
---
# aqua - Declarative CLI Version Manager
# https://aquaproj.github.io/
# checksum:
#   # https://aquaproj.github.io/docs/reference/checksum/
#   enabled: true
#   require_checksum: true
registries:
- type: standard
  ref: v3.102.0 # renovate: depName=aquaproj/aqua-registry
packages:

registries: にはデフォルトで Standard Registry が使用され、その時点での最新バージョンが設定されます。

このファイルの packages: にツールを追加していきます。

2.3 aqua generate によるツールの追加

aqua.yaml へのツールの追加は、手動で書くこともできますが aqua generate -i コマンドを利用すると便利です。short command の場合は aqua g -i です。

このコマンドを利用すると下記のように fzf like *2な検索画面でツールを検索することができて、選択すると aqua.yaml の packages に自動で追記してくれます。(-i オプションは aqua.yaml に追記するためのオプション)

  GoogleContainerTools/skaffold                           ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
  GoogleContainerTools/kpt                                │  1xyz/pryrite
  GoogleContainerTools/container-structure-test           │
  GoogleContainerTools/container-diff                     │  https://github.com/1xyz/pryrite
  GoogleCloudPlatform/terraformer/aws [terraformer]       │  Pryrite, interactively execute shell code blocks
  GoogleCloudPlatform/terraformer                         │  in a markdown file
  GoogleCloudPlatform/cloud-sql-proxy                     │
  GoogleCloudPlatform/berglas                             │
  GoodwayGroup/gwvault                                    │
  GoTestTools/gotestfmt                                   │
  FiloSottile/mkcert                                      │
  FiloSottile/age [age, age-keygen]                       │
  FairwindsOps/rbac-lookup                                │
  FairwindsOps/polaris                                    │
  FairwindsOps/pluto                                      │
  FairwindsOps/nova                                       │
  EdenEast/repo                                           │
  Dreamacro/clash                                         │
  DelineaXPM/dsv-cli (thycotic/dsv-cli) [dsv]: secrets .. │
  ClementTsang/bottom [btm]                               │
  CircleCI-Public/circleci-cli [circleci]                 │
  Cian911/switchboard                                     │
  BurntSushi/ripgrep [rg]                                 │
  BishopFox/cloudfox                                      │
  BeryJu/korb                                             │
  Azure/draft                                             │
  Azure/aztfy                                             │
  Azure/aks-engine                                        │
  Arriven/db1000n                                         │
  Aloxaf/silicon                                          │
  99designs/aws-vault                                     │
> 1xyz/pryrite                                            │
  967/967                                                 │
>                                                         └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─

例として GitHubCLI ツール (gh) である cli/cli を選択すると、aqua.yaml は次のようになります。

$ cat aqua.yaml
---
# aqua - Declarative CLI Version Manager
# https://aquaproj.github.io/
# checksum:
#   # https://aquaproj.github.io/docs/reference/checksum/
#   enabled: true
#   require_checksum: true
registries:
- type: standard
  ref: v3.102.0 # renovate: depName=aquaproj/aqua-registry
packages:
- name: cli/cli@v2.20.2

packages:- name: cli/cli@v2.20.2 が足されました。

3. ツールのインストールと実行

aqua.yaml にファイルを追加したら aqua i コマンドを利用してツールをインストールします。

aqua i

これで gh コマンドを利用する準備が整ったので実行してみます。

$ gh --version
gh version 2.20.2 (2022-11-15)
https://github.com/cli/cli/releases/tag/v2.20.2

gh コマンドが正常に実行され、さらにバージョンは指定した 2.20.2 が実行されていることが確認できました。

aqua のここが便利!

ざっくりとした使い方が紹介できたので、ここからは便利ポイントを紹介します。

バージョンの指定、切り替えが簡単にできる

aqua の場合には、ツールのバージョンも合わせて YAML ファイルに記載することになっているので、このバージョンを変更するだけで簡単にツールのバージョンを切り替えることができます。

$ cat aqua.yaml | grep cli/cli
- name: cli/cli@v2.20.2

$ gh --version
gh version 2.20.2 (2022-11-15)
https://github.com/cli/cli/releases/tag/v2.20.2

# aqua.yaml に記載されている cli/cli のバージョン v2.19.0 に切り替え
$ cat aqua.yaml | grep cli/cli
- name: cli/cli@v2.19.0

$ gh --version
gh version 2.19.0 (2022-11-03)
https://github.com/cli/cli/releases/tag/v2.19.0

言語のランタイム以外でバージョンを切り替えたい、もしくは指定されたバージョンで動かしたいケースはあまりないかと思いますが、下記のようなケースで有用だと思っています。

  • CI 環境で各種ツールを利用するケース(CI 環境での利用は後述します)
  • チーム開発でメンバーとツールのバージョンを揃えたいケース

バージョン切り替え時の挙動について

既にそのツールが aqua i によってインストールされており、バージョンのみ切り替わった場合には再度 aqua i をする必要はありません。これは aqua の Lazy Install の機能によるものです。

下記のように、指定されたバージョンが実行環境に存在しない場合には、実行時に自動的にインストールされます。

# cli/cli v2.20.2 がインストールされていない状態で実行
$ gh --version
INFO[0000] download and unarchive the package            aqua_version=1.25.0 env=darwin/amd64 exe_name=gh exe_path=/Users/username/.local/share/aquaproj-aqua/pkgs/github_release/github.com/cli/cli/v2.20.2/gh_2.20.2_macOS_amd64.tar.gz/gh_2.20.2_macOS_amd64/bin/gh package=cli/cli package_name=cli/cli package_version=v2.20.2 program=aqua registry=standard
gh version 2.20.2 (2022-11-15)
https://github.com/cli/cli/releases/tag/v2.20.2

バージョンの指定について

aqua.yaml には必ずバージョンを指定する必要があり、@latest のような指定はできません。これは aqua の思想によるものだと思われます。

On the other hand, aqua doesn't support specifying latest because aqua is CLI Version Manager. You must specify the version strictly.
https://aquaproj.github.io/blog/2022/05/30/support-building-go-tools/#can-we-specify-latest-like-go-install

場合によっては不便に感じるかもしれませんが「知らない間にバージョンが上がって挙動が変わった」といったトラブルを防ぐことができるため、個人的には便利に感じています。

aqua generate -s の利用

ツール追加時の aqua g でもバージョンを選択することができて aqua g -i -s のように -s をつけると、ツールを選択した後にバージョンのリストが表示されて、バージョンも fzf like に検索、指定できます。

  v2.6.0                                        │  v2.20.2 (GitHub CLI 2.20.2)
  v2.7.0                                        │
  v2.8.0                                        │  https://github.com/cli/cli/releases/tag/v2. .
  v2.9.0                                        │  ## What's Changed
  v2.10.0                                       │  * Fix up Linux packaging regression by
  v2.10.1                                       │  @samcoe in https://github.com/cli/cli/p
  v2.11.0                                       │  ull/6621
  v2.11.1                                       │
  v2.11.2                                       │
  v2.11.3                                       │  **Full Changelog**: https://github.com/
  v2.12.0                                       │  cli/cli/compare/v2.20.1...v2.20.2
  v2.12.1                                       │
  v2.13.0                                       │
  v2.14.0                                       │
  v2.14.1                                       │
  v2.14.2                                       │
  v2.14.3                                       │
  v2.14.4                                       │
  v2.14.5                                       │
  v2.14.6                                       │
  v2.14.7                                       │
  v2.15.0                                       │
  v2.16.0                                       │
  v2.16.1                                       │
  v2.17.0                                       │
  v2.18.0                                       │
  v2.18.1                                       │
  v2.19.0                                       │
  v2.20.0                                       │
  v2.20.1                                       │
> v2.20.2                                       │
  88/88                                         │
>                                               └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─

aqua.yaml による設定の共有ができる

チュートリアルで説明したように aqua.yaml で利用するツールやバージョンを指定します。そのため aqua.yaml を共有することで、環境が異なるマシンでも同様のツール、同様のバージョンを用意することができます。
複雑なインストール作業は不要で、aqua さえあれば aqua i を実行するだけで使えるようになります。

このファイルを Git 管理すれば、他のメンバーとツールやバージョンを共有できますし、CI でも aqua を利用することで CI 環境とローカルでツールのバージョンを揃えるといったこともできるようになります。

設定ファイルの読み込みについて

Configuration file path に記載されていますが、aqua はカレントディレクトリからルートディレクトリまでのファイルを探索するため、カレントディレクトリの aqua.yaml が優先して利用されます。
例えばモノレポ構成の場合、ディレクトリ毎に aqua.yaml を配置することでプロジェクトによってバージョンを固定したり切り替えたりすることができます。

手元の CLI ツールを管理したい場合には AQUA_GLOBAL_CONFIG を設定することで、カレントディレクトリに aqua.yaml がなくとも利用できるため、dotfiles 的に利用したい場合にはこちらを設定すると良いでしょう。(Global Configuration)

Renovate によるバージョンアップに対応

aqua は Renovate によるバージョンのアップデートに対応しています。個人的に一番の推しポイントです。

https://aquaproj.github.io/docs/tutorial-extras/renovateaquaproj.github.io

使い方は簡単で renovate.json に下記を追記するだけです。

{
  "extends": [
    "github>aquaproj/aqua-renovate-config#1.2.6"
  ]
}

Renovate には RegexManager と呼ばれる機能があり、カスタムの設定ファイルを記述することで、正規表現をベースに任意のファイルに記載されたバージョンを Renovate でアップデートさせることができます。
aqua ではこの RegexManager の設定が公式で提供されており、上記のようにこの設定を読み込むことで aqua.yaml などのファイルを自動で更新させることができます。(renovate.json のバージョンも自動で更新されます)

https://aquaproj.github.io/docs/tutorial-extras/renovateaquaproj.github.io

今まで自前で RegexManager の設定を書いていた箇所も aqua + 公式の Preset Config に置き換える事によって、消すことができました。もちろん新しいツールを追加した場合にも自前の設定は不要です。

新規ツールの追加が簡単

Standard Registry には毎日のように新しいツールが足されています。(Pull Request 一覧)
主要なツールはカバーされている印象ですが aqua g で検索しても出てこない場合には、aqua-registry に PR を出すことでツールの追加が可能です。

registry-tool の使用

まだ対応していないツールを Standard Registry で利用したい場合には、aqua-registry と呼ばれるコマンドを利用することで簡単に追加することができます。

github.com

CONTRIBUTION.md にある通りですが、(追加するツール次第では)下記コマンドを実行するだけで Standard Registry へのツールの追加から PR の作成までを行うことができます。

$ aqua-registry scaffold cli/cli
$ aqua-registry create-pr-new-pkg cli/cli

ツールによっては生成されたファイルの細かい手直しが必要になるので、公式ドキュメントに従って修正を行います。

PR マージ後に新しい Registry のバージョンがリリースされ、手元の aqua.yaml にバージョンを反映するとインストール可能になります。もちろん Renovate によるアップデートにも対応されます。

ユースケース

特に凝った使い方はしていないですが、会社や個人でどう使っているかを紹介します。

CI での利用

先月公開した記事でも書きましたが、Terraform の CI/CD に aqua を導入しています。

engineer.retty.me

インストール作業の簡易化

CI で利用する linter や各種デプロイツールのインストールは curl などを利用して GitHub Release からバイナリを落として来るようなパターンが多いかと思いますが、ここでも aqua を利用することができます。必要なツールを記載した aqua.yamlリポジトリに入れて、CI 上で aqua i を実行するだけです。
当然 aqua 自体のインストールは必要ですが、CircleCI Orb と GitHub Actions の Action が提供されているため、これを利用すると便利です。

circleci.com

github.com

GitHub Actions の場合には次のようなステップを用意するだけで aqua コマンドが利用可能になります。

- uses: aquaproj/aqua-installer@v1.1.2
  with:
    aqua_version: v1.25.0

余談ですが、aqua 本体のバージョンを指定するための aqua_version には latest などのデフォルト値が用意されておらず、必ずバージョンを指定する必要があるのは aqua の思想が感じられて面白いですね。

そして地味に嬉しいのが、GitHub Actions の input として指定する aqua_version についても提供されている Renovate Config Preset でカバーされており、ここも自前で設定を用意しなくても良いです。便利。

厳密なバージョン指定

例えば GitHub Actions で各ツール向けに提供されている Action は、バージョンを指定することもできますが、特に指定しない場合にはデフォルトで latest が降ってくるケースが多いかと思います。そしてこのデフォルト値を利用している方も多いのではないでしょうか?

特にバージョンアップ作業などを行う必要もなく、常に最新に追従できるのは大きなメリットだと思いますが、その裏には「実行タイミングによってバージョンが変わってしまう」というデメリットがあると思っています。そう頻繁には起きないですが、新しいバージョンがリリースされて1時間前と今では latest で降ってくるバージョンが違うパターンなどです。
特に CI 環境においては冪等性が担保されるべきだと考えているので、個人的にはあまり望ましくありません。

aqua の場合だと厳密にバージョンを指定できる上に、面倒なバージョンのアップデート作業は Renovate で自動化できるため CI での利用にはよりマッチしていると思います。

dotfiles での利用

冒頭でも書いたように dotfiles に入れてローカルツールの管理にも利用しています。

github.com

Renovate の設定で automerge を設定しておくと、アップデートが降ってきた時に勝手に main に反映してくれます。定期的に dotfiles を pull してくる必要はありますが、手元のツールのバージョンを最新の状態に保つことができます。

dotfiles に出された PR の一覧

実際の PR を見てもらえるとイメージが湧くと思います。

各ツールの機能追加時に自分をアサインする

dotfiles に作られる PR は、patch バージョン(≒ 主に bugfix)のアップデートは自動でマージして、minor 以上のアップデートにはレビュアーとして自分をアサインするようにしています。

Renovate が作る PR には Release Notes なども書かれるので、(必ずしも含まれている訳ではないですが)手元で利用している各ツールの機能追加にも気付けるようになりました。

terraform-ls の例

アサインのルールなどは細かく設定可能なので、ドキュメントとにらめっこしながら設定してみてください。

まとめ

今回は CLI Version Manager の aqua について紹介しました。
色々な機能がありますが、Renovate による自動アップデートができる点に一番便利さを感じています。今までツール毎に書いていた RegexManager の設定を消すことができ、ツールを足した際にも追加設定が不要なのはかなり体験がいいです。

簡単に導入できるので皆さんもぜひ使ってみてください。

*1:aquaproj-aqua/bin 配下に配置されるのは実際の実行バイナリではなくシンボリックリンクです。内部実装の話になるので気になる方は作者様のブログを参照してください。

*2:ktr0731/go-fuzzyfinder