mise로 모든 언어의 버전을 통합 관리하기

2024년 6월 16일 일요일 / , , , ,
简体中文한국어
요약 ·
nvm, pyenv, rustup, Homebrew에 있는 openjdk까지 언어마다 버전 관리 방식이 따로 있었습니다. 최근 mise를 써 보면서 이들을 모두 하나의 도구로 바꿨고, `.zshrc`도 훨씬 깔끔해졌습니다.

제 컴퓨터의 프로그래밍 언어들은 늘 각자 따로 관리해 왔습니다. 예를 들어 Node는 nvm, Python은 pyenv, Rust는 rustup을 쓰고, Go와 Java는 Homebrew로 바로 설치했습니다. 각각 고유한 명령과 설치 디렉터리, 그리고 .zshrc 에 추가할 초기화 코드가 있었습니다.

시간이 지나면서 .zshrc 에는 이런 것들이 한가득 쌓였습니다:

Shell
zstyle ':omz:plugins:nvm' lazy yes
plugins=(git nvm history fzf-tab)

export PATH="/opt/homebrew/opt/openjdk@17/bin:$PATH"
export JAVA_HOME="/opt/homebrew/Cellar/openjdk@17/17.0.7/libexec/openjdk.jdk/Contents/Home"
alias gradle7='/opt/homebrew/Cellar/gradle@7/7.6.1/bin/gradle'

export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

export PATH=$PATH:$HOME/go/bin

nvm은 악명 높을 정도로 느리기 때문에 oh-my-zsh의 nvm 플러그인을 사용하고 lazy 모드도 켜서, 처음 node 를 사용할 때에야 로드되게 했습니다. pyenv의 eval 두 줄 역시 터미널을 열 때마다 매번 실행해야 했습니다.

가장 골치 아팠던 건 Java였습니다. Java 17과 Gradle 7이 필요한 프로젝트가 있어서 Homebrew로 openjdk@17 과 gradle@7을 설치한 다음, JAVA_HOME 은 Homebrew Cellar 안의 경로로 하드코딩할 수밖에 없었고, 그 경로에는 구체적인 버전 번호 17.0.7도 들어 있었습니다. 그래서 brew upgrade 가 마이너 버전 하나만 올려도 이 경로가 무효가 되어 다시 수동으로 고쳐야 했습니다.

mise

그러던 중 mise를 알게 되었습니다. 예전 이름은 rtx였고 올해 초에야 mise로 이름을 바꿨으며, Rust로 작성되어 더 빠른 asdf라고 이해하면 됩니다. 이 도구 하나로 Node, Python, Go, Rust, Java 같은 언어의 버전을 관리할 수 있고, 개발할 때 쓰는 명령줄 도구도 설치할 수 있습니다.

nvm, pyenv 같은 도구와 비교하면 mise에는 다음과 같은 장점이 있습니다:

  • 하나의 도구로 모든 언어를 관리. 명령은 모두 동일합니다. mise use, mise install, mise ls만 기억하면 되고, 더 이상 각 도구의 사용법을 따로 외울 필요가 없습니다.
  • 빠름. 기본적으로 shim을 사용하지 않고, 프롬프트가 표시될 때마다 현재 디렉터리에 따라 어떤 버전을 사용할지 계산해 PATH를 직접 바꿉니다. 명령을 실행할 때 추가 오버헤드가 전혀 없고, 이 단계 자체도 매우 빠릅니다.
  • 디렉터리별 버전 전환. 프로젝트 디렉터리에 설정 파일을 하나 두면, 디렉터리에 들어갈 때 버전이 자동으로 바뀌고 나올 때 다시 원래대로 돌아옵니다.
  • 기존 버전 파일과 호환. 프로젝트에 이미 있는 .nvmrc, .python-version, .tool-versions 파일을 mise가 모두 직접 읽을 수 있습니다.

설치

저는 Homebrew로 설치했습니다.

Shell
brew install mise

그다음 ~/.zshrc 에서 활성화하고, 자동 완성도 함께 추가합니다.

Shell
eval "$(mise activate zsh)"
# mise completion
source <(mise completion zsh)

mise activate 는 zsh에 hook을 등록해서, 프롬프트가 나타날 때마다 현재 디렉터리를 확인한 뒤 해당 버전의 경로를 PATH에 넣습니다.

전역 버전

전역으로 사용할 버전은 ~/.config/mise/config.toml 에 작성합니다. 이 파일을 직접 편집해도 되고, mise use -g를 사용해도 되며, mise가 자동으로 설치하고 설정에 기록합니다.

Shell
mise use -g node@lts
mise use -g python@3.12
mise use -g go@latest
mise use -g rust@stable
mise use -g java@17

최종 설정 파일은 대략 다음과 같습니다.

TOML
[tools]
node = "lts"
python = "3.12"
go = "latest"
rust = "stable"
java = "17"
gradle = "7"
bun = "latest"
pnpm = "9"
"npm:wrangler" = "3"

여기서 버전은 아주 구체적으로 적을 수 있습니다. 예를 들어 3.12.4처럼 적을 수 있고, 접두사만 적을 수도 있습니다. 3.12 는 3.12 중 최신 버전을 뜻하며, latest 는 최신 버전입니다. 예를 들어 Rust에 stable 을 적으면 자동으로 안정 버전을 사용합니다.

Java도 mise가 관리하므로 이제 JAVA_HOME 을 직접 작성할 필요가 없습니다. mise가 Java 버전을 전환할 때 자동으로 설정해 줍니다. Gradle도 마찬가지로 설정에 바로 적으면 되고, Cellar를 가리키던 gradle7 별칭도 삭제할 수 있습니다.

언어 자체뿐 아니라 개발할 때 쓰는 도구 일부도 mise에 맡길 수 있습니다. 예를 들어 pnpm 과 bun, 그리고 npm: 로 시작하는 표기는 npm으로 설치하는 명령줄 도구를 뜻하는데, Cloudflare의 wrangler도 이렇게 설치했습니다. 이렇게 하면 이런 도구의 버전도 같은 파일에 기록되므로 더 이상 npm install -g를 사용할 필요가 없습니다.

프로젝트 버전

프로젝트에 특정 버전이 필요할 때는 프로젝트 디렉터리에서 mise use를 전역 플래그 -g 없이 실행합니다.

Shell
cd some-project
mise use node@18

그러면 프로젝트 디렉터리에 .mise.toml 파일이 생성됩니다.

TOML
[tools]
node = "18"

이후 이 디렉터리에 들어가기만 하면 node 가 18이 되고, 디렉터리를 벗어나면 전역 버전으로 돌아갑니다. 또한 이 파일은 저장소에 커밋할 수 있어서, 다른 사람이 mise를 쓴다면 이 디렉터리에서 mise install 만 실행하면 같은 버전을 설치할 수 있습니다.

프로젝트에 이미 .nvmrc 또는 .python-version 파일이 있다면 아무것도 할 필요 없이, mise가 그 안에 적힌 버전을 그대로 따릅니다.

환경 변수

.mise.toml 에서도 환경 변수를 설정할 수 있습니다. 디렉터리에 들어가면 자동으로 설정되고, 나가면 자동으로 해제되어 direnv와 비슷합니다.

TOML
[env]
NODE_ENV = "development"
DATABASE_URL = "postgres://localhost/dev"

에디터에서 사용하기

mise activate 는 shell hook을 통해 동작하므로 대화형 터미널에서만 적용됩니다. Dock에서 연 에디터나 shell을 거치지 않는 프로그램에는 mise가 설정한 PATH가 적용되지 않습니다.

이런 경우에는 shim 모드를 사용할 수 있습니다. ~/.zprofile 에 다음 한 줄을 추가합니다.

Shell
eval "$(mise activate zsh --shims)"

그러면 ~/.local/share/mise/shims 를 PATH에 추가합니다. 그 안에는 명령과 같은 이름의 작은 프로그램들이 있고, 호출될 때 mise가 어떤 버전을 사용할지 결정합니다. 다만 저는 주로 터미널에서 에디터를 열기 때문에 아직 이 모드는 켜지 않았습니다.

오래된 도구 정리

모두 mise로 바꾸고 나면 기존 것들은 삭제할 수 있습니다.

Shell
brew uninstall nvm
rm -rf ~/.nvm

.zshrc 에서 nvm 플러그인, pyenv 초기화, 하드코딩된 JAVA_HOME 과 gradle7도 모두 삭제하고, mise activate 한 줄만 남겼습니다.

설치한 뒤에는 mise ls 로 현재 어떤 버전이 설치되어 있고 어느 버전을 사용 중인지 확인할 수 있습니다.

Shell
mise ls

업데이트하고 싶다면 mise upgrade 가 설정에 latest, lts 또는 접두사 버전으로 적힌 도구를 조건에 맞는 최신 버전으로 업그레이드합니다. 이상한 문제가 생기면 먼저 mise doctor를 실행해 보세요. mise가 활성화되어 있는지, 설정 파일에 문제가 없는지 점검해 줍니다.

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...