言語環境のバージョン管理ツール「mise」を試す
以下の記事を見て。
環境構築まわり、自分は今こんな感じ
- Python
- Node.js
- たまにしか使わないけど、voltaを使っている
- たまにしか使わないので、あまり細かいところは気にせず
- その他
- 過去
- 以前はpyenv + pyenv-virtualenvをよく使ってた、あまり考えなくて良いのがよかった
- Rubyでrbenv使ってたけど、最近はRubyをそもそも使っていない
- Perlでplenv使ってたけど、最近は標準のPerlでワンライナー中心でそもそも必要なくなった
- Terraformでtfenv使ってたけど、最近Terraform触ることが減った
- 過去
- こういうのがめんどくさくて、なるだけDocker(DevContainer)でやりたいという思いがある
- VMレベルだとMultipassだけど、そこまで使ってない
- OSSのツールとかも、Goで書かれていてバイナリだけ落とせば良いようなものが望ましいと感じる
環境構築、個人的にはそんな苦にはならない(というかそれをIaCとかで設定したりするのはむしろ好きなんだけどw)けども、チームで統一とかはDockerというかDevContainerでええやん、みたいなところがある。
そういう意味では、asdf的なものにはあまり積極的にはなれない(と言いつつ使ったことはない)のだけども、完全にDockerでやるのも逆に面倒な場合もあるので、少し触ってみようと思う。
公式
GitHubレポジトリ
Getting Startedに従って進める。
自分はとりあえず、DockerでUbuntuコンテナを立ち上げてそこでやることにする。
$ docker run -it --rm --name mise-test ubuntu /bin/bash
miseのインストールは、インストール用のスクリプトを使うのが標準な様子だが、他にもインストール方法が用意されている。バイナリを直接インストールするとか、MacならHomebrew、Ubuntuならaptが使える様子。
今回はバイナリを直接ダウンロードすることにする。
まずcurlをインストール。
$ apt update && apt install -y curl
アーキテクチャに合わせて、バイナリをインストール
$ mkdir -p ~/.local/bin
$ curl https://mise.jdx.dev/mise-latest-linux-arm64 > ~/.local/bin/mise
$ chmod +x ~/.local/bin/mise
miseを有効化して、パスを通す
$ echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
$ echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bash_profile
$ source ~/.bashrc
$ which mise
/root/.local/bin/mise
$ mise --version
2024.9.3 linux-arm64 (e0d1317 2024-09-12)
Usageはこんな感じ
$ mise --help
mise is a tool for managing runtime versions. https://github.com/jdx/mise
It's a replacement for tools like nvm, nodenv, rbenv, rvm, chruby, pyenv, etc.
that works for any language. It's also great for managing linters/tools like
jq and shellcheck.
It is inspired by asdf and uses asdf's plugin ecosystem under the hood:
https://asdf-vm.com/
Usage: mise [OPTIONS] <COMMAND>
Commands:
activate Initializes mise in the current shell session
alias Manage aliases [aliases: a]
backends Manage backends [aliases: b]
bin-paths List all the active runtime bin paths
cache Manage the mise cache
completion Generate shell completions
config [experimental] Manage config files [aliases: cfg]
current Shows current active and installed runtime versions
deactivate Disable mise for current shell session
direnv Output direnv function to use mise inside direnv
doctor Check mise installation for possible problems [aliases: dr]
env Exports env vars to activate mise a single time [aliases: e]
exec Execute a command with tool(s) set [aliases: x]
generate [experimental] Generate files for various tools/services [aliases: gen]
implode Removes mise CLI and all related data
install Install a tool version [aliases: i]
latest Gets the latest available version for a plugin
link Symlinks a tool version into mise [aliases: ln]
ls List installed and active tool versions [aliases: list]
ls-remote List runtime versions available for install
outdated Shows outdated tool versions
plugins Manage plugins [aliases: p]
prune Delete unused versions of tools
registry [experimental] List available tools
reshim rebuilds the shim farm
run [experimental] Run a tasks [aliases: r]
self-update Updates mise itself
set Manage environment variables
settings Manage settings
shell Sets a tool version for the current session [aliases: sh]
sync Add tool versions from external tools to mise
tasks [experimental] Manage tasks [aliases: t]
trust Marks a config file as trusted
uninstall Removes runtime versions [aliases: remove, rm]
unset Remove environment variable(s) from the config file
upgrade Upgrades outdated tool versions [aliases: up]
usage Generate a usage CLI spec
use Install tool version and add it to config [aliases: u]
version Show mise version
watch [experimental] Run a tasks watching for changes [aliases: w]
where Display the installation path for a runtime
which Shows the path that a bin name points to
help Print this message or the help of the given subcommand(s)
Options:
-C, --cd <DIR>
Change directory before running command
-P, --profile <PROFILE>
Set the profile (environment)
-q, --quiet
Suppress non-error messages
-v, --verbose...
Show extra output (use -vv for even more)
-y, --yes
Answer yes to all confirmation prompts
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
Examples:
$ mise install node@20.0.0 Install a specific node version
$ mise install node@20 Install a version matching a prefix
$ mise install node Install the node version defined in config
$ mise install Install all plugins/tools defined in config
$ mise install cargo:ripgrep Install something via cargo
$ mise install npm:prettier Install something via npm
$ mise use node@20 Use node-20.x in current project
$ mise use -g node@20 Use node-20.x as default
$ mise use node@latest Use latest node in current directory
$ mise use -g node@system Use system node everywhere unless overridden
$ mise up --interactive Show a menu to upgrade tools
$ mise x -- npm install `npm install` w/ config loaded into PATH
$ mise x node@20 -- node app.js `node app.js` w/ config + node-20.x on PATH
$ mise set NODE_ENV=production Set NODE_ENV=production in config
$ mise run build Run `build` tasks
$ mise watch build Run `build` tasks repeatedly when files change
$ mise settings Show settings in use
$ mise settings set color 0 Disable color by modifying global config file
ではPython環境を構築してみる。-g
でグローバルな環境となる。
$ mise use -g python@3.11
mise python@3.11.10 ✓ installed mise ~/.config/mise/config.toml tools: python@3.11.10
設定は~/.config/mise/config.toml
に書かれている。
[tools]
python = "3.11"
mise ls
でインストールされた環境が確認できる。
$ mise ls
Tool Version Config Source Requested
python 3.11.10 ~/.config/mise/config.toml 3.11
インストールしたPythonにアクセスしてみる。
$ which python
/root/.local/share/mise/installs/python/3.11/bin/python
$ python --version
Python 3.11.10
プロジェクトごとに設定するパターン。ディレクトリを作成してみる。
$ mkdir test-proj && cd test-proj
$ mise use python@3.12
mise python@3.12.6 ✓ installed mise /test-proj/.mise.toml tools: python@3.12.6
設定ファイルはディレクトリ直下に作成される模様。
$ cat .mise.toml
[tools]
python = "3.12"
ディレクトリ内からPythonにアクセスしてみると、プロジェクト用にインストールしたものになっているのがわかる。
$ which python
/root/.local/share/mise/installs/python/3.12/bin/python
$ python --version
Python 3.12.6
mise ls
でもプロジェクト用のPythonが参照されているのがわかる。
$ mise ls
Tool Version Config Source Requested
python 3.11.10
python 3.12.6 /test-proj/.mise.toml 3.12
ここまでで、pyenv + direnvっぽいことはできることがわかった。あとはvenvができれば。
パッケージのインストールパスはこんな感じになる。
$ pwd
/
$ which pip
/root/.local/share/mise/installs/python/3.11/bin/pip
$ python -c "import site;print(site.getsitepackages())"
['/root/.local/share/mise/installs/python/3.11.10/lib/python3.11/site-packages']
$ cd test-proj
$ pwd
/test-proj
$ which pip
/root/.local/share/mise/installs/python/3.12/bin/pip
$ python -c "import site;print(site.getsitepackages())"
['/root/.local/share/mise/installs/python/3.12.6/lib/python3.12/site-packages']
ドキュメントを見ると自動でvirtualenvをアクティベイトできる様子。
プロジェクトディレクトリの.mise.tomlに設定を追加してみる
[tools]
python = "3.12"
[env]
_.python.venv = { path = ".venv", create = true }
こんなメッセージが出てくるので、mise trust
を実行してみる。
mise Config file /test-proj/.mise.toml is not trusted.
Trust it with `mise trust`.
mise Run with --verbose or MISE_VERBOSE=1 for more information
$ mise trust
mise trusted /test-proj/.mise.toml
mise creating venv at: /test-proj/.venv
.venvディレクトリが作成される
root@9749e2bb72a4:/test-proj# ls -la
total 16
drwxr-xr-x 3 root root 4096 Sep 15 02:07 .
drwxr-xr-x 1 root root 4096 Sep 15 01:39 ..
-rw-r--r-- 1 root root 81 Sep 15 02:06 .mise.toml
drwxr-xr-x 5 root root 4096 Sep 15 02:07 .venv
パッケージインストールパスが.venv以下になっているのがわかる。
$ python -c "import site;print(site.getsitepackages())"
['/test-proj/.venv/lib/python3.12/site-packages']
なお、create = true
をつけない場合は自分でpython -m venv 〜
する必要がある。
.mise.toml
で環境変数を設定することもできる。
自分的によくやるのは、.envに環境変数書いておいてpython-dotenvで読み出したりするやつ。例としてはあまり適切ではないと思うが、ここではOpenAIのAPIキーをセットしてみる。
[tools]
python = "3.12"
[env]
_.python.venv = { path = ".venv", create = true }
OPENAI_API_KEY="sk-XXXXXXXXXXXXXXXX"
$ echo $OPENAI_API_KEY
sk-XXXXXXXXXXXXXXXX
なお、グローバルな~/.config/mise/config.toml
に環境変数を設定すると、プロジェクトの中でも(上書きする設定をしない限り)それを参照できる様子。
タスクランナーの設定。試しにJupyter Labのインストールと起動を設定してみた。
[tools]
python = "3.12"
[env]
_.python.venv = { path = ".venv", create = true }
[tasks.setup_jupyter]
description = 'setup jupyter-lab environment'
run = 'pip install -U pip jupyterlab black ruff jupyterlab-lsp python-lsp-server python-lsp-ruff jupyterlab-code-formatter jupytext jupyterlab-git nbdime ipywidgets'
alias = 'sj'
[tasks.run_jupyter]
description = 'setup jupyter-lab environment'
run = 'jupyter-lab --ip="0.0.0.0" --NotebookApp.token="" --allow-root'
alias = 'rj'
インストールを実行してみる。
$ mise task run setup_jupyter
タスクランナーはexperimentalなので、mise settings set experimental true
を有効化してやる。
mise `mise run` is experimental. Enable it with `mise settings set experimental true`
mise Run with --verbose or MISE_VERBOSE=1 for more information
$ mise settings set experimental true
再度実行。もっと短く書ける。
$ mise r sj
Successfully installed ...
起動
$ mise r rj
[I 2024-09-15 02:52:00.959 ServerApp] Jupyter Server 2.14.2 is running at:
[I 2024-09-15 02:52:00.959 ServerApp] http://9749e2bb72a4:8888/lab
[I 2024-09-15 02:52:00.959 ServerApp] http://127.0.0.1:8888/lab
とりあえず自分のpython環境の使い方だと置き換えれそうな感はあるな。pyenv+virtualenv+direnvと分かれているものを1つでまとめることはできそう。
自分はNodeについてはそれほど大した使い方していないから、十分巻き取れそうではある。
他のインストール方法見てたんだけど、Dockerで使えるの結構便利な気がする。
$ docker run -it --rm jdxcode/mise x python@3.11 -- bash
$ which python
/mise/installs/python/3.11/bin/python
$ python --version
Python 3.11.10
x
って何?と思ったけどexec
か。
複数環境同時にセットしたりもできる
$ docker run -it --rm jdxcode/mise x python@3.11 node@18 -- bash
mise をプロジェクト共通とするべきか
筆者は mise をプロジェクト標準とすべきかはケースバイケースであると考えます。
まず開発環境が個々人でどれだけ異なるか、どれだけ自由が求められる風土かによっても異なります。
たとえば同一バージョンのインタプリタが入っていれば言語管理は何でも良いというチームもあれば、バージョン管理ツールまで指定されるチームもあります。Windows と Linux で共同開発するチームもあるかもしれません。
ですので、チームに無理のない範囲で導入できるのが良いと思います。
そしてチーム標準とならずとも、自分だけ mise を活用すればよいのです。
例えば direnv のようなツールはグローバルの shell に設定が必要となるため強制したくなく、プロジェクトでは「このようなツールを使うのが便利です」という紹介に留め、例として direnv や mise を挙げ、自分は mise を使う、という方法を取るなどです。
タスクランナー機能についても、自分だけが触る範囲であれば自分だけの .mise.toml を作るのもよいでしょう。ただし複数人で管理する場合に make に限界がきているのであれば、プロジェクトとしての mise の導入を提案してもよいかもしれません。
個人のマシン内で使うならば何でもいいと思うけど、チームでプロジェクトで使う、場合はどうかなぁ。自分はそこにあまり差異を許容したくないので、ガイドラインやルールよりも、プロジェクトレポジトリにはDevContainerをセットで用意しておいて必ずそれ使え、というのが良いのではないかと思っている。
でもまあチーム次第ってのはその通りだと思う。
最近のスニペット
$ mkdir FOOBAR && cd FOOBAR
$ mise use python@3.11
$ cat << 'EOS' >> .mise.toml
[env]
_.python.venv = { path = ".venv", create = true }
EOS
$ mise trust