マルチユーザー環境のための anyenv

# 概要
## はじめに
以前 [マルチユーザ環境のための pyenv](https://qiita.com/takkeybook/items/4640799385e0fc4be915) を記載したのですが、修正点がいまいちだった気がするのと、Python 環境以外にも適用したいと思い、今回 anyenv で同じ環境を作るための方法を紹介します。

## 動機
大昔のワークステーションではHDDなどの資源が限られていたので、共有可能なリソース（ツールとかライブラリとか）は散在させないという考え方がありました。今では数TBのストレージが安価に手に入るので、各自が Ruby やら Python やらをインストールしても大した資源の無駄遣いと感じることはないでしょう。そう考えると、ここで記載する内容はあまり意味がないのかもしれません。

しかし、フリーのエンジニアが複数案件を同一PCで作業するにあたり、作業の安全性等の観点からユーザーを切り替えて作業するような状況を考えてみましょう。ユーザーごとに Ruby やら Python をインストールするのは面倒な気がします。またPCだと内蔵ストレージの容量にも多少の制約がある（最新のモデルだと1～2TB搭載しているものがあるので、その点は気にしなくてもいいのかも）ので、可能ならば節約したいという思いも生まれてきます。

## 制限事項
[マルチユーザ環境のための pyenv](https://qiita.com/takkeybook/items/4640799385e0fc4be915) においても記載してありますが、anyenv 配下にある XXenv で特定のバージョンの言語およびそれに付随するパッケージ類をインストールするのは root ユーザーのみとなります。

## 対象
以下の説明は Ububntu 20.04.3 LTS を用いた構築方法となります。Debian 系ディストリビューションであれば同じ方法で構築できますが、RedHat 系等の異なるディストリビューションではコマンド、インストールするパッケージ名を読み替えてください。

# 環境構築手順
## パッケージのインストール
```bash
sudo apt-get install gcc make g++ gfortran git libssl-dev libbz2-dev zlib1g-dev libreadline-dev libsqlite3-dev libffi-dev libmysqlclient-dev postgresql-server-dev-12 libpq-dev liblzma-dev
```
※gfortran は不要ですが、後に Python の scipy パッケージなどをインストールするために入れておきます。同様に libmysqlclient-dev, postgresql-server-dev-12, libpq-dev も不要ですが、念のため入れておきます。

## anyenv のインストール
anyenv を github から取得して /usr/local 配下に置きます。
```bash
git clone https://github.com/anyenv/anyenv ~/anyenv
sudo mv anyenv /usr/local
sudo chown -R root.root /usr/local/anyenv
```

anyenv-install を修正します。
```diff
--- /usr/local/anyenv/libexec/anyenv-install.orig
+++ /usr/local/anyenv/libexec/anyenv-install
@@ -22,6 +22,7 @@ ANYENV_DEFAULT_MANIFEST_GIT_REPO="https://github.com/anyenv/anyenv-install.git"

 if [ -z ${ANYENV_DEFINITION_ROOT+x} ]; then
   # $XDG_CONFIG_HOME/anyenv/anyenv-install or ~/.config/anyenv/anyenv-install
+  XDG_CONFIG_HOME=/usr/local/anyenv/config
   ANYENV_DEFINITION_ROOT="${XDG_CONFIG_HOME:-${HOME}/.config}/anyenv/anyenv-install"
 fi
```

全ユーザー用の設定を作成します。ファイル名は任意ですが、/etc/profile.d 配下のファイル名のアルファベット順に読み込まれますので、他の設定とコンフリクトしないようにファイル名を設定すればよろしいです。
```bash
sudo vi /etc/profile.d/Z98-anyenv.sh
```
```bash:/etc/profile.d/Z98-anyenv.sh
export ANYENV_ROOT=/usr/local/anyenv
export PATH=${ANYENV_ROOT}/bin:$PATH
eval "$(anyenv init -)"
```

config を取得します。
```bash
sudo -i anyenv install --init
```

shell を起動し直して確認します。

```bash
exec $SHELL -l
anyenv --version
```

```:実行結果
anyenv 1.1.4
```

## Python の環境構築
### pyenv のインストール
```bash
sudo -i anyenv install pyenv
```

shims および versions ディレクトリを作成するために root で一度ログインしておきます。
```bash
su -
exit
```

### pyenv のファイル修正
`pyenv rehash` は一般ユーザーでは権限がなくて実行できなくなるので root のみ実行されるように変更します。この変更をしておかないとログインの度に警告が出ます。

```bash
sudo vi /usr/local/anyenv/envs/pyenv/libexec/pyenv-rehash
```

```diff
--- a/usr/local/anyenv/envs/pyenv/libexec/pyenv-rehash.orig
+++ b/usr/local/anyenv/envs/pyenv/libexec/pyenv-rehash
@@ -4,6 +4,9 @@
 set -e
 [ -n "$PYENV_DEBUG" ] && set -x

+# This script should be run only by root
+[ $(id -u) -ne 0 ] && { exit; }
+
 SHIM_PATH="${PYENV_ROOT}/shims"
 PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.pyenv-shim"

```

`pyenv global VERSION` コマンドでデフォルトで使用するバージョンを設定した時に、そのバージョン番号の保存先を `${HOME}/.python-version` に変更します。

```bash
sudo vi /usr/local/anyenv/envs/pyenv/libexec/pyenv-global
```
```diff
--- a/usr/local/anyenv/envs/pyenv/libexec/pyenv-global.orig
+++ b/usr/local/anyenv/envs/pyenv/libexec/pyenv-global
@@ -30,7 +30,8 @@ if [ "$1" = "--complete" ]; then
 fi

 versions=("$@")
-PYENV_VERSION_FILE="${PYENV_ROOT}/version"
+# PYENV_VERSION_FILE="${PYENV_ROOT}/version"
+PYENV_VERSION_FILE="${HOME}/.python-version"

 if [ -n "$versions" ]; then
   pyenv-version-file-write "$PYENV_VERSION_FILE" "${versions[@]}"
```

上記修正をしたので、以下にもそれを反映させます。
```bash
sudo vi /usr/local/anyenv/envs/pyenv/libexec/pyenv-version-file
```

```diff
--- a/usr/local/anyenv/envs/pyenv/libexec/pyenv-version-file.orig
+++ b/usr/local/anyenv/envs/pyenv/libexec/pyenv-version-file
@@ -24,5 +24,5 @@ if [ -n "$target_dir" ]; then
 else
   find_local_version_file "$PYENV_DIR" || {
     [ "$PYENV_DIR" != "$PWD" ] && find_local_version_file "$PWD"
-  } || echo "${PYENV_ROOT}/version"
+  } || echo "${HOME}/.python-version"
 fi
```

### Python のインストール
環境変数を sudo 経由で渡すのが面倒なので root でインストールします。

```bash
sudo su -
PYTHON_CONFIGURE_OPTS="--enable-shared" CFLAGS="-fPIC" pyenv install 3.9.8
pyenv rehash
exit
```
※PYTHON_CONFIGURE_OPTS は YouCompleteMe を使うために、CFLAGS は PyMC (実際はそれが利用する theano からの要請) を使うために設定しています。

shellを再起動した後に、デフォルトバージョンの設定をして確認してみます。
```bash
exec $SHELL -l
pyenv global 3.9.8
python --version
```

```:実行結果
Python 3.9.8
```

### pip のアップグレード
```bash
sudo -i pyenv global 3.9.8
sudo -i pip install --upgrade pip
```

root による pip の実行は推奨されていないので、パッケージをインストールする度に以下の警告が出ますが気にしないことにします。


> WARNING: Running pip as the 'root' user can result in broken permissions and 
conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

### パッケージのインストール方法
[pip のアップグレード](https://qiita.com/drafts/4e73449488a306b032c9/edit#pip-%E3%81%AE%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89) と同様に、パッケージインストールしたい Python のバージョンの設定をセットで実施することとなります。またパッケージ依存ではありますが、最後に rehash をする必要があります。すなわち

```bash
sudo -i pyenv global VERSION番号
sudo -i pip install PACKAGE名
sudo -i pyenv rehash
```

## Ruby の環境構築
基本的には [Pythonの環境構築](https://qiita.com/drafts/4e73449488a306b032c9/edit#python-%E3%81%AE%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89) とやることは同じです。

### rbenv のインストール
```bash
sudo -i anyenv install rbenv
```

shims および versions ディレクトリを作成するために root で一度ログインしておきます。
```bash
su -
exit
```

### rbenv のファイル修正
ファイルを 3 つ修正します。

```bash
sudo vi /usr/local/anyenv/envs/rbenv/libexec/rbenv-rehash
```

```diff
--- a/usr/local/anyenv/envs/rbenv/libexec/rbenv-rehash.orig
+++ b/usr/local/anyenv/envs/rbenv/libexec/rbenv-rehash
@@ -4,6 +4,9 @@
 set -e
 [ -n "$RBENV_DEBUG" ] && set -x

+# This script should be run only by root
+[ $(id -u) -ne 0 ] && { exit; }
+
 SHIM_PATH="${RBENV_ROOT}/shims"
 PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.rbenv-shim"

```

```bash
sudo vi /usr/local/anyenv/envs/rbenv/libexec/rbenv-global
```

```diff
--- a/usr/local/anyenv/envs/rbenv/libexec/rbenv-global.orig
+++ b/usr/local/anyenv/envs/rbenv/libexec/rbenv-global
@@ -22,7 +22,8 @@ if [ "$1" = "--complete" ]; then
 fi

 RBENV_VERSION="$1"
-RBENV_VERSION_FILE="${RBENV_ROOT}/version"
+# RBENV_VERSION_FILE="${RBENV_ROOT}/version"
+RBENV_VERSION_FILE="${HOME}/.ruby-version"

 if [ -n "$RBENV_VERSION" ]; then
   rbenv-version-file-write "$RBENV_VERSION_FILE" "$RBENV_VERSION"
```

```bash
sudo vi /usr/local/anyenv/envs/rbenv/libexec/rbenv-version-file
```

```diff
--- a/usr/local/anyenv/envs/rbenv/libexec/rbenv-version-file.orig
+++ b/usr/local/anyenv/envs/rbenv/libexec/rbenv-version-file
@@ -24,5 +24,5 @@ if [ -n "$target_dir" ]; then
 else
   find_local_version_file "$RBENV_DIR" || {
     [ "$RBENV_DIR" != "$PWD" ] && find_local_version_file "$PWD"
-  } || echo "${RBENV_ROOT}/version"
+  } || echo "${HOME}/.ruby-version"
 fi
```

### Ruby のインストール
```bash
sudo -i rbenv install 3.1.0
sudo -i rbenv rehash
```

shellを再起動した後に、デフォルトバージョンの設定をして確認してみます。
```bash
exec $SHELL -l
rbenv global 3.1.0
ruby --version
```

```:実行結果
ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-linux]
```


## Node.js の環境構築
こちらも基本的にやることは同じです。

### nodenv のインストール

```bash
sudo -i anyenv install nodenv
```

shims および versions ディレクトリを作成するために root で一度ログインしておきます。
```bash
su -
exit
```

### nodenv のファイル修正
ファイルを3つ修正します。

```bash
sudo vi /usr/local/anyenv/envs/nodenv/libexec/nodenv-rehash
```

```diff
--- a/usr/local/anyenv/envs/nodenv/libexec/nodenv-rehash.orig
+++ b/usr/local/anyenv/envs/nodenv/libexec/nodenv-rehash
@@ -4,6 +4,9 @@
 set -e
 [ -n "$NODENV_DEBUG" ] && set -x

+# This script should be run only by root
+[ $(id -u) -ne 0 ] && { exit; }
+
 SHIM_PATH="${NODENV_ROOT}/shims"
 PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.nodenv-shim"

```

```bash
sudo vi /usr/local/anyenv/envs/nodenv/libexec/nodenv-global
```

```diff
--- a/usr/local/anyenv/envs/nodenv/libexec/nodenv-global.orig
+++ b/usr/local/anyenv/envs/nodenv/libexec/nodenv-global
@@ -23,7 +23,8 @@ if [ "$1" = "--complete" ]; then
 fi

 NODENV_VERSION="$1"
-NODENV_VERSION_FILE="${NODENV_ROOT:?}/version"
+# NODENV_VERSION_FILE="${NODENV_ROOT:?}/version"
+NODENV_VERSION_FILE="${HOME}/.node-version"

 if [ "$1" = "--unset" ]; then
   exec rm -f "$NODENV_VERSION_FILE"
```

```bash
sudo vi /usr/local/anyenv/envs/nodenv/libexec/nodenv-version-file
```

```diff
--- a/usr/local/anyenv/envs/nodenv/libexec/nodenv-version-file.orig
+++ b/usr/local/anyenv/envs/nodenv/libexec/nodenv-version-file
@@ -24,5 +24,5 @@ if [ -n "$target_dir" ]; then
 else
   find_local_version_file "$NODENV_DIR" || {
     [ "$NODENV_DIR" != "$PWD" ] && find_local_version_file "$PWD"
-  } || echo "${NODENV_ROOT}/version"
+  } || echo "${HOME}/.node-version"
 fi
```

### Node.js のインストール
```bash
sudo -i nodenv install 16.13.2
sudo -i nodenv rehash
```

shellを再起動した後に、デフォルトバージョンの設定をして確認してみます。
```bash
exec $SHELL -l
nodenv global 16.13.2
node --version
```

```:実行結果
v16.13.2
```

### npm のアップグレード
```bash
sudo -i nodenv global 16.13.2
sudo -i npm install -g npm@8.4.1
```

### yarn の有効化
node-16.9 から corepack が標準バンドルされたことで yarn がそのままでは使えないようです。以下の手順で有効化する必要があります。
```bash
sudo -i corepack enable yarn
sudo -i nodenv rehash
```

[teppeis](https://zenn.dev/teppeis)さんの[corepack is 何?](https://zenn.dev/teppeis/articles/2021-05-corepack)が役に立ちました。

# さいごに
これを執筆した直後に rails console (irb) が起動できなくて困りました。対応方法がわかったので改定してありますが、まだ問題が起きるかもしれませんので、その場合にはコメントをください。