大家在開發環境的工具管理上是怎麼做的呢?
我在進入這個行業的三年半期間一直在使用 Homebrew,不過最近因為經常看到「Nix 的文章」和「大家的 Dotfiles 文章」,我感到想要更新自己的開發環境。
不過……我老實說並沒有時間和精力去學習像 Nix 這樣的「完全重現性」的獨特語言。
因此,我選擇了可以結合「Homebrew 的便利性」和「如 Nix 一樣的宣告式管理」的工具「Mise」。
這次我想記錄下這段經過和遇到的困難。
※ 本文不會介紹 mise 的功能
What is it?
它像 asdf(或 nvm 或 pyenv,但適用於任何語言)一樣,管理像 node、python、cmake、terraform 等數百種開發工具。
像 direnv 一樣為不同的項目目錄管理環境變數。
像 make 一樣管理用於構建和測試項目的任務。
以上是從 Mise 的 GitHub 引用來的。
簡而言之,就是具備「開發工具管理」、「環境變數管理」、「任務執行器」的功能。
Pronounced "MEEZ ahn plahs"
中文發音為「米茲」。
使用 brew update 和 brew upgrade 進行工具的批量更新非常方便,但無法指定要安裝的版本,例如想保持在「xx 系列」的版本。
在 brew list 輸出的工具列表中,無法分辨出這些工具是自己安裝的還是依賴的工具。
這無法像 package.json 和 package-lock.json 那樣進行區分。
當然,可以利用 brew deps 和 brew use 指令來確定是否有依賴,但這樣做非常麻煩。
這是個「困惑」,所以寫這些感受的文章我感到有些不好意思。
因為在 brew list 中列出了自己選擇的以外的包,讓我感到用代碼來管理這些包有些不自然。
此外,在創建文件並列出後,實施用於在其他環境中重現的腳本的管理成本也很高。
※ 如果 homebrew 發生更改,維護腳本會變得非常麻煩。
如果在主機上開發多個項目而不使用容器,像是 node 或 go 等語言的版本切換會非常麻煩。
現今不少情況下會進行遠端開發或容器開發,但在我的情況下,有時「我希望能在主機 OS 上迅速執行靜態分析或格式化」這種需求,所以「每個項目目錄的工具版本指定」是必要的。
市面上當然也有這樣的工具,在 Node 系列中,volta 和 nvm 給人的印象很強。
但是,「Homebrew 中同時安裝 node 和 volta,然後在項目目錄中使用指定的 volta 版本的 node」會讓人感到不適。
可以像下面這樣指定工具的版本:
[tools]
go = "latest"
golangci-lint = "v2.7.2" # 固定版本
node = "latest"
jq = "latest"
在 Homebrew 中經常會發生「不知不覺就升級到新版本而不能使用」的情況,但在 Mise 中可以用代碼明確指定所使用的工具和版本。
只需在 dotfiles 中管理此文件,當更換電腦或建立開發用的遠端虛擬機時,僅需執行 mise install 即可安裝所需的開發工具。
# 安裝工具
mise install
# 更新工具版本
mise upgrade
「想在每個項目中使用不同版本的開發工具」的需求只需放置設定文件即可解決。
例如,當在項目 A(Node.js v18)和項目 B(Node.js v20)之間切換時,只需更改目錄,Mise 就會自動切換。
[tools]
node = "18.00.0"
[tools]
node = "20.10.0"
即使不單獨安裝 direnv 或 volta,僅用 Mise 就能輕鬆完成語言版本和工具版本的切換,這樣的體驗非常好。
Mise 也具有任務執行器的功能(與 make 類似)。
可以通過 mise run 列出並執行定義的任務。
我也在日常操作系統的維護任務中使用它。
下面分享一個範例。
[tasks.cleanup]
description = "系統整體的快取清理"
run = """
echo "Cleaning Go cache..." && go clean -modcache -cache -testcache
echo "Cleaning Docker..." && docker system df && docker volume ls -qf dangling=true | xargs -r docker volume rm && docker images -f 'dangling=true' -q | xargs -r docker rmi && docker container prune --force --filter 'until=168h' && docker ps --filter 'status=exited' | grep 'weeks ago' | awk '{print $1}' | xargs -r docker rm && docker builder prune && docker system df
echo "Cleaning npm cache..." && npm cache clean --force
"""
[tasks.doctor]
run = "mise doctor && mise reshim"
對於用 mise 安裝的開發工具,當然希望可以使用 mise 進行別名註冊。
當然,mise 也有別名註冊的功能。
我設置了如下的別名。
mr = "mise run"
d = "docker"
dc = "docker compose"
dps = "docker ps"
dpsa = "docker ps -a"
dst = "docker stats"
dil = "docker images"
dclog = "docker compose logs -f"
dex = "docker exec -it"
dcex = "docker compose exec -it"
drm = "docker rm"
dri = "docker rmi"
我認為,Mise 的可移植性價值非常高。
只需設置一個符號鏈接,讓 OS 能讀取以 Dotfiles 管理的 Mise 的 config.toml,就可以完成 Mise 的設置。
在我自己的情況下,會在 Dotfiles 的初始化腳本中編寫設置內容。以下是範例。
# 為 mise 創建符號鏈接
ln -sfvn "$DOTFILES_DIR/mise" ~/.config/mise
mise install
遷移過程中我並沒有花費太多時間,很順利。
但其中有幾個小困難的點,還有閱讀者可能會關心的點,我這裡總結一下。
在和 AI 一起進行遷移計畫時,我把 docker 相關工具視為一大難關。
具體來說,候選開發工具如下。
的確,如果「上述五個中有一個未被 Mise 支持」,我會覺得非常麻煩,但實際上可問題不大,Mise 可以正常管理。
關於 docker compose 和 docker buildx,在 mise 中的安裝路徑如下所示。
% which docker compose
~/.local/share/mise/installs/docker-cli/29.2.1/docker/docker
因此,由於每次版本升級時路徑會改變,每次都需要設置符號鏈接,讓 docker cli 具識別為子命令。
ln -s $(which docker-cli-plugin-docker-compose) ~/.docker/cli-plugins/docker-compose
ln -s $(which docker-cli-plugin-docker-buildx) ~/.docker/cli-plugins/docker-buildx
或者。
可以利用 mise 的 tools 定義的安裝時的 postinstall 功能。
這樣在更新後會自動設置路徑。目前為止我並未遇到任何問題。
[tools]
lima = "latest"
colima = "latest"
docker-cli = "latest"
docker-compose = { version = "latest", postinstall = "ln -s $(which docker-cli-plugin-docker-compose) ~/.docker/cli-plugins/docker-compose" }
"aqua:docker/buildx" = { version = "latest", postinstall = "ln -s $(which docker-cli-plugin-docker-buildx) ~/.docker/cli-plugins/docker-buildx" }
Mise 無法管理 GUI 應用,因此在選擇時我也有些猶豫。
仔細想一下,或許我對於 GUI 應用的管理實際上並不是那麼需要。
以下是我總結的理由。
理由
因此,我決定從這次開始不進行 GUI 應用的管理。
引進 Mise 使我能將「Homebrew 的便利性」和「Nix 的宣告式管理」的優點結合在一起。
到目前為止,選擇這個工具是值得的。
特別是對於那些「想挑戰開發環境代碼化但還未實現的人......,但是看起來難度好高」的人,我特別推薦 Mise。
我非常喜歡 Mise 的「開發工具管理」和「項目單位開發工具管理」無縫的體驗。
這次的文章像是「Mise 遷移體驗記」,但希望這篇文章能成為閱讀者開發環境更加豐富的契機。
原文出處:https://qiita.com/umekikazuya/items/388a7aaad767b9e11fa3