在 Nix 中從 URL 匯入
原文由 Michael Lynch 于 發布,訂閱此部落格
我還是個 Nix 新手,有一件事直到最近才搞懂,就是該如何把我的 configuration.nix 檔案中一部分納入版本控制。
我的目標
我希望我的 Nix 設定檔能夠模組化且可重複使用,這樣就能依照不同的系統或 flake,只引入需要的設定檔。我也希望所有的 Nix 設定檔都能納入版本控制,讓不同的系統可以依賴同一個檔案的不同版本,這樣就不用一次把每台系統上的每個設定檔都升級到最新版。
用本機檔案建立可重複使用的 bash 別名
我想做成可重複使用的 Nix 設定之一,就是我的 bash 別名。在我現有的系統上,我的 .bashrc 裡有這幾行:
alias gc='git commit --message'
alias gs='git status'
alias td='pushd $(mktemp -d)'要在 Nix 中達到同樣的效果,我可以在 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 上,這樣就能用一個標準的 configuration.nix 檔案去參照那個 URL。
以下是我的 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 來取得。
以下是一個 configuration.nix 範例,會從公開的 GitHub 儲存庫取得我的 shell.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 函式,再為這些函式建立簡短的別名。
一個例子是我的 gcbm 別名。用法像是 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?
我想,更常見的 Nix 設定模組化方式是使用Home Manager。
但老實說,我還是不太懂 Home Manager 的用途。根據專案的 README:
這個專案提供了一套基本系統,用於透過 Nix 套件管理器搭配 Nixpkgs 中的 Nix 函式庫來管理使用者環境。它可以對使用者專屬(非全域)的套件與 dotfiles 進行宣告式設定。
這讓我很困惑,因為聽起來這就是 Nix 本來就在做的事。
我已經看過 Home Manager 好幾次,但每次都覺得得花很多時間去學這個新工具,而能得到的好處卻不明顯。
或許我之後終究會發現用 Home Manager 管理 Nix 設定的好處,但就目前來說,我已經找到一個用標準 Nix 來管理遠端 Nix 檔案、相當直接的方法。
隨機一篇部落格
留言
登入後參與討論