Closed9

言語環境のバージョン管理ツール「mise」を試す

kun432kun432

以下の記事を見て。

https://zenn.dev/euxn23/articles/b12f1f9b495d47

環境構築まわり、自分は今こんな感じ

  • 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でやるのも逆に面倒な場合もあるので、少し触ってみようと思う。

公式

https://mise.jdx.dev/

GitHubレポジトリ

https://github.com/jdx/mise

kun432kun432

Getting Startedに従って進める。

https://mise.jdx.dev/getting-started.html

自分はとりあえず、DockerでUbuntuコンテナを立ち上げてそこでやることにする。

$ docker run -it --rm --name mise-test ubuntu /bin/bash

miseのインストールは、インストール用のスクリプトを使うのが標準な様子だが、他にもインストール方法が用意されている。バイナリを直接インストールするとか、MacならHomebrew、Ubuntuならaptが使える様子。

https://mise.jdx.dev/getting-started.html#alternate-installation-methods

今回はバイナリを直接ダウンロードすることにする。

まず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に書かれている。

~/.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
kun432kun432

ここまでで、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をアクティベイトできる様子。

https://mise.jdx.dev/lang/python.html#automatic-virtualenv-activation

プロジェクトディレクトリの.mise.tomlに設定を追加してみる

test-proj/.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 〜する必要がある。

kun432kun432

.mise.tomlで環境変数を設定することもできる。

https://mise.jdx.dev/environments.html

自分的によくやるのは、.envに環境変数書いておいてpython-dotenvで読み出したりするやつ。例としてはあまり適切ではないと思うが、ここではOpenAIのAPIキーをセットしてみる。

test-proj/.mise.toml
[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に環境変数を設定すると、プロジェクトの中でも(上書きする設定をしない限り)それを参照できる様子。

kun432kun432

タスクランナーの設定。試しにJupyter Labのインストールと起動を設定してみた。

test-proj/.mise.toml
[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
kun432kun432

とりあえず自分のpython環境の使い方だと置き換えれそうな感はあるな。pyenv+virtualenv+direnvと分かれているものを1つでまとめることはできそう。

自分はNodeについてはそれほど大した使い方していないから、十分巻き取れそうではある。

kun432kun432

他のインストール方法見てたんだけど、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か。

https://mise.jdx.dev/cli/exec.html

複数環境同時にセットしたりもできる

$ docker run -it --rm jdxcode/mise x python@3.11 node@18 -- bash
kun432kun432

https://zenn.dev/euxn23/articles/b12f1f9b495d47

mise をプロジェクト共通とするべきか

筆者は mise をプロジェクト標準とすべきかはケースバイケースであると考えます。
まず開発環境が個々人でどれだけ異なるか、どれだけ自由が求められる風土かによっても異なります。
たとえば同一バージョンのインタプリタが入っていれば言語管理は何でも良いというチームもあれば、バージョン管理ツールまで指定されるチームもあります。Windows と Linux で共同開発するチームもあるかもしれません。
ですので、チームに無理のない範囲で導入できるのが良いと思います。
そしてチーム標準とならずとも、自分だけ mise を活用すればよいのです。
例えば direnv のようなツールはグローバルの shell に設定が必要となるため強制したくなく、プロジェクトでは「このようなツールを使うのが便利です」という紹介に留め、例として direnv や mise を挙げ、自分は mise を使う、という方法を取るなどです。
タスクランナー機能についても、自分だけが触る範囲であれば自分だけの .mise.toml を作るのもよいでしょう。ただし複数人で管理する場合に make に限界がきているのであれば、プロジェクトとしての mise の導入を提案してもよいかもしれません。

個人のマシン内で使うならば何でもいいと思うけど、チームでプロジェクトで使う、場合はどうかなぁ。自分はそこにあまり差異を許容したくないので、ガイドラインやルールよりも、プロジェクトレポジトリにはDevContainerをセットで用意しておいて必ずそれ使え、というのが良いのではないかと思っている。

でもまあチーム次第ってのはその通りだと思う。

kun432kun432

最近のスニペット

$ mkdir FOOBAR && cd FOOBAR
$ mise use python@3.11
$ cat << 'EOS' >> .mise.toml

[env]
_.python.venv = { path = ".venv", create = true }
EOS
$ mise trust
このスクラップは2ヶ月前にクローズされました