Skip to main content

5 posts tagged with "aqua"

View All Tags

· 9 min read
Shunsuke Suzuki

以前 aqua v0.7.3 がリリースされた際に aqua で組織・チームのツール群を管理 という記事を書きました。 あれからもうすぐ 2 ヶ月になり、最新バージョンは v0.7.16 になりました。 そこで v0.7.4 ~ v0.7.16 の間の更新と、関連 repository の更新を幾つか(全部ではない)紹介します。

基本的に Release Note に書いてある内容です。

  • GitHub の Access Token が基本的に不要になりました
  • Homebrew で install できるようになりました
  • aqua.yaml がより簡潔に書けるようになりました
  • aqua.yaml の packages を他のローカルのファイルから import できるようになりました
  • aqua.yaml をディレクトリの階層的にネストできるようになりました
  • aqua which コマンドをサポートしました
  • github_archive, github_content type をサポートしました
  • (advanced) バージョンによってパッケージの type が変更された場合にも対応できるようになりました
  • Standard Registry の package の数が 139 => 220 になりました。
  • aqua のための CircleCI Orb をリリースしました

GitHub の Access Token が基本的に不要になりました

private repository から package をインストール場合は当然必要ですが、そうでなければ不要になりました。 これにより、 aqua を導入するハードルが下がりましたし、 GitHub API の Rate Limit に引っかかることが基本的になくなりました。

余談ですが、 private repository のツールを簡単に install できるのも aqua の便利な点だなと思っています。

Homebrew で install できるようになりました

$ brew install suzuki-shunsuke/aqua/aqua

Homebrew で install できるようにすることで、より手軽に導入してもらえるようになりました。

ちなみに aqua を aqua で管理出来ないのかというと、現状できません(無限ループになってしまう)。

aqua.yaml がより簡潔に書けるようになりました

AS IS

- name: direnv/direnv
registry: standard
version: v2.28.0 # renovate: depName=direnv/direnv

TO BE

- name: direnv/direnv@v2.28.0
  • registry: standard が省略可能
  • version を name に @ のあとに書ける
    • これにより、 Renovate の Regex Manager 用にコメント # renovate: depName=direnv/direnv を書く必要がなくなりました

これにより 1 行で書けるようになりました。 ただ簡潔にかけて楽というだけでなく、 Renovate 用のコメントでパッケージ名を間違えて指定するリスクがなくなりました。

e.g. パッケージ名を間違えている例

- name: direnv/direnv
registry: standard
version: v2.28.0 # renovate: depName=cli/cli

aqua-renovate-config を使えばコメントを書かなくても version を上げることが出来ます。

aqua.yaml の packages を他のローカルのファイルから import できるようになりました

e.g.

ディレクトリ構成

aqua.yaml
aqua/
reviewdog.yaml
golangci-lint.yaml
...

aqua.yaml

packages:
- import: aqua/*.yaml

aqua/reviewdog.yaml

packages:
- name: reviewdog/reviewdog@v0.13.0

このようにファイルを分割することで、特定のパッケージが update された場合のみ CI で特定の job を実行するといったことがやりやすくなります。 例えば GitHub Actions の on.<push|pull_request>.paths の場合、

name: test
on:
pull_request:
paths:
- '**.go'
- aqua/golangci-lint.yaml

aqua.yaml をディレクトリの階層的にネストできるようになりました

aqua はカレントディレクトリからルートディレクトリに遡って ^\.?aqua\.ya?ml$ を探索します。 このとき、それまではファイルを一つ見つけた時点で探索をやめていましたが、全ての階層を探索するようになりました。 同じパッケージが定義されていた場合、先に見つかった設定ファイルの version が優先されます。 これにより、 Monorepo でも aqua が使いやすくなりました。 Monorepo 直下に aqua.yaml を置きつつ、サブディレクトリに aqua.yaml を置いても、サブディレクトリ配下で Monorepo 直下の aqua.yaml も参照できるようになりました。

aqua which コマンドをサポートしました

$ which gh
/Users/shunsuke-suzuki/.aqua/bin/gh

$ aqua which gh
/Users/shunsuke-suzuki/.aqua/pkgs/github_release/github.com/cli/cli/v2.2.0/gh_2.2.0_macOS_amd64.tar.gz/gh_2.2.0_macOS_amd64/bin/gh

aqua は ~/.aqua/bin 配下にシンボリックリンクを作成し、コマンド実行時に動的にバージョンを決定し、インストールされたコマンドを実行します。 よって whichcommand -v では実際に実行されるコマンドのパスが分かりませんが、 aqua which コマンドを使うとパスが分かります。 ちなみに aqua で管理されてないコマンドでもパスを取得できます。

$ aqua which git
/usr/local/bin/git

少々変わった使い方ですが、 aqua でインストールされた実行ファイルを別のパスにコピーしたい場合に便利です。

$ cp "$(aqua which gh)" src/gh

github_archive, github_content type をサポートしました

Standard Registry にある package をインストールしているだけの方は、そもそも package の type というものを気にする必要もないので あまり関係ない話かもしれませんが、 http, github_release package に加えて、 github_archive, github_content package もサポートされました。 github_archive は GitHub Repository の Archive をパッケージとして扱うもので、 tfenv を aqua でインストールする際なんかに使われています。 github_content は GitHub Content API を使って GitHub Repository の単一ファイルをパッケージとして扱うものです。

(advanced) バージョンによってパッケージの type が変更された場合にも対応できるようになりました

これまた advanced な内容で多くの方にはあまり関係ない裏側の仕組みの話ですが、パッケージによってはバージョンによって type が変わることがあります(かなりまれですが)。 あとは repository の owner が変わったりなんかもありますが、そういった場合にも対応できるようになりました。 ちなみに GitHub Release の asset のファイル名のフォーマットや、 asset のディレクトリ構成が変わることは時々ありますが、それらには元々対応できています。

e.g. https://github.com/suzuki-shunsuke/aqua-registry/blob/v0.10.7/registry.yaml#L486-L492

https://github.com/suzuki-shunsuke/aqua/blob/v0.7.16/docs/registry_config.md#version_constraint-version_overrides

Standard Registry package の数が 139 => 220 になりました。

Standard Registry は v0.8.6 から v0.10.7 になりましたが、その結果 package の数は 139 から 220 になりました。

aqua のための CircleCI Orb をリリースしました

CircleCI で Orb を使って aqua を install したり aqua を使ってパッケージをインストールできるようになりました。 aqua とパッケージを cache してくれます。

· 4 min read
Shunsuke Suzuki

aqua v0.7.3 をリリースし、 複数の global configuration をサポートしました。

個人で使う分にはあまり嬉しい機能でもないかもしれませんが、 会社・組織・チームといった集団(以下組織で統一)で設定を共有するには便利な機能だと思います。

これまで aqua では 2 つの設定ファイルをサポートしていました。

  • -c で指定した場合はそのファイル、そうでなければカレントディレクトリから探索して最初に見つかったファイル
    • リポジトリ直下にそのリポジトリ用の aqua.yaml をおく
  • global configuration (デフォルトは ~/.aqua/global/[.]aqua.y[a]ml)
    • 個人の dotfiles とかで aqua.yaml を管理しておく

こうすることで特定のリポジトリ用の設定と個人の設定を管理することができます。

しかし、第三の設定を参照することはできませんでした。 例えばある組織で使うツールセットを aqua で管理しようと思ってもこれまでは難しかったです。

そこで AQUA_GLOBAL_CONFIG という環境変数に : 区切りで設定ファイルへのパスを設定することで先頭から順に設定ファイルを参照するようにしました。

設定ファイルの優先順位は高い方から順に次のようになります。

  1. -c で指定した場合はそのファイル、そうでなければカレントディレクトリから探索して最初に見つかったファイル
  2. AQUA_GLOBAL_CONFIG
  3. global configuration (デフォルトは ~/.aqua/global/[.]aqua.y[a]ml)

イメージとしては

  1. プロジェクト(リポジトリ)の設定
  2. 組織の設定
  3. 個人の設定

という感じです。

例えば GitHub Organizations に aqua-config というリポジトリを作成し、以下のようなファイルを用意したとしましょう。

  • all.yaml: 全 Developer 用の設定
  • sre.yaml: SRE Team 用の設定

そのリポジトリをローカル /home/foo/aqua-config に checkout したとしましょう。 あなたが SRE の場合、 AQUA_GLOBAL_CONFIG を次のように設定しましょう。

export AQUA_GLOBAL_CONFIG=/home/foo/aqua-config/sre.yaml:/home/foo/aqua-config/all.yaml

こうすることで特定のリポジトリ用の設定と個人の設定(dotfiles)に加えて、 自分が所属する組織用の設定も参照することができます。

組織としては組織に必要なツール群を aqua で宣言的に管理できるため、 セットアップのコストも下がり、バージョンも組織で統一することができます。

aqua install に追加された -a option

v0.7.3 では aqua install-a option が追加されました。 aqua install はデフォルトでは global configuration は参照しません。 global configuration を参照するのは aqua exec 及び aqua でインストールされたツールを実行したとき(内部的に aqua exec が実行されている)だけです。

-a option をつけると global configuration も含めて全ての設定ファイルを参照し install を実行します。

aqua.yaml を Git で管理している場合は定期的に リポジトリを pull して aqua i -a -l を実行するのが良いでしょう。 簡単なスクリプトを書いたり、 cron で定期実行するようにするとよいかもしれません。

· 4 min read
Shunsuke Suzuki

aqua v0.5.1 で追加された generate というサブコマンドを紹介します。

aqua では Registry を活用することで設定を記述する手間を省くことができますが、 Registry を活用するには、インストールしたいツールが Registry で定義されているか、されているとしたら name はなにか調べる必要があります。 Registry で定義されているのに見逃してしまうこともあるでしょう。

また、 aqua でツールをインストールするには version を指定する必要がありますが、多くの場合はとりあえず最新バージョンはなにかを調べることになるでしょう。

これらの手間を減らすために generate というインタラクティブなコマンドを追加しました。 これは aqua.yaml で指定されている Registry で定義されている packages の一覧から package を fuzzy search によって選択し、 更に github_release package の場合は release version の一覧を fuzzy search によって選択することで package の YAML 定義を出力するコマンドです。

使ってみるのが早いでしょう。 aqua.yaml に Standard Registry を追加した上で aqua g を実行してみます。

registries:
- type: standard
ref: v0.2.1 # renovate: depName=suzuki-shunsuke/aqua-registry

すると fuzzy search が起動し、 package が選択できます。 自分が使いたいツール名で検索してみて、あればそれを選択します。

  direnv (standard)
consul (standard)
conftest (standard)
> golangci-lint (standard)
47/47
>

golangci-lint を選んでみましょう。 すると golangci-lint のバージョンの一覧が選択できます。

  v1.40.0
v1.40.1
v1.41.0
v1.41.1
> v1.42.0
30/30
>

version を選ぶと YAML が出力されます。

$ aqua g
- name: golangci-lint
registry: standard
version: v1.42.0

この YAML を aqua.yamlpackages に追記すれば OK です。

helm のように GitHub Release からではなく公式サイトからインストールするようなツールはバージョンのリストを取得するのが難しいのでバージョンの選択はできません。

$ aqua g
- name: helm
registry: standard
version: ""

それでも helm が helm という name で standard registry に登録されていることはわかるので、十分便利です。

ちなみに fuzzy search には ktr0731/go-fuzzyfinder を使わせていただきました。非常に簡単に fuzzy search が実装できてとても便利でした。

· 9 min read
Shunsuke Suzuki

先日 aqua v0.1.0 をリリースした記事を書いたばかりですが、 そこから更に開発を続けて v0.5.0 をリリースしたので、変更点を紹介します。

基本的に Release Note に書いてあるとおりです。

  • PATH を project (aqua.yaml) 毎に設定する必要がなくなりました
    • ~/.aqua/bin を PATH に追加すればよくなりました
    • direnv などを使って環境変数を追加する必要がなくなりました
  • install コマンドに --test option を追加し、 file.src の設定が正しいかテストできるようになりました
    • CI で aqua の設定をテストするのに便利
  • GitHub Release だけでなく、任意の URL から tool のダウンロード・インストールができるようになりました
    • Go や helm, Hashicorp の product のような公式サイトからダウンロードするタイプのツールも install できるようになりました
  • Breaking Change: inline_registry の設定の形式を変更しました
  • aqua の設定の再利用性を高める Registry という仕組みを導入しました
  • 簡単な slide を公開しました: https://speakerdeck.com/szksh/introduction-of-aqua

PATH を project (aqua.yaml) 毎に設定する必要がなくなりました

aqua v0.1.0 では symbolic link を aqua.yaml のあるディレクトリの .aqua/bin 配下に作成しており、ここを PATH に追加する必要がありました。 direnv とかを使うと便利ですが、間接的に(?) aqua が direnv のようなツールに依存している形になり、微妙でした。

aqua v0.5.0 では symbolic link を ~/.aqua/bin 配下に作成するため、 .bashrc などで ~/.aqua/bin を PATH に追加しておけば project ごとに環境変数を追加する必要はなくなりました。 ちなみに作成される symbolic link は aqua-proxy へのリンクであり、ツールのバージョンには依存しないので ~/.aqua/bin を共有しても干渉することはありません。

~/.aqua/bin 配下に symbolic link を作って PATH に追加する場合、一つ大きな課題があります (だからこそ v0.1.0 ではプロジェクトごとに symbolic link を作っていました)。 ~/.aqua/bin 配下に symbolic link を作って PATH に追加すると、基本的にそのファイルが呼ばれることになります。 そのツールを aqua で管理しているプロジェクト配下ならそれで良いですが、そうでない場合、本来 aqua 以外でインストールしたものを実行したくても実行できません。 例えば homebrew で jq を install していて、あるプロジェクトでは aqua を使ってバージョンを固定したものを使いたいが、それ以外では homebrew で install したものを使いたいといった場合に問題になります。

この問題を解決するため、 aqua ではツールを呼び出す際に PATH をチェックして aqua-proxy へのリンクとなっているものは除外するというハック(?)のようなことをしています。

install コマンドに --test option を追加し、 file.src の設定が正しいかテストできるようになりました

地味な更新ですが、 aqua の設定を更新した際に CI でテストするのに便利です。 --test option なしだと、 warning は出力しますが、 exit code は 0 になります。

GitHub Release だけでなく、任意の URL から tool のダウンロード・インストールができるようになりました

ちなみに、 GitHub Release で公開されてないようなツールでも、 GitHub リポジトリで versioning されていて Renovate の github_release data source で自動更新できるケースは少なくないと思います。

Breaking Change: inline_registry の設定の形式を変更しました

小さな breaking change ですが、inline_registry の形式が変わりました。

AS IS

inline_registry:
- name: jq
type: github_release
repo_owner: stedolan
repo_name: jq
asset: 'jq-{{if eq .OS "darwin"}}osx{{else}}{{.OS}}{{end}}-{{.Arch}}'
files:
- name: jq

TO BE

inline_registry:
packages:
- name: jq
type: github_release
repo_owner: stedolan
repo_name: jq
asset: 'jq-{{if eq .OS "darwin"}}osx{{else}}{{.OS}}{{end}}-{{.Arch}}'
files:
- name: jq

aqua の設定の再利用性を高める Registry という仕組みを導入しました

これが一番大きな更新です。 aqua を使うにはツールのインストール方法を YAML で記述しないといけませんが、 これはちょっとした手間ですし、新規ユーザーにとって障壁となるでしょう。

ツールとそのバージョンを定義したらインストールできてほしいものです。 ツールとそのバージョンの定義は aqua.yamlpackages の部分なので、それ以外の設定を如何に簡略化するかという話になります。

Registry はツールのインストール方法の設定を、プロジェクト固有のバージョン設定とは独立させ、再利用可能な形で共有する仕組みです。

Registry には現状 4 種類あります。

  • inline registry: aqua.yaml の中に直接 install 方法を定義する。 v0.1.0 からサポートされている方法
  • github_content registry: GitHub Repository にあるファイルを Registry として参照する方法
  • local registry: GitHub Repository にあるファイルを Registry として参照する方法
  • standard registry: 自分がメンテしている github_content registry のエイリアス

inline registry

inline registry は従来からあるやつで、 aqua.yaml 内に定義する方法です。

inline_registry:
packages:
- name: jq
type: github_release
repo_owner: stedolan
repo_name: jq
asset: 'jq-{{if eq .OS "darwin"}}osx{{else}}{{.OS}}{{end}}-{{.Arch}}'
files:
- name: jq

シンプルではありますが、コピペする以外に再利用性がありません。

local registry

local registry はローカルにあるファイルを参照する registry です。 絶対パスか、 aqua.yaml からの相対パスを指定します。

github_content registry

ユーザーとしては次のように Registry を定義すればあとは packages で Registry を参照できます。 GitHub Access Token を環境変数 GITHUB_TOKEN として設定する必要があります。

registries:
- name: suzuki-shunsuke/aqua-registry
type: github_content
repo_owner: suzuki-shunsuke
repo_name: aqua-registry
ref: v0.2.0 # renovate: depName=suzuki-shunsuke/aqua-registry
path: registry.yaml

packages:
- name: conftest
registry: standard
version: v0.27.0 # renovate: depName=open-policy-agent/conftest

Registry を公開する

自分で Registry を公開したい場合は GitHub Repository に設定ファイルを置くだけで OK です。 e.g. https://github.com/suzuki-shunsuke/aqua-registry/blob/main/registry.yaml

Standard Registry

Standard Registry も作りました。

https://github.com/suzuki-shunsuke/aqua-registry

jq や gh, kubectl, Terraform など有名なツールはこの Registry を使えばインストールできますが、 当然 PR も受け付けているので、追加してほしいツールがあれば PR を投げてください。

Official Registry を github_content Registry として利用することも当然できますが、 より簡潔に書けるように type: standard という Registry がサポートされています。

AS IS

registries:
- name: standard
type: github_content
repo_owner: suzuki-shunsuke
repo_name: aqua-registry
ref: v0.2.0 # renovate: depName=suzuki-shunsuke/aqua-registry
path: registry.yaml

TO BE

registries:
- type: standard
ref: v0.2.0 # renovate: depName=suzuki-shunsuke/aqua-registry

上 2 つは等価ではありますが、後者のほうが簡潔です。

· 16 min read

2021-09-04 追記: aqua v0.1.0 から v0.5.0 での変更点

aqua という OSS を開発しているので紹介します。

記事の内容は aqua v0.1.0 に基づきます。将来的に仕様が変わる可能性があります。

aqua とは

aqua は CLI ツールのバージョン管理のための CLI です。 aqua で管理する主な対象は GitHub Release で公開されているツールです。 YAML の設定ファイルを書いてコマンドを実行すると指定したツールをインストールすることができます。

例えば以下のような設定ファイルを書き、 aqua install というコマンドを実行すると jq, conftest などが GitHub Release からダウンロードされ、インストールされます。

packages:
- name: jq
registry: inline
version: jq-1.6
- name: conftest
registry: inline
version: v0.27.0
inline_registry:
- name: jq
type: github_release
repo_owner: stedolan
repo_name: jq
asset: 'jq-{{if eq .OS "darwin"}}osx-amd64{{else}}{{if eq .OS "linux"}}linux64{{else}}win64.exe{{end}}{{end}}'
files:
- name: jq
- name: conftest
type: github_release
repo_owner: open-policy-agent
repo_name: conftest
asset: 'conftest_{{trimPrefix "v" .Package.Version}}_{{title .OS}}_x86_64.tar.gz'
files:
- name: conftest

ちなみに上記の設定ファイルの

  asset: 'conftest_{{trimPrefix "v" .Package.Version}}_{{title .OS}}_x86_64.tar.gz'

の部分では Go の text/templatesprig が使われています。

ツールごとに URL を調べて download して tarball などを展開してインストールしてなどの面倒な作業を aqua で自動化できます。 update も基本的に設定ファイルの version を更新するだけで OK です。

aqua を使うと同じツールの複数のバージョンを管理してプロジェクトによってバージョンを切り替えるといったことも容易にできます。

3 つの主なユースケース

aqua では以下の 3 つの主なユースケースを想定しています。

  • CI/CD で必要なツールの管理
  • ローカルでの開発に必要なプロジェクト(リポジトリ)固有のツールの管理
  • 特定のプロジェクト(リポジトリ)によらないツールの管理

ユースケース1: CI/CD で必要なツールの管理

例えば Terraform の Monorepo の CI で以下のような様々なツールを使っていたとしましょう。

これらを1個1個 curl などを使ってインストールするコードを書くのは面倒ですが、 aqua であれば設定ファイルを宣言的に書いて aqua i を実行すれば終わりです。 新たにツールを追加する場合でも設定ファイルに追記すればよく、スクリプトを更新する必要はありません。 バージョンを明示的に指定できるのでコードを変更してないのに急にツールが更新されることもありませんし、 Renovate の Regex Manager などを使えば更新を自動化することもできます。

ユースケース2: ローカルでの開発に必要なプロジェクト(リポジトリ)固有のツールの管理

あるリポジトリのローカルでの開発に必要なツールを aqua で管理することができます。 リポジトリ直下に aqua.yaml を置いておけば OK です。 バージョンも指定されているので、人によってバージョンが違ったりする問題も解消できます。 aqua.yaml と同じディレクトリに .aqua が作成されるのでそれを .gitignore に追加し、 .aqua/bin を PATH に追加しましょう。 direnv を使い、リポジトリ直下に .envrc を置いて .aqua/bin を PATH に追加すると便利です。

aqua.yaml
.aqua/bin
.envrc

.envrc

PATH_add .aqua/bin

.aqua/bin を PATH に追加しなくても aqua exec -- <コマンド> ... で実行することもできます。

ユースケース3: 特定のプロジェクト(リポジトリ)によらないツールの管理

特定のプロジェクトによらずにツールを laptop にインストールしたい場合にも使えます。 ~/.aqua/global/aqua.yaml に設定ファイルを記述し、 ~/.aqua/global/.aqua/bin を PATH に追加してください。

export PATH=$HOME/.aqua/global/.aqua/bin:$PATH

そして ~/.aqua/global 配下で aqua i を実行すればインストールができます。 ~/.aqua/global を Git で管理して GitHub などでホスティングするのも良いでしょう。

https://github.com/suzuki-shunsuke/my-aqua-config

akoi との違い

ところで、自分は aqua に似たツールとして akoi というツールを公開していて、自分もこれまでこのツールを使ってきました。 aqua と akoi は「CLI ツールのバージョン管理」という目的・ゴールは同じです。 akoi も結構便利なツールですが、 akoi が抱える様々な課題を解決するために aqua を開発しています。 aqua は akoi のいわば後継ツールです。 ただしコードは全く別物ですし、互換性もありません。

akoi と比べた aqua の良い点

  • GitHub Access Token を使ったインストールをサポート
    • private repository をサポート
    • akoi は anonymous なアクセスなので rate limit に引っかかりやすい
  • 管理対象のコマンド実行時にツールのインストールが可能
  • 設定ファイルを更新したあとに install コマンドを実行する必要がない
    • akoi は symbolic link を作り直すために install コマンドを実行する必要がある
  • 管理対象のツールの実体を共有できる
    • project ごとにツールを install する必要がない(計算資源の効率化)
    • akoi と違って ツールによって実体のインストール先は一意に決まるので、干渉することがなく安全に共有できる
  • 事前に archive の中のパスを知っている必要がない
    • akoi は install 時に archive を展開してファイルをコピーし、シンボリックリンクを作成する
      • パスが間違っていると失敗し、 download からやり直しになる
      • そのため、新しいツールを akoi で管理する場合はまず archive の構造を調べる必要がある
    • aqua は install したあとに ~/.aqua 配下を見て file.src を修正すれば良いし、間違っててコマンドの実行に失敗しても download のやり直しとかはない
  • bin_path, link_path ような設定について考えなくて良い
    • akoi は設定ファイルでインストール先などを設定できるようになっている
    • どう設定すべきか悩ましいし、リポジトリによって設定が違ったりして設定を統一するのが難しい
    • aqua はインストール先などが設定できないのでユーザーが迷う必要がない

管理対象のツールの実体を共有できる

aqua はツールの実体を AQUA_ROOT_DIR ~/.aqua にインストールし、共有することができます。 複数のリポジトリで同じバージョンの同じツールを使う場合に共有できるので、 インストールにかかる時間を短縮できますし、無駄にディスク容量を消費することもありません。 設定ファイルによって動的にバージョンを取得するので、共有していてもリポジトリごとに異なるバージョンを使うこともできます。

安全に共有できるようにツールの実体のインストール先はダウンロード元によってユニークかつ一意に決まるようになっています。 ユーザーがカスタマイズすることはできません(ルートディレクトリは変えられますが、ルート以下は変えられません)。

例えば OSX で jq-1.6 のインストール先は以下になります。

.aqua/pkgs/github_release/github.com/stedolan/jq/jq-1.6/jq-osx-amd64/jq-osx-amd64

このように GitHub Release からインストールする場合

  • リポジトリのオーナー
  • リポジトリ名
  • tag
  • GitHub Release のアセット名

などから一意に決まるため、あるリポジトリでは jq をフォークしたものを使うといった場合でも安全に共存することができます。

aqua install を実行するとツールごとに以下のことが実行されます。

  1. .aqua/bin 配下にシンボリックリンクを作成
  2. ダウンロード
  3. tarball などの展開
  4. ~/.aqua 配下にインストール

aqua.yaml の packages に大量のツールが定義されていると、 大量のツールが一度にインストールされることになり、 並列で実行されるとはいえ、都合が悪いこともあるでしょう。

--only-link option をつけて実行すると、シンボリックリンクだけ作成しダウンロードなどは行わないので直ぐに終わります。

$ aqua install --only-link

その状態でツールを実行すると、ツールが自動でインストールされてから実行されるので 本当に必要になってからインストールすることが可能であり、余計なインストールが発生しないので便利です。

コマンド実行時の自動インストール、動的なバージョン切り替えの仕組み

aqua は設定ファイルを更新すると aqua install 実行をしなくても更新が反映される、 ツールがまだインストールされていなくてもツールを実行時に自動でインストールされるという機能があります。

tfenv も .terraform-version を更新すればすぐ反映されますし、 terraform コマンドを実行時にまだ指定したバージョンがインストールされてなかったら自動でインストールされますが、それに似ていますね (ただし tfenv の機能がどう実装されているかは調べてませんし、 aqua を実装する上で参考にしたりはしていません)。

上記の機能が aqua でどう実現されているか簡単に説明します。

例えば aqua で jq をインストールし、 jq -h を実行したとしましょう。 jq を実行すると aqua-proxy を経由して aqua exec -- jq -h が実行されます。 この辺の詳細は aqua-proxy とは を参照してください。 aqua exec は aqua の設定ファイルで指定されたバージョンがインストールされているかチェックし、まだインストールされていなかったらインストールし、コマンド jq -h を実行します。

aqua-proxy とは

aqua-proxy は aqua が内部的に依存しているツールです。 コマンド実行時に aqua 及び aqua で管理するツールのバージョンを動的に変更するために作られました。 aqua のために開発されており、 aqua 以外で使われることは想定していません。

aqua-proxy は aqua installaqua exec を実行した際に自動で ~/.aqua/bin/aqua-proxy にインストールされます。 aqua はツールをインストールする際に .aqua/bin/<ツール> から ~/.aqua/bin/aqua-proxy へのシンボリックリンクを作成するので、 <ツール> を実行すると ~/.aqua/bin/aqua-proxy が呼ばれます。

aqua-proxy は os#Args からツール名を取得し、 aqua exec -- <ツール名> ... を実行します。 これによりコマンド実行時に aqua 及び <ツール> のバージョンを動的に変更することを実現しています。

.aqua/bin/<ツール> から aqua-proxy へのシンボリックリンクは静的であり、 aqua-proxy のバージョンを切り替えることは難しいです。 aqua-proxy の機能・責務が大きくなると aqua-proxy のバージョン管理や aqua との互換性を考えなくてはならなくなります。 aqua-proxy のバージョンをほぼ気にしなくて良いよう、 aqua-proxy は最小限の機能・責務しか持たず、安定的であまり変更されないように設計されています。

プロセスツリーを確認してみる

既に説明したとおり <ツール> を実行した際には実はプロセスツリー的には aqua-proxy => aqua => <ツール> という風になっています。

<ツール> を直接実行した場合と挙動に違いが出ないように以下のようなことに気を配っています。

  • SIGINT, SIGTERM などのシグナルが適切に <ツール> のプロセスまで伝達されるようにする
  • <ツール> の exit code が伝達されるようにする

試しに fzf を実行してみて別のターミナルでプロセスツリーを確認してみます。

$ ls | fzf

fzf が起動しますが、そのままにしておいて別のターミナルでプロセスツリーを確認してみます。 Mac の pstree を使っています。

-+- 83548 foo fzf
\-+- 83549 foo aqua exec -- fzf
\--- 83550 foo /Users/foo/.aqua/pkgs/github_release/github.com/junegunn/fzf/0.27.2/fzf-0.27.2-darwin_amd64.zip/fzf

紛らわしいのですが、最初のプロセスの実体は fzf ではなくて aqua-proxy です。 fzf が aqua-proxy へのシンボリックになっているのでこうなっています。 ここで aqua-proxy に SIGTERM を送ると手元の Mac ではちゃんと子プロセスまで終了しました。

$ kill 83548

この辺のシグナルハンドリングは Windows だと正常に動かないかもしれません。

https://pkg.go.dev/os#Signal

The only signal values guaranteed to be present in the os package on all systems are os.Interrupt (send the process an interrupt) and os.Kill (force the process to exit). On Windows, sending os.Interrupt to a process with os.Process.Signal is not implemented; it will return an error instead of sending a signal.