在 Nix 中從 URL 匯入
我還是 Nix 的新手,直到最近才弄清楚如何將 configuration.nix 檔案的部分內容納入版本控制。
我的目標
我希望我的 Nix 設定檔是模組化且可重複使用的,這樣我就能根據不同的系統或 flake,只引入需要的設定檔。我也希望所有 Nix 設定檔都能納入版本控制,讓不同系統可以依賴同一個檔案的不同版本,這樣我就不需要同時將所有系統都升級到每個設定檔的最新版本。
使用本機檔案建立可重複使用的 bash 別名
我想要的可重複使用 Nix 設定之一是我的 bash 別名。在我現有的系統上,我的 .bashrc 中有以下幾行:
alias gc='git commit --message'
alias gs='git status'
alias td='pushd $(mktemp -d)'要在 bash 中達到相同的效果,我可以在 NixOS 系統上建立一個包含以下內容的檔案。我將它命名為 shell.nix,但檔名可以是任意名稱:
{
programs.bash.shellAliases = {
gc = "git commit --message";
gs = "git status";
td = "pushd $(mktemp -d)";
};
}如果我將這個檔案儲存為 /tmp/shell.nix,就可以透過將路徑加入 imports,把它匯入到我的 /etc/nixos/configuration.nix 中:
{
imports = [
"/tmp/shell.nix"
];
}如果我執行 sudo nixos-rebuild switch 然後重新啟動 shell,就可以看到我的 bash 別名已經生效:
$ td
/tmp/tmp.awb7PW7aus ~
[/tmp/tmp.awb7PW7aus]$ pwd
/tmp/tmp.awb7PW7aus將 Nix 檔案移到 URL
上述解法雖然可行,但需要在我的每台 Nix 系統上複製同一個檔案。
我比較希望將檔案託管在可公開存取的 URL 上,這樣我就能使用一個會參照該 URL 的標準 configuration.nix 檔案。
以下是調整我的 configuration.nix 以從遠端 URL 引入我的 shell.nix 檔案的方法:
let
shellAliases = builtins.fetchurl {
url = "https://mtlynch.io/notes/nix-import-from-url/shell.nix";
};
in {
imports = [
shellAliases
];
}同樣地,如果我將這些變更儲存到 configuration.nix,執行 sudo nixos-rebuild switch 並重新啟動 shell,Nix 就會從該 URL 匯入我的 bash 別名。
使用 fetchGit(選用)
取得遠端 Nix 檔案的另一種選項,是將它們存放在公開的 Git 儲存庫中,然後使用 fetchGit 來取得檔案。
以下是從公開的 GitHub 儲存庫取得我的 shell.nix 的 configuration.nix 檔案範例:
let
repo = builtins.fetchGit {
url = "https://github.com/mtlynch/nix-files";
rev = "f98500a995cb5838e40be139a8327867faaff2d5";
};
in {
imports = [
"${repo}/shell.nix"
];
}url 是我的 GitHub 儲存庫的公開 URL,而 rev 是我想匯入的檔案版本的 Git commit hash。
對 configuration.nix 做出這些變更後,我可以重新執行 sudo nixos-rebuild switch,Nix 就會從我的 GitHub 儲存庫匯入 shell.nix 檔案。
更進階的 shell 設定
我的大多數 bash 別名都是簡單的一行指令,但有幾個比較複雜。對於這些情況,我會定義 bash 函式,然後為這些函式建立簡短的 bash 別名。
其中一個例子是我的 gcbm bash 別名。我會像這樣使用它:gcbm some-branch,它會執行以下動作:
- 切換到 main 分支。
- 從遠端儲存庫拉取最新變更。
- 建立並切換到名為
some-branch的新分支。
我在名為 git_sync_and_branch 的 bash 函式中實作這個別名的主要邏輯。以下是我在 shell.nix 中的實作方式:
{
programs.bash = {
shellInit = ''
function git_sync_and_branch {
local readonly TARGET_BRANCH="''${1}"
local readonly MAIN_BRANCH='master'
git checkout "''${MAIN_BRANCH}" && \
git pull origin "''${MAIN_BRANCH}" && \
if [[ -n "''${TARGET_BRANCH}" ]]; then
git checkout -b "''${TARGET_BRANCH}"
fi
}
'';
shellAliases = {
gc = "git commit --message";
gcbm = "git_sync_and_branch";
gs = "git status";
td = "pushd $(mktemp -d)";
};
};
}陷阱:跳脫錢字號
我在嘗試將 bash 函式搬到 Nix 時遇到的一個陷阱是,我需要跳脫 ${ 序列。否則,Nix 會嘗試將它們內插為區域變數,但它們其實是 bash 變數,而不是 Nix 變數。
以下是我一開始嘗試在 git_sync_and_branch bash 函式中撰寫其中一行的方式:
git checkout "${MAIN_BRANCH}"Nix 出現了以下錯誤:
error: undefined variable 'MAIN_BRANCH'我需要在 ${ 前面加上兩個單引號('')來跳脫 ${,如下所示:
git checkout "''${MAIN_BRANCH}"為什麼不用 Home Manager?
我想,用 Home Manager 來模組化你的 Nix 設定是更為主流的做法。
但老實說,我仍然不太理解 Home Manager 的用途。根據該專案的 README:
這個專案提供了一套基礎系統,可結合 Nix 套件管理器與 Nixpkgs 中的 Nix 函式庫來管理使用者環境。它允許以宣告式方式設定使用者專屬(非全域)的套件與 dotfiles。
這讓我感到困惑,因為聽起來這就是 Nix 本來就在做的事。
我已經看過 Home Manager 好幾次,但每次都覺得必須投入大量時間學習這個新工具,而能獲得的好處對我來說卻不明確。
或許我最終會體會到用 Home Manager 管理 Nix 設定的好處,但就目前而言,我已經找到一種相當直接的方式,可以用標準的 Nix 來管理遠端的 Nix 檔案。
隨機一篇部落格