从零开始的 NixOS 教程(Flake篇)

本文最后更新于 2023年11月11日 凌晨

想不到还有人看,本来我都弃坑了,想到有人看,又捡起来了。我的 flake 已经托管到 github 仓库了,目前我还算新手,因此提交信息还比较乱。

安装教程见从零开始的 NixOS 教程(安装篇)

Nix 系列最吸引我的地方之一就在于 flake 了,我们可以把所有的 flake 托管到 GitHub, 然后在服务器上引入。这样简简单单就备份好了各种配置文件。重装系统或者批量部署非常容易。

简单说明 Nix 的语法

我在写 flake 的时候,最痛苦的就是不知道要怎么写,也不知道该去哪里学。后来慢慢读了一些文档,但是也还是不会写。直到我看到了 nix pills. 对 nix 的机制有了一些了解,才算比较会用/会写 nix 了。

Nix 是一个函数式编程语言,我们需要注意的通常就三点:

  1. {} 是一个 set, 不是其他语言中的作用域的概念。
  2. p:q 是一个函数,接受一个参数 p,输出一个 q.
  3. 某些 .nix 文件需要遵守特定的格式(包含且只能包含某些特定的属性,比如 flake.nix

开始抄一个 Flake

我们在上一篇教程里面已经开启了 flake 支持,现在只要在 /etc/nixos 下面新建 flake.nix 并输入如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
# Description, write anything or even nothing
description = "Lan Tian's NixOS Flake";

# Input config, or package repos
inputs = {
# Nixpkgs, NixOS's official repo
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};

# Output config, or config for NixOS system
outputs = { self, nixpkgs, ... }: {
# Define a system called "nixos"
nixosConfigurations."nixos" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};

# You can define many systems in one Flake file.
# NixOS will choose one based on your hostname.
#
# nixosConfigurations."nixos2" = nixpkgs.lib.nixosSystem {
# system = "x86_64-linux";
# modules = [
# ./configuration2.nix
# ];
# };
};
}

这里简单说一下: 这个 flake.nix 就是一个 set, 具有三个属性 description(字符串), inputsset), outputs(函数)。
没错, outputs 是一个函数 {self,nixpkgs,...}:{} 参数是第一个 {}中的 self(就是 output 自己)与 nixpkgs, 还有一些省略的参数...(就是说你可以多传,但是我们不使用),
函数返回值{}是一个 set, 它包含一个属性 nixosConfigurations

保存之后执行 nix flake update 会自动生成一个 flake.lock, 它是一个 json, 记录了当时所使用的仓库的具体的 commit id 以保证可重复性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1663357389,
"narHash": "sha256-oYA2nVRSi6yhCBqS5Vz465Hw+3BQOVFEhfbfy//3vTs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "da6a05816e7fa5226c3f61e285ef8d9dfc868f3c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

接下来我们运行 nixos-rebuild switch 来切换到 flake.nix。当存在 flake.nix 的时候, Nix 会优先使用 flake.nix 而不是 configuration.nix。(注意:并不是同时使用,因此我们在 flake.nix 里面手动引入了 configuration.nix

注意:如果你启用了 flake, 并使用 Git 来管理文件,那么 nix 会忽略不由 Git 管理的文件,并且仅读取 stage 或者 commit 了的文件。 这点在创建新文件的时候需要注意。
另外可以把 /etc/nixos 链接到用户目录下,并且权限改为 777(或者把 owner 修改为自己的用户,这样子可以用普通用户修改了,问过 NickCao, 这么干没问题)

值得注意的是 flake.nix 的语法是固定的,我们并不能在 output 里面自己定义变量。下面举个例子说明:

1
2
3
4
5
6
7
...
outputs={pkgs,...}:rec{
# 注意到这里我们想要用 b引用 a, 所以要加 rec.
a=1;
b=a;
...
}

这样子是不可以的,在 output里面只能使用它规定好的一些属性(比如nixosConfigurations),不能自己定义新的属性。
当我们需要一些辅助变量的时候,我们要使用 let 块,举例:

1
2
3
4
5
6
...
outputs={pkgs,...}:
let host1="nixos"; in
{
nixosConfigurations.$host1 = ...;
}

另外一个建议是在 flake 的最后配置一个 Formatter, 我一般使用

1
formatter.x86_64-linux = nixpkgs.legacyPackages.${system}.alejandra;

然后每次提交之前执行 nix fmt 格式化文档。当然你也可以把它写到git pre-commit 的 hook 里面,这样每次提交前会自动帮你格式化。

使用 Nvfetcher 更新 vscode 插件

vscode 的插件只有一部分在 nix 的官方源里面,其他的要自己管理,但是管理起来比较麻烦。需要手动填写版本号跟shasum, 每次还得手动更新,让人苦不堪言。
但是后来有人给我推荐了 nvfetcher, 它可以自动更新版本号跟 shasum, 替代手工维护,达到跟 flake.lock 类似的效果。我们只需要写一个 nvfetcher.toml
然后执行 nvfetcher 就能自动生成一个 nix 文件跟 json文件。
先看一下 nvfetcher 的配置吧

1
2
3
4
[vscode-remote-ssh-edit]
src.vsmarketplace = "ms-vscode-remote.remote-ssh-edit"
fetch.vsmarketplace = "ms-vscode-remote.remote-ssh-edit"
passthru = { publisher = "ms-vscode-remote", name = "remote-ssh-edit" }

这几个部分都是必要的,不能再精简了。
生成的文件我们只用 generated.json, 不使用 generated.nix. 这里需要用到一些 nix 的内置函数来处理 json, 我们先看一下需要的格式长什么样

1
2
3
4
5
6
7
8
9
extFromMarket = pkgs.vscode-utils.extensionsFromVscodeMarketplace (
[
# {
# name = "vscode-typescript-next";
# publisher = "ms-vscode";
# version = "5.0.202302260";
# sha256 = "KNQslqJa8zEKMu0FsVHj/J5ZonKy0wrZf+jN8PJQbLA=";
# }
]);

[] 之间的部分就是我们需要的啦,它是一个 list of set(集合的数组)。
再看看我们的 gernerated.json (一小部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"vscode-LiveServer": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "vscode-LiveServer",
"passthru": {
"name": "LiveServer",
"publisher": "ritwickdey"
},
"pinned": false,
"src": {
"name": "LiveServer-5.7.9.zip",
"sha256": "sha256-w0CYSEOdltwMFzm5ZhOxSrxqQ1y4+gLfB8L+EFFgzDc=",
"type": "url",
"url": "https://ritwickdey.gallery.vsassets.io/_apis/public/gallery/publisher/ritwickdey/extension/LiveServer/5.7.9/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage"
},
"version": "5.7.9"
},

我们需要一个函数把 json转化为 nix 的 set. 可以参考如下函数。

1
2
3
4
5
6
7
parsePkg = pkg:
with pkg; {
name = passthru.name;
publisher = passthru.publisher;
inherit version;
sha256 = src.sha256;
};

这里 parsePkg 接受一个 pkg 返回一个 set, 其中 with pkg; 算是一个局部作用域,在后面可以直接使用 pkg 的属性,例如 version,而不用写 ``pkg.version. inherit versionversion=version的简便写法,因此这里等同于version=pkg.version`.

nix 下有函数可以读取 json

1
sources = builtins.fromJSON (builtins.readFile ../_sources/generated.json);

最后我们可以使用 builtins.attrValues 来将 属性转化为 list

1
map parsePkg (builtins.attrValues sources)

整体可以看 extFromMarket.

使用 deploy-rs 进行批量部署

参考资料


从零开始的 NixOS 教程(Flake篇)
https://blog.askk.cc/2023/05/29/nix-flakes/
作者
sukanka
发布于
2023年5月29日
许可协议