在 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 仓库的公开地址,rev 则是我想导入的文件版本所对应的 Git 提交哈希。
对 configuration.nix 做出这些修改后,再次执行 sudo nixos-rebuild switch,Nix 就会从我的 GitHub 仓库导入 shell.nix 文件。
更复杂的 Shell 配置
我的大多数 bash 别名都是简单的一行命令,但有几个要复杂一些。对于这些复杂的情况,我会先定义 bash 函数,再为这些函数创建简短的别名。
一个例子是我的 gcbm 别名。用法是这样的:gcbm some-branch,它会执行以下操作:
- 切换到主分支。
- 从远程仓库拉取最新改动。
- 创建一个名为
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 函数中写的其中一行:
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 文件。
随机一篇博客
评论
登录后参与讨论