如何 nix-darwin 環境下更新 nix 版本
TL;DR
簡單說因為是用 nix-darwin
來管理,所以就算照著官方文件做升級是會有問題的。 雖然沒用過 nixos
,不過我想在 nixos
上也有相同的問題。比較正確的做法是把在 configuratino.nix
中將 nix.package
指向對的版本,
# configuration.nix
nix = {
pacakge = pkgs.nixVersions.nix_2_21; # 指定版本
# skip
}
前言
在使用 nix
的情況下有個一直未解的問題,就是要如何升級 nix
版本,比如說安裝的時候是 2.18.3
,隔一陣子官網上說已經更新到 2.21.1
,不確定是因為太簡單還是怎麼樣,網路上完全找不到該怎麼做。
第一次嘗試:使用官方文件
如果就基本關鍵字查詢,大概會先看到的是官方文件,如果按照官方文件上面更新之後,會遇到的問題是用 nix doctor
時,他會跳個警告說現在有多個版本
[FAIL] Multiple versions of nix found in PATH:
/nix/store/{某版號}/bin
/nix/store/{另外版號}/bin
雖然放著好像還好,不過如果覺得很礙眼想要修正的話。就要知道為什麼會有兩個,會有兩個的原因通常是一個是系統用的,一個是使用者自己的。 檢查的方式是比較兩個指令:
nix-env --version
# 基本上如果已經用 nix-darwin 做管理,這個不該有東西。
sudo nix-env --version
# 這邊應該會出現某個版號的 nix
要修正的話,把系統用的移除就好了。不過基本上就是回到原點。
sudo nix-env -e nix
網路上多說多版本的狀況還行,因為 nix
會按照優先序來執行。不過如果拖很久的會遇到另外一個 portocol 的問題。
[FAIL] Warning: protocol version of this client does not match the store.
While this is not necessarily a problem it's recommended to keep the client in
sync with the daemon.
Client protocol: {某版號}
Store protocol: {另外一個版號}
原因是在 macOS 中, nix 是用 daemon 的方式執行,所以就算更新使用者的 nix,只要 daemon 的版本沒有更新,就可能會出現版本對不上的情形。 順帶一提,要看目前執行的 daemon 的版號的話指令是
nix store ping # 舊版
nix store info # 新版
另外是作者有提供大概這樣的 snippet,千萬別無腦亂套,一用下去連 nix 的 PATH 都不見了,費了好大的力氣才弄回來。
{
environment.profiles = mkForce [];
}
第二種嘗試:透過 nix-darwin 在使用者安裝新版 nix
對 nix
來說 nix
也是其中一個 package,在 nixos 搜尋結果 就可以看到有提供很多版本像 nixVersions.nix_2_21
這樣。所以在使用者加入總行了吧。
# configuration.nix
home-manager.users.jack = {pkgs, ...}: {
home.stateVersion = "23.11";
home.packages = with pkgs; [
nixVersions.nix_2_21 # 直接在使用者 package 中指定
coreutils
emacs-unstable-pgtk
];
# skip
}
當然這作法跟結果第一種做法一樣,只是反過來而已。
第三種嘗試:透過 nix-darwin 在系統安裝新版 nix
既然 daemon 是由 nix-darwin 中透過 service.nix-daemon.enable
設定,那就在系統中安裝。
environment.systemPackages = with pkgs; [
nixVersions.nix_2_21
];
這個的結果是會 package 在建立的過程中會衝突,因為我同時指定要在同一層使用不同版本的 nix。
第四種嘗試:自己控制版本
既然知道 service.nix-daemon.enable
是由 nix-darwin 來控制,那就自己來控制吧。把 service.nix-daemon.enable
改成 false
之後會失敗,因為 nix-darwin
會偵測到, macOS 只能用 daemon 來管理,關閉沒設定是會出事的。 如果要自己管理,那還要把 nix.useDaemon
打開。想當然一沒弄好當然是整個 daemon 就不見了。因為沒了 daemon 所以就整個卡死了。
解決的方式是自己起 nix-daemon
,這邊要注意的是要用 sudo 而且還要解除 macOS 對 fork 的限制。然後把系統還原。
sudo OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES nix-daemon
第五種嘗試:看 source code
在 nix-darwin 文件 有連結到 source code 連結。算是 nix 文件的好處跟壞處吧。好處是有 source code,壞處是因為有 source code 所以文件稀缺必須要看 source code。
其中有 這一段 大概是去 launchd 中增加這一段,對照 /Library/LaunchDaemons/org.nixos.nix-daemon.plist
中的內容是差不多的。 這邊發現他是去抓 config.nix.package
對比就是抓 nix.package
指定的 package。
文件指出 nix.package
的預設值是 pkgs.nix
,這邊就改成指定的版本試試。原來這邊寫 pkgs.nixFlakes
應該是不知道從哪邊抄來的,現在也沒這個套件了。
# configuration.nix
nix = {
pacakge = pkgs.nixVersions.nix_2_21; # 指定版本
# skip
}
darwin-rebuild
的訊息看起來也很正確,重新建立了新的 launchd daemon。檢查一下看來是正確的
心得
光是升級就弄懷疑人生。nix 目前還沒有達到完全抽象的高度,導致要用除了要熟系統本身的基本架構之外還要在額外疊一層 nix 的抽象,更別提要在 nix 之上在加 nix-darwin(nixos) 和 home-manager。跟 homebrew 來說使用者友善度還有很長一段路要走。 而這段期間也開始有底層掛 nix 的開發環境工具慢慢出現。 像 flox 就是其中之一。某些層度上也算接近自己理想的介面。可以用比較傳統的方式把環境拉出來之後在儲存,而不用去寫 nix。
當然 nix 好處就是這篇文章使用的 hugo 依然是直接用 nix-shell 跑出來的,很方便。搭配 direnv
還可以達到進專案進出資料夾自動 load/unload。 完全不會污染整個系統。
reference
https://discourse.nixos.org/t/fail-multiple-versions-of-nix-found-in-path/19890/5 https://github.com/LnL7/nix-darwin/issues/655#issuecomment-1551771624 https://daiderd.com/nix-darwin/manual/index.html