从零开始的 NixOS 教程(Flake篇)
本文最后更新于 2023年11月11日 凌晨
想不到还有人看,本来我都弃坑了,想到有人看,又捡起来了。我的 flake 已经托管到 github 仓库了,目前我还算新手,因此提交信息还比较乱。
安装教程见从零开始的 NixOS 教程(安装篇)。
Nix 系列最吸引我的地方之一就在于 flake 了,我们可以把所有的 flake 托管到 GitHub, 然后在服务器上引入。这样简简单单就备份好了各种配置文件。重装系统或者批量部署非常容易。
简单说明 Nix 的语法
我在写 flake 的时候,最痛苦的就是不知道要怎么写,也不知道该去哪里学。后来慢慢读了一些文档,但是也还是不会写。直到我看到了 nix pills. 对 nix 的机制有了一些了解,才算比较会用/会写 nix 了。
Nix 是一个函数式编程语言,我们需要注意的通常就三点:
{}是一个set, 不是其他语言中的作用域的概念。p:q是一个函数,接受一个参数p,输出一个q.- 某些
.nix文件需要遵守特定的格式(包含且只能包含某些特定的属性,比如flake.nix)
开始抄一个 Flake
我们在上一篇教程里面已经开启了 flake 支持,现在只要在 /etc/nixos 下面新建 flake.nix 并输入如下内容
1 | |
这里简单说一下: 这个 flake.nix 就是一个 set, 具有三个属性 description(字符串), inputs(set), outputs(函数)。
没错, outputs 是一个函数 {self,nixpkgs,...}:{} 参数是第一个 {}中的 self(就是 output 自己)与 nixpkgs, 还有一些省略的参数...(就是说你可以多传,但是我们不使用),
函数返回值{}是一个 set, 它包含一个属性 nixosConfigurations。
保存之后执行 nix flake update 会自动生成一个 flake.lock, 它是一个 json, 记录了当时所使用的仓库的具体的 commit id 以保证可重复性。
1 | |
接下来我们运行 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 | |
这样子是不可以的,在 output里面只能使用它规定好的一些属性(比如nixosConfigurations),不能自己定义新的属性。
当我们需要一些辅助变量的时候,我们要使用 let 块,举例:
1 | |
另外一个建议是在 flake 的最后配置一个 Formatter, 我一般使用
1 | |
然后每次提交之前执行 nix fmt 格式化文档。当然你也可以把它写到git pre-commit 的 hook 里面,这样每次提交前会自动帮你格式化。
使用 Nvfetcher 更新 vscode 插件
vscode 的插件只有一部分在 nix 的官方源里面,其他的要自己管理,但是管理起来比较麻烦。需要手动填写版本号跟shasum, 每次还得手动更新,让人苦不堪言。
但是后来有人给我推荐了 nvfetcher, 它可以自动更新版本号跟 shasum, 替代手工维护,达到跟 flake.lock 类似的效果。我们只需要写一个 nvfetcher.toml
然后执行 nvfetcher 就能自动生成一个 nix 文件跟 json文件。
先看一下 nvfetcher 的配置吧
1 | |
这几个部分都是必要的,不能再精简了。
生成的文件我们只用 generated.json, 不使用 generated.nix. 这里需要用到一些 nix 的内置函数来处理 json, 我们先看一下需要的格式长什么样
1 | |
[] 之间的部分就是我们需要的啦,它是一个 list of set(集合的数组)。
再看看我们的 gernerated.json (一小部分)
1 | |
我们需要一个函数把 json转化为 nix 的 set. 可以参考如下函数。
1 | |
这里 parsePkg 接受一个 pkg 返回一个 set, 其中 with pkg; 算是一个局部作用域,在后面可以直接使用 pkg 的属性,例如 version,而不用写 ``pkg.version. inherit version 是version=version的简便写法,因此这里等同于version=pkg.version`.
nix 下有函数可以读取 json
1 | |
最后我们可以使用 builtins.attrValues 来将 属性转化为 list
1 | |
整体可以看 extFromMarket.