開発環境にはバージョン管理システム(asdf)を導入しよう


# はじめに

あなたは、日頃どの言語を使って開発をしていますか？
この問いには、人によって様々な（おそらく多くの方は2つ以上の）答えが返ってくるかと思います。

では、その言語のバージョンを把握していますか？
これは、あまり把握していない方も一定数いらっしゃるのではないでしょうか。

もしあなたが「ナニソレ....」と思ったのであれば、本記事で「言語のバージョンを管理する」ということについて理解を深めていただければと思います。

# 言語のバージョンを管理する重要性

プログラミング言語もヒトの手によって作成されるものですので、日々アップデートが加えられています。
それに伴い、仕様変更が行われることもしばしばあります。

筆者のメイン言語であるPythonを例に取れば、2021年10月に正式リリースされたPython3.10.0で以下の機能が追加されました。

### 構造的パターンマッチング（match文）
```python
def status_check(status: int) -> str:
    match status:
        case 1:
            return "success"
        case 2:
            return "error"
        case _:
            return "undefined"

# 以前はif文やdictを使って行っていた
```
### 共用体型オペレータ
```python
variable: int | float

# 以前はvariable:　Union[int, float]としていた
```
それぞれについて本記事で詳細は触れませんので、「新しい文法が実装されたんやな」と思っていただければOKです。
※ [詳しく知りたい方はこちら](https://qiita.com/ksato9700/items/2cd85bec2ae4c1785238)（@ksato9700さんの記事）

さて、上記のように**プログラミング言語も日々変わっていくものだ**（当然、新機能が追加されることもあれば、これまであった機能が削除/非推奨(deprecated)になることもある）ということを認識いただいた上で、下記のようなシナリオを考えてみましょう。

> 1. あなたは、**プロジェクトA**で**Python3.8**を使ってシステム開発を行いました。
> 2. プロジェクトA完了後、**Python3.10**がリリースされたので、ローカルのPythonを更新してから**プロジェクトB**でのシステム開発を行いました。
> 3. プロジェクトBの途中で、プロジェクトAで作成したシステムの改修を行うことになったので、そのままシステムを更新しました。

このとき、1の時点では`Python3.8`、2の時点では`Python3.10`がローカルでは利用されています。
その状態でそのまま3の修正作業を行うと、以前の開発時やデプロイ環境とは異なるバージョンのPythonを用いて開発を行うことになります。

プログラミング言語が後方互換性を持たないことは（ほぼ）ないのですが、望ましい状況ではないことはお分かりいただけるかと思います。

このとき、3を行う前に`Python3.10`から`3.8`にダウングレードすればよいのですが、exeインストーラー（Windows）やhomebrew（mac）などを用いてPythonを導入している場合、このダウングレードは大変面倒です。
場合によっては、パスを通し直したりする必要も出てきます。

そこで、これを簡単に行えるようにしてくれるのが、本題である **「バージョン管理システム」** です。

# バージョン管理システムのいろいろ

:::note
ここからは、macユーザを想定した記述になります。
:::

さて、ここまでの説明をまとめると、バージョン管理システムとは
> **「プロジェクトごとに異なるバージョンのプログラミング言語(処理系)を使えるようにしてくれる便利なヤツ」**

ということになります。

さて、この便利なヤツはいくつか存在していますが、その中でも代表的なのは以下の2つです。
>1. *env系 / anyenv 
>2. **asdf**

まずは前者について。
*env系とは、各言語専用のバージョン管理システムの総称で、`jenv(Java)`、`pyenv(Python)`などが存在します。
ただ、言語ごとに`〇〇env`を導入することになり、それぞれをいちいち管理するのは大変面倒なので、それらをまとめて管理し、`〇〇env`自体のアップデートなどを自動でやってくれたりする`anyenv`が存在します。

ここまで聞くと便利そうなのですが、あくまでもそれぞれの`〇〇env`は独立しているので、細かいコマンドや仕様が違ったりしてイラつくポイントも存在します。

そこで、本記事でおすすめするのは対抗馬`asdf`です。

# asdfとは？

[asdf](https://asdf-vm.com/)もバージョン管理システムの一種ですが、そのつくりに特徴があります。

### 一貫性のあるインターフェース

asdfは、あくまでも多様な言語や各種ツールについて、そのバージョン管理を行うための**インターフェース**を提供しているに過ぎず、具体的な実装は全てプラグインに丸投げしています。
要は、**「統一されたインターフェースを使ってプラグインを動かすことで、全ての言語・ツールを同じコマンドで管理できる」** のです。

### 多様なプラグイン

前述の通り、asdfはプラグインを導入することで動作します。
このプラグイン群は、本記事執筆時点で549個が存在し、主要な言語処理系のほぼ全てが含まれる他、MySQLやPostgreSQLなどのミドルウェアにも対応しています。


上記の2つがasdfの大きな特徴として挙げられます。
まとめれば、「**統一された操作**で、**様々な開発環境のバージョン管理ができる**」のがasdfなのです。


# asdfの導入

ここまでの説明で、asdfを導入すべき理由はおわかりいただけたでしょうか？

お分かり頂けていない方はとっくにブラウザバックしていることかと思いますので、実際の導入についてご紹介します。

ただ、具体的な手順は[公式リファレンス](https://asdf-vm.com/guide/getting-started.html)が大変詳しいので、ここでは簡単に流れを説明します。
この記事と、公式リファレンスを参照しながら進めていただければと思います。

## homebrewを用いてmacにasdfを導入する

### 1. homebrewでインストール
```sh
brew install asdf
```

### 2. `.zshrc`に`asdf.sh`を追加
:::note warn
homebrew + zsh(macデフォルトシェル)のコマンドを掲載しています。
その他の環境の方は、[こちら](https://asdf-vm.com/guide/getting-started.html#_3-install-asdf)から自分の環境に合ったコマンドを実行してください。
:::
```sh
echo -e "\n. $(brew --prefix asdf)/libexec/asdf.sh" >> ${ZDOTDIR:-~}/.zshrc
```

### 3. シェルを再起動
```sh
exec $SHELL -l
```

ここまでで、asdfのコア(インターフェース)部分のインストールができました。

### 4. プラグインを検索

ここからは、実際にプラグインをインストールしてみます。
デモとして、pythonのバージョン管理プラグインを導入してみます。もし他の言語のプラグインを導入する場合は、適宜置き換えてください。

```sh
asdf plugin list all | grep python
# >> python                       *https://github.com/danhper/asdf-python.git
```

ここで出力された行は、`https://github.com/danhper/asdf-python.git`というリポジトリの内容が`python`というプラグイン名で登録されているよ、ということを表します。
このプラグイン名`python`を用いてasdfの操作を行っていきます。

### 5. プラグインを導入

```sh
asdf plugin add python
```

この1行で、asdfにプラグインがインストールされます。

実際に確認してみましょう。
```sh
asdf plugin list
# >> python
```
`asdf plugin list`コマンドは、現在インストールされているプラグインを一覧表示してくれるコマンドです。
ここに`python`が存在していれば、インストールは成功しています。

### 6. 特定バージョンのPythonをインストールする

プラグインをインストールしたので、実際に特定バージョンのPythonをインストールしてみます。

まずは、現在asdf上で利用可能なPythonのバージョンを調べましょう。Python3.X系に限定して検索します。

```sh
asdf list all python 3
```

このコマンドを実行すると、大量の文字列が出力されるかと思います。これらは全て1行1つのバージョンを表します。

この中で、記事執筆時点での最新版である`3.11.0`を探してみましょう。多分下の方にあるはずです。

見つかったら、そのバージョン名を使ってインストールを実行します。

```sh
asdf install python 3.11.0 
```
:::note
`asdf install python latest`で、その時点での最新安定版を自動的に検索してインストールしてくれます
:::
:::note warn
ここでビルド中にエラーが発生した場合は、エラーメッセージで調べると何かしら出てくると思います。大抵M1 or M2 Mac特有の事象ですので、見つからなかったら`M1`を検索ワードに追加しても良いかもしれません。
:::

ビルドが終了したら、導入できているか確認してみましょう。

```sh
asdf list python
# "all"はナシです
```
ここに、`3.11.0`が表示されていればOKです。


### 7. グローバル／ディレクトリごとにバージョン指定を行う

おまたせしました、ここがasdfの使い所の根幹です。

まずは、グローバル、つまりPC全体でデフォルトで利用されるPythonのバージョンを設定してみましょう。

```sh
asdf global python 3.11.0
```

これで完了です。
例によって、実際にできているか確認してみます。
```sh
python --version
# >> Python 3.11.0
```

これで、PC全体でのデフォルトバージョンの変更ができました。
新しいバージョンが出たときなどに、インストールとあわせて実行するとよいでしょう。

さて、asdfにできることはこれだけではありません。
「**ディレクトリごとに**バージョンを変更する」ことも可能です。ここがバージョン管理システムの良いところです。

下準備として、`Python3.10.8`をインストールしておきます。
本記事では、`Python3.11.0`と`3.10.8`が併存する環境を作ってみます。

```sh
asdf install python 3.10.8
```

ビルドが完了したら、適当なディレクトリに移動します(新しく作成すると変なエラーを起こさないのでオススメです)

```sh
mkdir ~/testdir
cd ~/testdir
```

移動したら、そこで以下のコマンドを実行します。

```
asdf local python 3.10.8
```

そうしたら、例によって以下のコマンドでPythonのバージョンを確認してみます。

```
python --version
# >> Python 3.10.8
```

このディレクトリ内では`Python3.10.8`が使用されていることがわかりました。

では、ディレクトリ外ではどうでしょうか。確認してみましょう。

```sh
cd ~
python --version
# >> Python 3.11.0
```

ディレクトリ外では、グローバルに設定した3.11.0が使われていることがわかります。

これが、asdfの主な使い方です。

### 8. コマンド一覧

```sh
# ---プラグイン関係---

# インストール可能なプラグインのリストを表示
asdf plugin list all
asdf plugin list all | grep XXX   grepで絞り込むと良い

# インストール済みのプラグインのリストを表示
asdf plugin list

# プラグインの追加
asdf plugin add <プラグイン名>

# プラグインの更新
asdf plugin update --all

# プラグインの削除
asdf plugin remove <プラグイン名>

# ---バージョン管理---
# インストール可能なバージョンのリストを表示
asdf list all <プラグイン名>
asdf list all <プラグイン名> <バージョン(前方一致)> # 絞り込みが可能

# インストールされているバージョンのリストを表示
asdf list <バージョン名>

# インストール
asdf install <プラグイン名> <バージョン>
asdf install <プラグイン名> latest  # 最新の安定版を自動でインストール

# バージョンを削除
asdf uninstall <プラグイン名> <バージョン>

# グローバルなバージョン指定
asdf global <プラグイン名> <バージョン>

# ディレクトリ内でのバージョン指定
asdf local <プラグイン名> <バージョン>

# 設定されているバージョンの確認
asdf current
```

# おわりに

如何でしたか？
是非、お手持ちのPCにasdfをインストールして、より快適な開発を行っていただければと思います！