asdf で開発言語と利用ツールのバージョン管理

本記事は [フューチャー Advent Calendar 2023](https://qiita.com/advent-calendar/2023/future) 17日目の記事です。

昨日は、[@ShimizuJimmy](https://qiita.com/ShimizuJimmy) さんの「[LLMと略語の同定](https://qiita.com/ShimizuJimmy/items/f83b6e0b4f9a43130f10)」でした。今年は大規模言語モデル(LLM)が「ビジネスや日常生活で多用されるようになった年」であり、OpenAI の [ChatGPT](https://chat.openai.com/)、GitHub の [Copilot](https://github.com/features/copilot)、Google の [Bard](https://bard.google.com/) などは、多くの開発エンジニアが「アシストツール」として多用していると思います。また、記事内での「日々利用する中で生じる小さな疑問を見逃さずに検証し続けることが重要」との記述からは、私自身も AI に任せっきりにせず、自分で検証することが重要なんだなと再認識しました。

# はじめに
みなさんは、開発環境でのバージョン管理には、どの様に対処されていますか？

自分の担当範囲が「1プロダクト、1開発環境」で済むならば、バージョン管理は意識せずに「パッケージの直ダウンロード」でも問題ないと思います。しかしながら、エンジニアとしてのキャリアを続けていれば、いずれは「複数プロダクト、複数開発環境」にうまく対処することが求められます。他にも、新しくリリースされたバージョンの新機能を使いたい場合に、環境をサクッと変えて検証できるとスマートですよね。実際の方法としては、以下のような方法があると思います。

* 開発で使う PC を変える（物理 PC を変える）
* [WSL](https://learn.microsoft.com/ja-jp/windows/wsl/) や [Docker](https://www.docker.com/) などの仮想化技術を使う
* [anyenv](https://github.com/anyenv/anyenv) や [asdf](https://asdf-vm.com/) などのバージョン管理ツールを使う
* その他

今回は、「バージョン管理ツール」の [asdf](https://asdf-vm.com/) にフォーカスを当てて、その設定方法と利用方法を説明していきます。

また、本記事の内容は、以下環境にて実行しています。

```bash
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.2 LTS (Jammy Jellyfish)"
```

# TL;DR
* [asdf](https://asdf-vm.com/) はバージョン管理ツールです。
* `$ asdf help` で作業コマンドの情報は一通り揃います。
* asdf は左手で「小指→薬指→中指→人差し指」の順番です。覚えやすいですね。

## `asdf` 利用の最短ルート

```bash
# asdf の取得
$ git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.13.1

# .bashrc に追記
$ vim ~/.bashrc
$ cat ~/.bashrc
~
. "$HOME/.asdf/asdf.sh"
. "$HOME/.asdf/completions/asdf.bash"
~

# plugin のインストール
## asdf plugin add <name> [<git-url>]
$ asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git

# version のインストール
## asdf install <name> <version>
$ asdf install nodejs 21.4.0

# version の有効化
## asdf local <name> <version>
$ asdf local nodejs 21.4.0
```

# asdf の全体像

asdf の利用フローを図示すると、以下のようになります。

① asdf のインストール
② plugin のインストール
③ version のインストール
④ version の有効化

![コメント 2023-12-16 225015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261058/8311cc2a-d612-6eb9-cce1-db51a2cdc720.png)

それぞれの操作コマンドを見ていきます。

## ① asdf のインストール

asdf の [Getting Started](https://asdf-vm.com/guide/getting-started.html) を見ながら作業を進めます。
依存パッケージとして `curl` と `git` が指定されていますので、もしローカル環境に未インストールであれば追加します。

```bash:Aptitude
$ apt install curl git
```

続いて、[GitHub リポジトリ](https://github.com/asdf-vm/asdf?tab=readme-ov-file) から asdf の本体をダウンロードします。

```bash
$ git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.13.1
(略)

$ ls ~/.asdf/
CHANGELOG.md     README.md    asdf.fish  asdf.sh            completions  downloads  lib         scripts  tmp
CONTRIBUTING.md  SECURITY.md  asdf.nu    ballad-of-asdf.md  defaults     help.txt   plugins     shims    version.txt
LICENSE          asdf.elv     asdf.ps1   bin                docs         installs   repository  test
```

ダウンロードが完了したら、`~/.bashrc` に以下を追記します。
（参照: ▼ Bash & Git）

```sh:~/.bashrc
. "$HOME/.asdf/asdf.sh"
. "$HOME/.asdf/completions/asdf.bash"
```

`$ which asdf` によりパスが通っていることが確認できたら、asdfのインストールは完了です。

```bash
$ which asdf
/home/me/.asdf/bin/asdf
```

`$ asdf help` により表示されるコマンド一覧は、ドキュメントの [All Commands](https://asdf-vm.com/manage/commands.html) でも確認可能です。

```bash
$ asdf help
version: v0.13.1-0586b37

MANAGE PLUGINS
asdf plugin add <name> [<git-url>]      Add a plugin from the plugin repo OR,
                                        add a Git repo as a plugin by
                                        specifying the name and repo url
asdf plugin list [--urls] [--refs]      List installed plugins. Optionally show
                                        git urls and git-ref
asdf plugin list all                    List plugins registered on asdf-plugins
                                        repository with URLs
asdf plugin remove <name>               Remove plugin and package versions
asdf plugin update <name> [<git-ref>]   Update a plugin to latest commit on
                                        default branch or a particular git-ref
asdf plugin update --all                Update all plugins to latest commit on
                                        default branch


MANAGE PACKAGES
asdf current                            Display current version set or being
                                        used for all packages
asdf current <name>                     Display current version set or being
                                        used for package
asdf global <name> <version>            Set the package global version
asdf global <name> latest[:<version>]   Set the package global version to the
                                        latest provided version
asdf help <name> [<version>]            Output documentation for plugin and tool
asdf install                            Install all the package versions listed
                                        in the .tool-versions file
asdf install <name>                     Install one tool at the version
                                        specified in the .tool-versions file
asdf install <name> <version>           Install a specific version of a package
asdf install <name> latest[:<version>]  Install the latest stable version of a
                                        package, or with optional version,
                                        install the latest stable version that
                                        begins with the given string
asdf latest <name> [<version>]          Show latest stable version of a package
asdf latest --all                       Show latest stable version of all the
                                        packages and if they are installed
asdf list <name> [version]              List installed versions of a package and
                                        optionally filter the versions
asdf list all <name> [<version>]        List all versions of a package and
                                        optionally filter the returned versions
asdf local <name> <version>             Set the package local version
asdf local <name> latest[:<version>]    Set the package local version to the
                                        latest provided version
asdf shell <name> <version>             Set the package version to
                                        `ASDF_${LANG}_VERSION` in the current shell
asdf uninstall <name> <version>         Remove a specific version of a package
asdf where <name> [<version>]           Display install path for an installed
                                        or current version
asdf which <command>                    Display the path to an executable


UTILS
asdf exec <command> [args...]           Executes the command shim for current version
asdf env <command> [util]               Runs util (default: `env`) inside the
                                        environment used for command shim execution.
asdf info                               Print OS, Shell and ASDF debug information.
asdf reshim <name> <version>            Recreate shims for version of a package
asdf shim-versions <command>            List the plugins and versions that
                                        provide a command
asdf update                             Update asdf to the latest stable release
asdf update --head                      Update asdf to the latest on the master branch

RESOURCES
GitHub: https://github.com/asdf-vm/asdf
Docs:   https://asdf-vm.com

PLUGIN nodejs
  asdf nodejs nodebuild
  asdf nodejs resolve
  asdf nodejs update nodebuild

"Late but latest"
-- Rajinikanth
```

以上で、asdf のインストールは完了です。

## ② plugin のインストール

続いて、plugin のインストール作業を進めます。

plugin の一覧は、`$ asdf plugin list all` コマンドと、[asdf-plugins の Plugin List](https://github.com/asdf-vm/asdf-plugins?tab=readme-ov-file#plugin-list) により確認できます。

First Party Plugins としては [Ruby](https://github.com/asdf-vm/asdf-ruby), [Node.js](https://github.com/asdf-vm/asdf-nodejs), [Erlang](https://github.com/asdf-vm/asdf-erlang), [Elixir](https://github.com/asdf-vm/asdf-elixir) があり、開発コミュニティ（[asdf-community](https://github.com/asdf-community)）による Plugins では [Golang](https://github.com/asdf-community/asdf-golang), [Terraform](https://github.com/asdf-community/asdf-hashicorp) など、多数の開発言語やツールが揃えられています。

Plugins の [Existing Plugins](https://github.com/asdf-vm/asdf-plugins?tab=readme-ov-file#existing-plugins) には、

* リストアップされているプラグインは安定版であり、かつ、積極的にメンテナンスされているべきです
* 特定のプラグインに問題がある場合には、まずはプラグイン先のリポジトリにissueを立ててください
* 非推奨のプラグインが見つかった場合は、代わりのプラグインに更新するPRを作成してください

との記載があり、継続的なメンテナンスの意志が伺えます。

今回は Node と Golang の Plugin をインストールします。

```bash
# asdf plugin add <name> [<git-url>]

$ asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
$ asdf plugin add golang https://github.com/asdf-community/asdf-golang.git
```

```bash
$ asdf plugin list
golang
nodejs
```

以上で、asdf による plugin のインストールは完了しました。

## ③ version のインストール

追加された plugin からは、インストール可能なバージョン情報が確認できます。


```bash
# asdf list all <name> [<version>]

$ asdf list all nodejs 21
21.0.0
21.1.0
21.2.0
21.3.0
21.4.0

$ asdf list all golang 1.21
1.21.0
1.21rc1
1.21rc2
1.21rc3
1.21rc4
1.21.1
1.21.2
1.21.3
1.21.4
1.21.5
```

せっかくなので、作業時点でのそれぞれの最新バージョンをインストールします。

* nodejs
    * 21.4.0
* golang
    * 1.21.5

```bash
# asdf install <name> <version>
$ asdf install nodejs 21.4.0
Trying to update node-build... ok
Downloading node-v21.4.0-linux-x64.tar.gz...
-> https://nodejs.org/dist/v21.4.0/node-v21.4.0-linux-x64.tar.gz
Installing node-v21.4.0-linux-x64...
Installed node-v21.4.0-linux-x64 to /home/me/.asdf/installs/nodejs/21.4.0

$ asdf install golang 1.21.5
Platform 'linux' supported!
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 63.5M  100 63.5M    0     0  1539k      0  0:00:42  0:00:42 --:--:-- 1490k
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    64  100    64    0     0    271      0 --:--:-- --:--:-- --:--:--   272
verifying checksum
/home/me/.asdf/downloads/golang/1.21.5/archive.tar.gz: OK
checksum verified
```

インストールしたバージョンは `$ asdf list` により確認できます。

```bash
$ asdf list
golang
 *1.21.5
nodejs
 *21.4.0
```

以上で、version のインストールは完了です。

## ④ version の有効化

最後に、version を有効化して、それぞれのバージョンが適切に設定されていれば、asdf の設定は終了です。

```bash
# asdf local <name> <version>
$ asdf local nodejs 21.4.0
$ asdf local golang 1.21.5
```

```bash
$ node -v
v21.4.0

$ go version
go version go1.21.5 linux/amd64
```

`local` により version を有効化すると、コマンドを実行したディレクトリに `.tool-versions` が作成されます。
（`global` で有効化した場合には、ホームディレクトリ配下 `~/.tool-versions` に作成）

```bash
$ cat .tool-versions
nodejs 21.4.0
golang 1.21.5
```

## `.tool-versions` の配布

GitHub などで `.tool-versions` を配布することで、環境構築の手間を減らせます。

具体的には、以下図での「③ version のインストール」と「④ version の有効化」が `$ asdf install` のコマンド一発で代用できます。ただし、asdf の実行環境と、該当 plugin の事前インストール作業はスキップできません。

![コメント 2023-12-16 225355.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261058/00b8f678-3a43-c44b-6b90-60faeea30aa2.png)

```bash
$ pwd
/home/me/sandbox

$ ls -la
total 12
drwxr-xr-x 2 me me 4096 Dec 16 22:32 ./
drwxr-xr-x 7 me me 4096 Dec 16 22:16 ../
-rw-r--r-- 1 me me   28 Dec 16 22:17 .tool-versions

$ cat .tool-versions
nodejs 21.4.0
golang 1.21.5

# 動作検証用に、version を削除
$ asdf uninstall golang 1.21.5
$ asdf uninstall nodejs 21.4.0
$ asdf current
golang          1.21.5          Not installed. Run "asdf install golang 1.21.5"
nodejs          21.4.0          Not installed. Run "asdf install nodejs 21.4.0"

$ asdf install
# golang
Platform 'linux' supported!
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 63.5M  100 63.5M    0     0   963k      0  0:01:07  0:01:07 --:--:--  978k
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    64  100    64    0     0    277      0 --:--:-- --:--:-- --:--:--   278
verifying checksum
/home/me/.asdf/downloads/golang/1.21.5/archive.tar.gz: OK
checksum verified

# nodejs
Trying to update node-build... ok
Downloading node-v21.4.0-linux-x64.tar.gz...
-> https://nodejs.org/dist/v21.4.0/node-v21.4.0-linux-x64.tar.gz
Installing node-v21.4.0-linux-x64...
Installed node-v21.4.0-linux-x64 to /home/me/.asdf/installs/nodejs/21.4.0

$ asdf current
golang          1.21.5          /home/me/sandobx/.tool-versions
nodejs          21.4.0          /home/me/sandobx/.tool-versions
```

以上で、本記事の内容は終了です。

# おわりに

本記事では [asdf](https://asdf-vm.com/) のインストール方法と利用手順について説明しました。

asdf は `$ asdf help` によるヘルプ表示が充実していますので、初学者や新規参画者のキャッチアップコストが比較的低いなと感じました。バージョン管理は「チームで利用するバージョン情報」だけでなく、メンバーそれぞれが抱える「個人的な事情」も影響するため、特定のツール利用を強制することは難しいのですが、こういった `asdf` のような選択肢があるだけでも、環境構築コストの削減につながりそうだなと思いました。

[フューチャー Advent Calendar 2023](https://qiita.com/advent-calendar/2023/future) はまだまだ続きますので、今後の記事もお楽しみに！