format: prettier --write .

This commit is contained in:
Ryan Yin
2025-09-08 21:28:19 +08:00
parent cbd368817e
commit 49894699f9
57 changed files with 954 additions and 1179 deletions

View File

@@ -8,13 +8,15 @@ you've come to the right place!
An unofficial and opinionated NixOS & Flakes :book: for beginners:
https://nixos-and-flakes.thiscute.world/
### Author-Maintained Versions:
* **English Version**: https://nixos-and-flakes.thiscute.world/
* **中文版**: https://nixos-and-flakes.thiscute.world/zh/
### Author-Maintained Versions:
- **English Version**: https://nixos-and-flakes.thiscute.world/
- **中文版**: https://nixos-and-flakes.thiscute.world/zh/
### Community-Maintained Versions:
* **Versão em Português**: https://nixos-and-flakes.ieda.me/
* **日本語版**: https://nixos-and-flakes-ja.hayao0819.com/
- **Versão em Português**: https://nixos-and-flakes.ieda.me/
- **日本語版**: https://nixos-and-flakes-ja.hayao0819.com/
> If you're using macOS,
> [ryan4yin/nix-darwin-kickstarter](https://github.com/ryan4yin/nix-darwin-kickstarter)
@@ -88,6 +90,7 @@ who already contributed to this project!
Yin is licensed under [CC BY-SA 4.0](./LICENSE.md)
[^1]: [Flakes - NixOS Wiki](https://wiki.nixos.org/wiki/Flakes)
[^2]:
[Flakes are such an obviously good thing](https://grahamc.com/blog/flakes-are-an-obviously-good-thing/)

View File

@@ -1,8 +1,7 @@
import { defineConfig } from 'vitepress'
import { shared } from './shared'
import { en } from './en'
import { zh } from './zh'
import { defineConfig } from "vitepress"
import { shared } from "./shared"
import { en } from "./en"
import { zh } from "./zh"
export default defineConfig({
...shared,
@@ -12,13 +11,13 @@ export default defineConfig({
"en/:rest*": ":rest*",
},
locales: {
root: {
label: 'English',
...en
root: {
label: "English",
...en,
},
zh: {
label: '简体中文',
...zh
label: "简体中文",
...zh,
},
pt: {
label: "Português",
@@ -45,14 +44,13 @@ export default defineConfig({
// },
//
// // Languages maintained by the original author
// en: {
// en: {
// label: 'English',
// link: "https://nixos-and-flakes.thiscute.world/",
// },
// zh: {
// label: '简体中文',
// label: '简体中文',
// link: "https://nixos-and-flakes.thiscute.world/zh/",
// },
// },
})

View File

@@ -1,4 +1,4 @@
import { createRequire } from 'module'
import { createRequire } from "module"
import { generateSitemap as sitemap } from "sitemap-ts"
import { PageData, defineConfig } from "vitepress"
@@ -43,14 +43,17 @@ export const shared = defineConfig({
},
head: [
["link", { rel: "icon", href: "/favicon-16x16.png", sizes: "16x16" }],
["link", { rel: "icon", href: "/favicon-32x32.png", sizes: "32x32" }],
["link", { rel: "icon", href: "/favicon-16x16.png", sizes: "16x16" }],
["link", { rel: "icon", href: "/favicon-32x32.png", sizes: "32x32" }],
// Google Search and Android Chrome
["link", { rel: "icon", href: "/favicon-96x96.png", sizes: "96x96" }],
["link", { rel: "icon", href: "/web-app-manifest-192x192.png", sizes: "192x192" }],
["link", { rel: "icon", href: "/web-app-manifest-512x512.png", sizes: "512x512" }],
// For Apple iPhone/iPad
["link", { rel: "apple-touch-icon", href: "/apple-touch-icon.png", sizes: "180x180" }],
[
"link",
{ rel: "apple-touch-icon", href: "/apple-touch-icon.png", sizes: "180x180" },
],
// site.manifest
["link", { rel: "manifest", href: "/site.webmanifest" }],

View File

@@ -40,8 +40,8 @@ in
After modifying the configuration, run `sudo nixos-rebuild switch` (or
`home-manager switch` if you are using Home Manager standalone) to apply the changes. From
then on, any modifications you make to `~/nix-config/home/nvim` or `~/nix-config/home/doom` will be
immediately observed by Neovim/Emacs.
then on, any modifications you make to `~/nix-config/home/nvim` or
`~/nix-config/home/doom` will be immediately observed by Neovim/Emacs.
This way, you can manage all your Dotfiles using a single nix-config repository, while
frequently modified non-Nix configurations can take effect quickly, unaffected by Nix.

View File

@@ -38,10 +38,10 @@ then downloads the repository, locates the `flake.nix` within, and runs the corr
The roles of `NIX_PATH` and the Flake Registry have been explained earlier. In daily use,
we typically want the `nixpkgs` used in commands like `nix repl '<nixpkgs>'`,
`nix run nixpkgs#ponysay hello` to match the system's `nixpkgs`. This is done
by default as of [NixOS 24.05][automatic flake registry]. Also, although
`nix-channel` can coexist with the Flakes feature, in practice, Flakes can
completely replace it, so we can also disable it.
`nix run nixpkgs#ponysay hello` to match the system's `nixpkgs`. This is done by default
as of [NixOS 24.05][automatic flake registry]. Also, although `nix-channel` can coexist
with the Flakes feature, in practice, Flakes can completely replace it, so we can also
disable it.
[automatic flake registry]: https://github.com/NixOS/nixpkgs/pull/254405

View File

@@ -27,7 +27,7 @@ add the following code to one of your Nix modules:
(let base = pkgs.appimageTools.defaultFhsEnvArgs; in
pkgs.buildFHSUserEnv (base // {
name = "fhs";
targetPkgs = pkgs:
targetPkgs = pkgs:
# pkgs.buildFHSUserEnv provides only a minimal FHS environment,
# lacking many basic packages needed by most software.
# Therefore, we need to add them manually.

View File

@@ -37,9 +37,9 @@ In Nix, you can configure cache servers using the following options:
the build process of a certain library, they must take on the corresponding security
risks and decide whether to add the public key of that cache server to
`trusted-public-keys`. To completely solve this trust issue, Nix has introduced the
experimental feature [ca-derivations](https://wiki.nixos.org/wiki/Ca-derivations), which
does not depend on `trusted-public-keys` for signature verification. Interested
users can explore it further.
experimental feature [ca-derivations](https://wiki.nixos.org/wiki/Ca-derivations),
which does not depend on `trusted-public-keys` for signature verification.
Interested users can explore it further.
You can configure the `substituters` and `trusted-public-keys` parameters in the following
ways:

View File

@@ -104,7 +104,12 @@ submodule. Here's an example of a Home Manager submodule:
## Pinning a package version with an overlay
The above approach is perfect for application packages, but sometimes you need to replace libraries used by those packages. This is where [Overlays](../nixpkgs/overlays.md) shine! Overlays can edit or replace any attribute of a package, but for now we'll just pin a package to a different nixpkgs version. The main disadvantage of editing a dependency with an overlay is that your Nix installation will recompile all installed packages that depend on it, but your situation may require it for specific bug fixes.
The above approach is perfect for application packages, but sometimes you need to replace
libraries used by those packages. This is where [Overlays](../nixpkgs/overlays.md) shine!
Overlays can edit or replace any attribute of a package, but for now we'll just pin a
package to a different nixpkgs version. The main disadvantage of editing a dependency with
an overlay is that your Nix installation will recompile all installed packages that depend
on it, but your situation may require it for specific bug fixes.
```nix
# overlays/mesa.nix

View File

@@ -85,29 +85,31 @@ them with the corresponding New CLI commands (except for `nix-collect-garbage`,
is currently no alternative for this command):
1. `nix-channel`: `nix-channel` manages versions of inputs like nixpkgs through
stable/unstable channels, similar to the package lists used by other package
management tools such as apt/yum/pacman. This is what traditionally provides
`<nixpkgs>` in the Nix language.
1. In Flakes, the functionality of `nix-channel` is replaced by
the Flake Registry (`nix registry`) for providing "some unspecified global
version of nixpkgs" for interactive CLI usage (e.g. `nix run nixpkgs#hello`).
When using a `flake.nix`, input versions are managed in the flake itself.
2. Flakes use the `inputs` section in `flake.nix` to manage
versions of nixpkgs and other inputs in each Flake instead of using
global state.
stable/unstable channels, similar to the package lists used by other package management
tools such as apt/yum/pacman. This is what traditionally provides `<nixpkgs>` in the
Nix language.
1. In Flakes, the functionality of `nix-channel` is replaced by the Flake Registry
(`nix registry`) for providing "some unspecified global version of nixpkgs" for
interactive CLI usage (e.g. `nix run nixpkgs#hello`). When using a `flake.nix`,
input versions are managed in the flake itself.
2. Flakes use the `inputs` section in `flake.nix` to manage versions of nixpkgs and
other inputs in each Flake instead of using global state.
2. `nix-env`: `nix-env` is a core command-line tool for classic Nix used to manage
software packages in the user environment.
1. It installs packages from the data sources added by `nix-channel`, causing the
installed package's version to be influenced by the channel. Packages installed with
`nix-env` are not automatically recorded in Nix's declarative configuration and are
completely independent of its control, making them challenging to reproduce on other
machines. Upgrading packages installed by `nix-env` is slow and may
produce unexpected results because the attribute name where the package
was found in nixpkgs is not saved.
machines. Upgrading packages installed by `nix-env` is slow and may produce
unexpected results because the attribute name where the package was found in nixpkgs
is not saved.
Therefore, it is not recommended to use this command directly.
2. The corresponding command in the New CLI is `nix profile`. Personally, I don't
recommend it for beginners.
3. `nix-shell`: `nix-shell` creates a temporary shell environment, which is useful for
development and testing.
1. New CLI: This tool is divided into three sub-commands: `nix develop`, `nix shell`,
@@ -126,6 +128,7 @@ is currently no alternative for this command):
[Try to explain nix commands](https://qiita.com/Sumi-Sumi/items/6de9ee7aab10bc0dbead?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en).
[^1]: [Flakes - NixOS Wiki](https://wiki.nixos.org/wiki/Flakes)
[^2]:
[Flakes are such an obviously good thing](https://grahamc.com/blog/flakes-are-an-obviously-good-thing/)

View File

@@ -182,5 +182,3 @@ source code, and study its implementation.
https://github.com/NixOS/nixpkgs/blob/nixos-25.05/lib/modules.nix#L122-L184
[nixpkgs/nixos-25.05/nixos/doc/manual/development/option-types.section.md#L268-L275]:
https://github.com/NixOS/nixpkgs/blob/nixos-25.05/nixos/doc/manual/development/option-types.section.md?plain=1#L268-L275

View File

@@ -128,12 +128,10 @@ Currently, our flake includes these files:
Up to this point, we have merely added a very simple configuration file,
`/etc/nixos/flake.nix`, which has merely been a thin wrapper around
`/etc/nixos/configuration.nix`, offering no new functionality and introducing no
disruptive changes.
disruptive changes.
In the content of the book that follows, we will learn about the structure and
functionality of `flake.nix` and gradually see the benefits that such a wrapper can
bring.
functionality of `flake.nix` and gradually see the benefits that such a wrapper can bring.
> Note: The configuration management method described in this book is NOT "Everything in a
> single file". It is recommended to categorize configuration content into different nix

View File

@@ -202,9 +202,10 @@ for `foo` by setting the `options` defined here. For example:
```
In the example above, the way we assign values to `options` is actually a kind of
**abbreviation**. When a module only contains `config` without any other declaration (like `option` and other
special parameters of the module system), we can omit the `config` wrapping , just directly write the
content of `config` to assign value to `option` section declared in other modules!
**abbreviation**. When a module only contains `config` without any other declaration (like
`option` and other special parameters of the module system), we can omit the `config`
wrapping , just directly write the content of `config` to assign value to `option` section
declared in other modules!
## Assignment and Lazy Evaluation in the Module System

View File

@@ -78,10 +78,9 @@ Here's an example excerpt from the NixOS Wiki that demonstrates the structure of
}
```
## References
- [Flakes Check - Nix Manual]
[Flakes Check - Nix Manual]: https://nix.dev/manual/nix/stable/command-ref/new-cli/nix3-flake-check
[Flakes Check - Nix Manual]:
https://nix.dev/manual/nix/stable/command-ref/new-cli/nix3-flake-check

View File

@@ -18,4 +18,4 @@
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
}

View File

@@ -2,14 +2,10 @@
## 社区
- [Nix 社区官方页](https://nixos.org/community/): 包含官方社区、论坛、RFCs、官方团队的架构
以及沟通贡献渠道等信息。
- [Nix 社区官方页](https://nixos.org/community/): 包含官方社区、论坛、RFCs、官方团队的架构以及沟通贡献渠道等信息。
- [Nix Channel Status](https://status.nixos.org/): Nix 各 Channels 的当前的构建状态。
- [nix-community/NUR](https://github.com/nix-community/NUR): Nixpkgs 虽然包含了大量的软件
但是因为审核速度、许可协议等原因总有些包并未被及时收录。NUR 则是一个去中心化的 Nix
包 仓库,任何人都可以创建自己的私人仓库并将其加入到 NUR 中以便其他人使用。如果你想要使用
Nixpkgs 中没有的包,可以尝试在这里寻找。如果你希望将自己打的 Nix 包分享给别人,可以根据
NUR 的 README 创建并分享你自己的私人 Nix 仓库。
- [nix-community/NUR](https://github.com/nix-community/NUR):
Nixpkgs 虽然包含了大量的软件但是因为审核速度、许可协议等原因总有些包并未被及时收录。NUR 则是一个去中心化的 Nix 包 仓库,任何人都可以创建自己的私人仓库并将其加入到 NUR 中以便其他人使用。如果你想要使用 Nixpkgs 中没有的包,可以尝试在这里寻找。如果你希望将自己打的 Nix 包分享给别人,可以根据 NUR 的 README 创建并分享你自己的私人 Nix 仓库。
中文社区:
@@ -22,33 +18,29 @@
## 文档与视频
逐渐熟悉 Nix 这一套工具链后,可以进一步读一读 Nix 的三本手册以及其他社区文档,挖掘更多的玩
法:
逐渐熟悉 Nix 这一套工具链后,可以进一步读一读 Nix 的三本手册以及其他社区文档,挖掘更多的玩法:
- [Eelco Dolstra - The Purely Functional Software Deployment Model - 2006](https://edolstra.github.io/pubs/phd-thesis.pdf):
Eelco Dolstra 开创性的博士论文,介绍了 Nix 包管理器的设计思想。
- [Nix Reference Manual](https://nixos.org/manual/nix/stable/package-management/profiles.html):
Nix 包管理器使用手册,主要包含 Nix 包管理器的设计、命令行使用说明。
- [nixpkgs Manual](https://nixos.org/manual/nixpkgs/unstable/): 主要介绍 Nixpkgs 的参
数、Nix 包的使用、修改、打包方法。
- [NixOS Manual](https://nixos.org/manual/nixos/unstable/): NixOS 系统使用手册,主要包含
Wayland/X11, GPU 等系统级别的配置说明。
- [nix-pills](https://nixos.org/guides/nix-pills): Nix Pills 对如何使用 Nix 构建软件包进行
了深入的阐述,写得比官方文档清晰易懂,而且也足够深入,值得一读
- [nixos-in-production](https://github.com/Gabriella439/nixos-in-production): 这是一本介绍
如何在生产环境中使用 NixOS 的书籍,目前还在编写中,不过已经有了一些很棒的内容。
- [nixpkgs Manual](https://nixos.org/manual/nixpkgs/unstable/): 主要介绍 Nixpkgs 的参数、Nix 包的使用、修改、打包方法。
- [NixOS Manual](https://nixos.org/manual/nixos/unstable/):
NixOS 系统使用手册,主要包含 Wayland/X11, GPU 等系统级别的配置说明。
- [nix-pills](https://nixos.org/guides/nix-pills): Nix
Pills 对如何使用 Nix 构建软件包进行了深入的阐述,写得比官方文档清晰易懂,而且也足够深入,值得一读。
- [nixos-in-production](https://github.com/Gabriella439/nixos-in-production): 这是一本介绍如何在生产环境中使用 NixOS 的书籍,目前还在编写中,不过已经有了一些很棒的内容
另外 Youtube 上的 [NixOS Foundation](https://www.youtube.com/@NixOS-Foundation) 跟
[NixCon](https://www.youtube.com/@NixCon) 两个频道上也有很多官方视频,干货满满。如下几个视
频强烈推荐一阅:
[NixCon](https://www.youtube.com/@NixCon)
两个频道上也有很多官方视频,干货满满。如下几个视频强烈推荐一阅:
- [Summer of Nix 2022 — Public Lecture Series](https://www.youtube.com/playlist?list=PLt4-_lkyRrOMWyp5G-m_d1wtTcbBaOxZk):
NixOS Foundation 举办的一系列公开讲座,由 Eelco Dolstra、Armijn Hemel 等 Nix 社区核心成
员主讲,内容涵盖了 Nix 的发展历程、NixOS 的历史、Nix 的未来等多个方面,干货满满。
NixOS Foundation 举办的一系列公开讲座,由 Eelco Dolstra、Armijn
Hemel 等 Nix 社区核心成员主讲,内容涵盖了 Nix 的发展历程、NixOS 的历史、Nix 的未来等多个方面,干货满满。
- [Summer of Nix 2023 — Nix Developer Dialogues](https://www.youtube.com/playlist?list=PLt4-_lkyRrOPcBuz_tjm6ZQb-6rJjU3cf):
2023 年的 Summer of Nix一系列 Nix 社区核心成员的对话,内容包含 Nixpkgs 的演进与架构方
面的挑战,探索 Nix 的模块系统,讨论 Nix 生态Nixpkgs 中的 AI 应用Nix 在商业领域的应用
与开源经济学。
2023 年的 Summer of
Nix一系列 Nix 社区核心成员的对话,内容包含 Nixpkgs 的演进与架构方面的挑战,探索 Nix 的模块系统,讨论 Nix 生态Nixpkgs 中的 AI 应用Nix 在商业领域的应用与开源经济学。
另外 @NickCao 在 2021 年做的一个深入介绍 Nix 包管理器的演讲也很值得一阅:
@@ -56,13 +48,11 @@
## 进阶技术与社区项目
在对 Nix Flakes 熟悉到一定程度后,你可以尝试一些 Flakes 的进阶玩法,如下是一些比较流行的社
区项目,可以试用:
在对 Nix
Flakes 熟悉到一定程度后,你可以尝试一些 Flakes 的进阶玩法,如下是一些比较流行的社区项目,可以试用:
- [flake-parts](https://github.com/hercules-ci/flake-parts): 通过 Module 模块系统简化配置
的编写与维护。
- [flake-utils-plus](https://github.com/gytis-ivaskevicius/flake-utils-plus):同样是用于简
化 Flake 配置的第三方包,不过貌似更强大些
- [flake-parts](https://github.com/hercules-ci/flake-parts): 通过 Module 模块系统简化配置的编写与维护。
- [flake-utils-plus](https://github.com/gytis-ivaskevicius/flake-utils-plus):同样是用于简化 Flake 配置的第三方包,不过貌似更强大些
- ......
以及其他许多实用的社区项目可探索:
@@ -70,20 +60,13 @@
- [nix-output-monitor](https://github.com/maralorn/nix-output-monitor): 美化 `nix build`
命令的输出日志,同时打印出更详细的日志信息,以及构建计时器等额外信息,强烈推荐使用!
- [agenix](https://github.com/ryantm/agenix): secrets 管理工具
- [nixos-generator](https://github.com/nix-community/nixos-generators): 镜像生成工具,从
nixos 配置生成 iso/qcow2 等格式的镜像
- [nixos-generator](https://github.com/nix-community/nixos-generators): 镜像生成工具,从 nixos 配置生成 iso/qcow2 等格式的镜像
- [lanzaboote](https://github.com/nix-community/lanzaboote): 启用 secure boot
- [impermanence](https://github.com/nix-community/impermanence): 用于配置无状态系统。可用
它持久化你指定的文件或文件夹,同时再将 /home 目录挂载为 tmpfs 或者每次启动时用工具擦除一
遍。这样所有不受 impermanence 管理的数据都将成为临时数据,如果它们导致了任何问题,重启下
系统这些数据就全部还原到初始状态了!
- [impermanence](https://github.com/nix-community/impermanence): 用于配置无状态系统。可用它持久化你指定的文件或文件夹,同时再将 /home 目录挂载为 tmpfs 或者每次启动时用工具擦除一遍。这样所有不受 impermanence 管理的数据都将成为临时数据,如果它们导致了任何问题,重启下系统这些数据就全部还原到初始状态了!
- [colmena](https://github.com/zhaofengli/colmena): NixOS 主机部署工具
- [devbox](https://github.com/jetpack-io/devbox): 一个基于 Nix 的轻量级开发环境管理工具,
类似 earthly目标是统一开发环境与部署环境使得开发环境与部署环境一致
- [nixpak](https://github.com/nixpak/nixpak): 一个使用沙箱运行任何 Nix 应用程序的工具(包
括 GUI 应用程序),提升系统安全性
- [nixpacks](https://github.com/railwayapp/nixpacks): 一个将任何代码自动打包为 OCI 容器镜
像的工具,类似 buildpacks
- [devbox](https://github.com/jetpack-io/devbox): 一个基于 Nix 的轻量级开发环境管理工具,类似 earthly目标是统一开发环境与部署环境使得开发环境与部署环境一致
- [nixpak](https://github.com/nixpak/nixpak): 一个使用沙箱运行任何 Nix 应用程序的工具(包括 GUI 应用程序),提升系统安全性
- [nixpacks](https://github.com/railwayapp/nixpacks): 一个将任何代码自动打包为 OCI 容器镜像的工具,类似 buildpacks
- ...
想了解更多内容,可以看看 [awesome-nix](https://github.com/nix-community/awesome-nix).

View File

@@ -1,20 +1,19 @@
# 加速 Dotfiles 的调试
在使用了 Home Manager 管理我们的 Dotfiles 后,会遇到的一个问题是,每次修改完我们的
Dotfiles都需要通过跑一遍 `sudo nixos-rebuild switch`(或者如果你是单独使用 home manager的
话,应该是这个指令 `home-manager switch`) 才能生效,但每次运行这个指令都会重新计算整个系统
的状态,即使 Nix 内部现在已经有了很多缓存机制可以加速这个计算,这仍然是很痛苦的。
在使用了 Home
Manager 管理我们的 Dotfiles 后,会遇到的一个问题是,每次修改完我们的 Dotfiles都需要通过跑一遍
`sudo nixos-rebuild switch`(或者如果你是单独使用 home manager的话应该是这个指令
`home-manager switch`) 才能生效,但每次运行这个指令都会重新计算整个系统的状态,即使 Nix 内部现在已经有了很多缓存机制可以加速这个计算,这仍然是很痛苦的。
以我的 Neovim/Emacs 配置为例,我日常修改它们的频率非常高,有时候一天要改几十上百次,如果每
次修改都要等 `nixos-rebuild` 跑个几十秒,这简直是在浪费时间。
以我的 Neovim/Emacs 配置为例,我日常修改它们的频率非常高,有时候一天要改几十上百次,如果每次修改都要等
`nixos-rebuild` 跑个几十秒,这简直是在浪费时间。
幸运的是Home Manager 提供了一个 [mkOutOfStoreSymlink][mkOutOfStoreSymlink] 函数, 它可以
创建一个指向你 Dotfiles 绝对路径的软链接, 这样就能绕过 Home Manager 自身,你对 Dotfiles 的
修改就能立即生效了.
幸运的是Home Manager 提供了一个 [mkOutOfStoreSymlink][mkOutOfStoreSymlink]
函数, 它可以创建一个指向你 Dotfiles 绝对路径的软链接, 这样就能绕过 Home
Manager 自身,你对 Dotfiles 的修改就能立即生效了.
这种方法能有用的前提是,你的 Dotfiles 内容不是由 Nix 生成的,比如我的 Emacs/Neovim 配置都
是原生的,仅通过 Nix Home-Manager 的 `home.file``xdg.configFile` 将它们链接到正确的位
置。
这种方法能有用的前提是,你的 Dotfiles 内容不是由 Nix 生成的,比如我的 Emacs/Neovim 配置都是原生的,仅通过 Nix
Home-Manager 的 `home.file``xdg.configFile` 将它们链接到正确的位置。
下面简单说明下如何通过这个函数加速 Dotfiles 的调试.
@@ -36,12 +35,12 @@ in
}
```
修改完配置后,运行 `sudo nixos-rebuild switch` (或者如果你是单独使用 home manager的话
该是这个指令 `home-manager switch`)即可生效。这之后,你对 `~/nix-config/home/nvim`
`~/nix-config/home/doom` 的修改就能立即被 Neovim/Emacs 观察到了.
修改完配置后,运行 `sudo nixos-rebuild switch` (或者如果你是单独使用 home
manager的话该是这个指令 `home-manager switch`)即可生效。这之后,你对
`~/nix-config/home/nvim` `~/nix-config/home/doom`
的修改就能立即被 Neovim/Emacs 观察到了.
这样你就既能使用一个 nix-config 仓库统一管理所有 Dotfiles, 一些频繁修改的非 Nix 配置也能快
速生效,不受 Nix 的影响.
这样你就既能使用一个 nix-config 仓库统一管理所有 Dotfiles, 一些频繁修改的非 Nix 配置也能快速生效,不受 Nix 的影响.
[mkOutOfStoreSymlink]:
https://github.com/search?q=repo%3Anix-community%2Fhome-manager%20outOfStoreSymlink&type=code

View File

@@ -18,8 +18,8 @@ sudo nixos-rebuild switch --flake .#myhost --show-trace -L -v
> 注:如果你禁用了 `NIX_PATH`,那么 `<nixpkgs>` 这样的语法将无法使用,你需要改用
> `nix repl -f flake:nixpkgs` 来加载 nixpkgs。
前面我们已经使用 `nix repl '<nixpkgs>'` 看过很多次源码了,这是一个非常强大的工具,可以帮助
我们理解 Nix 的工作原理。
前面我们已经使用 `nix repl '<nixpkgs>'`
看过很多次源码了,这是一个非常强大的工具,可以帮助我们理解 Nix 的工作原理。
要学会用 `nix repl`,最好先看看它的 help 信息:
@@ -155,8 +155,8 @@ outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/
#......
```
能看到,通过 `nix repl` 加载好我的 flake 配置后,就能很方便地检查所有的配置项了,这对于调
试非常有用。
能看到,通过 `nix repl`
加载好我的 flake 配置后,就能很方便地检查所有的配置项了,这对于调试非常有用。
## 使用 nixpkgs 中提供的调试函数

View File

@@ -1,4 +1,3 @@
# 最佳实践
Nix 非常强大且灵活,做一件事有非常多的方法,这就导致了很难找到最合适的方法来做你的工作。这
里记录了一些我在使用 NixOS 中学习到的最佳实践,希望能帮到你。
Nix 非常强大且灵活,做一件事有非常多的方法,这就导致了很难找到最合适的方法来做你的工作。这里记录了一些我在使用 NixOS 中学习到的最佳实践,希望能帮到你。

View File

@@ -2,39 +2,38 @@
## NIX_PATH 介绍 {#nix-path-introduction}
Nix 搜索路径由环境变量 `NIX_PATH` 控制,它的格式与 Linux 的 `PATH` 环境变量一致,由冒号分
隔的多个路径组成。
Nix 搜索路径由环境变量 `NIX_PATH` 控制,它的格式与 Linux 的 `PATH`
环境变量一致,由冒号分隔的多个路径组成。
Nix 表达式中形如 `<name>` 的路径会被解析为 `NIX_PATH` 中名为 `name` 的路径。
这种使用方式在 Flakes 特性下已经不推荐使用了,因为它会导致 Flake 的构建结果依赖一个可变的
环境变量 `NIX_PATH`,可复现能力大打折扣。
这种使用方式在 Flakes 特性下已经不推荐使用了,因为它会导致 Flake 的构建结果依赖一个可变的环境变量
`NIX_PATH`,可复现能力大打折扣。
但是在某些场景下,我们还是需要使用 `NIX_PATH`,比如我们前面多次使用了
`nix repl '<nixpkgs>'` 命令,它就是使用了从 `NIX_PATH` 搜索到的 Nixpkgs。
## Flakes Registry 介绍 {#flakes-registry-introduction}
Flake Registry 是一个 Flake 注册中心,它可以帮助我们在使用 `nix run`, `nix shell` 等命令
时,使用一个简短的 id 来代替长长的 flake 仓库地址。
Flake Registry 是一个 Flake 注册中心,它可以帮助我们在使用 `nix run`, `nix shell`
等命令时,使用一个简短的 id 来代替长长的 flake 仓库地址。
默认情况下Nix 会从
<https://github.com/NixOS/flake-registry/blob/master/flake-registry.json> 中找到这个 id 对
应的 github 仓库地址。
<https://github.com/NixOS/flake-registry/blob/master/flake-registry.json>
中找到这个 id 对应的 github 仓库地址。
比如说我们执行 `nix run nixpkgs#ponysay hello`nix 会自动从上述 json 文件中找到 `nixpkgs`
对应的 github 仓库地址,然后下载这个仓库,再通过其中的 `flake.nix` 查找对应的 `ponysay`
并运行它。
对应的 github 仓库地址,然后下载这个仓库,再通过其中的 `flake.nix` 查找对应的 `ponysay`
并运行它。
## 自定义 NIX_PATH 与 Flake Registry {#custom-nix-path-and-flake-registry-2}
> **注意:新手请先跳过这部分内容!因为配置如果抄得不对,关掉 nix-channel 可能会导致一些令
> 人头疼的错误。**
> **注意:新手请先跳过这部分内容!因为配置如果抄得不对,关掉 nix-channel 可能会导致一些令人头疼的错误。**
前面说明了 `NIX_PATH` 与 Flake Registry 的作用。在日常使用中,我们一般都会希望能在执行
`nix repl '<nixpkgs>'`, `nix run nixpkgs#ponysay hello` 等命令时,使用的 nixpkgs 与系统一
致。**NixOS 24.05 自带了这一功能**PR [automatic flake registry])。此外我们也可以主动关闭 `nix-channel`,因为 flakes 已经能够完全替代它。
`nix repl '<nixpkgs>'`, `nix run nixpkgs#ponysay hello`
等命令时,使用的 nixpkgs 与系统一致。**NixOS 24.05 自带了这一功能**PR [automatic flake
registry])。此外我们也可以主动关闭 `nix-channel`,因为 flakes 已经能够完全替代它。
[automatic flake registry]: https://github.com/NixOS/nixpkgs/pull/254405

View File

@@ -5,13 +5,8 @@ Nix 本身的设计就很适合远程部署Nix 社区也有许多专门用于
[colmena](https://github.com/zhaofengli/colmena)。另外我们前面已经用了很多次的官方工具
`nixos-rebuild`,它拥有一定的远程部署能力。
此外在多架构场景下,远程部署还可以充分利用 Nix 的多架构支持,比如说在 x86_64 主机上交叉编
译 aarch64/aarch64 的 NixOS 系统配置,然后通过 SSH 远程部署到对应的主机上。我最近遇到的一
个场景是,我本地交叉编译了一块 RISCV64 开发板的 NixOS 系统镜像,那么我本地已经拥有了交叉编
译该系统的所有编译缓存。但是由于 NixOS 官方几乎没有 RISCV64 的二进制缓存,我直接在开发板上
执行任何未预装的程序(比如 `nix run nixpkgs#cowsay hello`)都会导致大量的编译,这会耗费我
数小时的时间,是难以接受的。而改用远程部署的话,我就能充分利用上本机的高性能 CPU 与大量编
译缓存,体验就很好了。
此外在多架构场景下,远程部署还可以充分利用 Nix 的多架构支持,比如说在 x86_64 主机上交叉编译 aarch64/aarch64 的 NixOS 系统配置,然后通过 SSH 远程部署到对应的主机上。我最近遇到的一个场景是,我本地交叉编译了一块 RISCV64 开发板的 NixOS 系统镜像,那么我本地已经拥有了交叉编译该系统的所有编译缓存。但是由于 NixOS 官方几乎没有 RISCV64 的二进制缓存,我直接在开发板上执行任何未预装的程序(比如
`nix run nixpkgs#cowsay hello`)都会导致大量的编译,这会耗费我数小时的时间,是难以接受的。而改用远程部署的话,我就能充分利用上本机的高性能 CPU 与大量编译缓存,体验就很好了。
这里我简单介绍下如何使用 colmena 与 `nixos-rebuild` 进行远程部署。
@@ -21,24 +16,22 @@ Nix 本身的设计就很适合远程部署Nix 社区也有许多专门用于
1. 为了防止远程主机的 sudo 密码验证失败,有两种方法,二选一:
1. 以远程主机的 `root` 用户身份部署,这是推荐使用的方法。
2. 在远程主机的配置中添加 `security.sudo.wheelNeedsPassword = false;` 并提前手动部署一
次,从而为用户授予免密码验证的 sudo 权限。
1. **这会导致用户级别的程序能静默获取 sudo 权限,存在安全风险**!因此如果选用这种方
法,建议远程部署创建一个专门的用户,不应该使用自己的常用用户!
2. 在远程主机的配置中添加 `security.sudo.wheelNeedsPassword = false;`
并提前手动部署一次,从而为用户授予免密码验证的 sudo 权限。
1. **这会导致用户级别的程序能静默获取 sudo 权限,存在安全风险**!因此如果选用这种方法,建议远程部署创建一个专门的用户,不应该使用自己的常用用户!
2. 为远程主机配置 SSH 公钥身份验证
1. 可使用 `users.users.<name>.openssh.authorizedKeys.keys` 配置项完成配置。
3. 在本机主机上添加好远程主机的 Known Hosts 记录,否则 colmena/nixos-rebuild 会因为无法验
证远程主机的身份而部署失败。
3. 在本机主机上添加好远程主机的 Known
Hosts 记录,否则 colmena/nixos-rebuild 会因为无法验证远程主机的身份而部署失败。
1. 可使用 `programs.ssh.knownHosts` 配置项将远程主机的公钥添加到 Known Hosts 记录中。
4. 手动使用 `ssh root@<you-host>` 命令,验证能正常登录到远程主机。
1. 如果遇到任何问题,请先解决它们,再继续后续操作。
建议使用 `root` 用户进行部署,因为这更方便且不需要额外的配置,没有令人头疼的 sudo 权限问
题。
建议使用 `root`
用户进行部署,因为这更方便且不需要额外的配置,没有令人头疼的 sudo 权限问题。
假设我们现在要通过 root 用户进行远程部署,首先需要在远程主机上为该用户配置 SSH 公钥身份验
证。直接在远程主机的 Nix 配置的任一 NixOS Module 中(比如 `configuration.nix`)添加如下内
容,然后重新构建系统即可:
假设我们现在要通过 root 用户进行远程部署,首先需要在远程主机上为该用户配置 SSH 公钥身份验证。直接在远程主机的 Nix 配置的任一 NixOS
Module 中(比如 `configuration.nix`)添加如下内容,然后重新构建系统即可:
```nix{6-9}
# configuration.nix
@@ -55,8 +48,8 @@ Nix 本身的设计就很适合远程部署Nix 社区也有许多专门用于
}
```
然后还需要提前在本机上将用于登录的 SSH 私钥添加到 SSH agent以便在部署配置时进行身份验
证:
然后还需要提前在本机上将用于登录的 SSH 私钥添加到 SSH
agent以便在部署配置时进行身份验证:
```bash
ssh-add ~/.ssh/your-private-key
@@ -64,9 +57,9 @@ ssh-add ~/.ssh/your-private-key
## 通过 `colmena` 进行部署
`colmena` 不能直接使用我们已经熟悉的 `nixosConfigurations.xxx` 进行远程部署,它自定义了一
个名为 `colmena` 的 flake outputs 来进行远程部署,其内容结构与 `nixosConfigurations.xxx`
类似但不完全相同。
`colmena` 不能直接使用我们已经熟悉的 `nixosConfigurations.xxx`
进行远程部署,它自定义了一个名为 `colmena` 的 flake outputs 来进行远程部署,其内容结构与
`nixosConfigurations.xxx` 类似但不完全相同。
在你系统的 `flake.nix` 中添加一个新的名为 `colmena` 的 outputs一个简单的例子如下
@@ -120,8 +113,8 @@ nix run nixpkgs#colmena apply
## 通过 `nixos-rebuild` 进行部署
用 `nixos-rebuild` 进行远程部署的好处在于,它的工作方式与部署到本地主机完全相同,只需要多
传几个参数,指定下远程主机的 IP 地址、用户名等信息即可。
用 `nixos-rebuild`
进行远程部署的好处在于,它的工作方式与部署到本地主机完全相同,只需要多传几个参数,指定下远程主机的 IP 地址、用户名等信息即可。
例如,使用以下命令将 flake 中的 `nixosConfigurations.my-nixos` 这份配置部署到远程主机:
@@ -130,8 +123,8 @@ nixos-rebuild switch --flake .#my-nixos \
--target-host root@192.168.4.1 --build-host localhost --verbose
```
上述命令将会构建并部署 my-nixos 的配置到 IP 为 `192.168.4.1` 的服务器,系统构建过程将在本
机执行。
上述命令将会构建并部署 my-nixos 的配置到 IP 为 `192.168.4.1`
的服务器,系统构建过程将在本机执行。
如果你希望在远程主机上构建系统,只需要将 `--build-host localhost` 替换为
`--build-host root@192.168.4.1`。

View File

@@ -1,12 +1,7 @@
# 运行非 NixOS 的二进制文件
NixOS 不遵循 FHS 标准,因此你从网上下载的二进制程序在 NixOS 上大概率是跑不了的。为了在
NixOS 上跑这些非 NixOS 的二进制程序,需要做一些骚操作。有位老兄在这里总结了 10 种实现此目
的的方
法:[Different methods to run a non-nixos executable on Nixos](https://unix.stackexchange.com/questions/522822/different-methods-to-run-a-non-nixos-executable-on-nixos)
推荐一读。此外如果你懒得自己折腾,只想实现需求,也可以直接看看这个傻瓜式工具
[nix-alien](https://github.com/thiagokokada/nix-alien). 或者如果你熟悉 Docker直接用
Docker 跑也是个不错的选择。
NixOS 不遵循 FHS 标准,因此你从网上下载的二进制程序在 NixOS 上大概率是跑不了的。为了在 NixOS 上跑这些非 NixOS 的二进制程序,需要做一些骚操作。有位老兄在这里总结了 10 种实现此目的的方法:[Different methods to run a non-nixos executable on Nixos](https://unix.stackexchange.com/questions/522822/different-methods-to-run-a-non-nixos-executable-on-nixos),推荐一读。此外如果你懒得自己折腾,只想实现需求,也可以直接看看这个傻瓜式工具
[nix-alien](https://github.com/thiagokokada/nix-alien). 或者如果你熟悉 Docker直接用 Docker 跑也是个不错的选择。
我个人用的比较多的方法是,直接创建一个 FHS 环境来运行二进制程序,这种方法非常方便易用。
@@ -25,7 +20,7 @@ Docker 跑也是个不错的选择。
(let base = pkgs.appimageTools.defaultFhsEnvArgs; in
pkgs.buildFHSUserEnv (base // {
name = "fhs";
targetPkgs = pkgs:
targetPkgs = pkgs:
# pkgs.buildFHSUserEnv 只提供一个最小的 FHS 环境,缺少很多常用软件所必须的基础包
# 所以直接使用它很可能会报错
#
@@ -46,8 +41,8 @@ Docker 跑也是个不错的选择。
}
```
部署好上面的配置后,你就能用 `fhs` 命令进入我们定义好的 FHS 环境了,然后就可以运行你下载的
二进制程序了,比如:
部署好上面的配置后,你就能用 `fhs`
命令进入我们定义好的 FHS 环境了,然后就可以运行你下载的二进制程序了,比如:
```shell
# 进入我们定义好的 fhs 环境,它就跟其他 Linux 发行版一样了

View File

@@ -1,12 +1,11 @@
# 使用 Justfile 简化 NixOS 相关命令
在使用 NixOS 的过程中,我们会经常使用 `nixos-rebuild` 命令,经常需要输入一大堆参数,比较繁
琐。
在使用 NixOS 的过程中,我们会经常使用 `nixos-rebuild`
命令,经常需要输入一大堆参数,比较繁琐。
我使用了 [just](https://github.com/casey/just) 来管理我的 flake 配置相关的命令,简化使用。
你也可以使用其他类似的工具来干这个活(比如说 Makefile 或
[cargo-make](https://github.com/sagiegurari/cargo-make)),这里我仅介绍下我的用法以供参
考。
我使用了 [just](https://github.com/casey/just)
来管理我的 flake 配置相关的命令,简化使用。你也可以使用其他类似的工具来干这个活(比如说 Makefile 或
[cargo-make](https://github.com/sagiegurari/cargo-make)),这里我仅介绍下我的用法以供参考。
我的 Justfile 大概内容截取如下:
@@ -83,6 +82,6 @@ idols: aqua ruby kana
idols-debug: aqua-debug ruby-debug kana-debug
```
将上述 `Justfile` 文件存放到 NixOS 配置的根目录下,然后我们就可以使用 `just` 命令来执行相
关的命令了。比如说我这里 `just deploy` 就是部署 NixOS 配置到本地主机,`just idols` 就是部
署到我的远程主机集群。
将上述 `Justfile` 文件存放到 NixOS 配置的根目录下,然后我们就可以使用 `just`
命令来执行相关的命令了。比如说我这里 `just deploy`
就是部署 NixOS 配置到本地主机,`just idols` 就是部署到我的远程主机集群。

View File

@@ -1,14 +1,13 @@
# 跨平台编译
首先在任何 Linux 平台上,都有两种方法进行跨平台构建。以在 x86_64 架构上构建 aarch64 架构程
序为例,两种构建方法说明如下:
首先在任何 Linux 平台上,都有两种方法进行跨平台构建。以在 x86_64 架构上构建 aarch64 架构程序为例,两种构建方法说明如下:
1. 使用 QEMU 模拟 aarch64 架构,然后在模拟器中编译程序
1. 缺点是指令集模拟,性能低下
2. 优点是能利用上 NixOS 的 binary cache不需要自己编译所有内容
2. 使用交叉编译器编译 aarch64 架构的程序
1. 缺点是无法利用 NixOS 的 binary cache需要自己编译所有内容交叉编译也有 cache但是
里面基本没啥东西)
1. 缺点是无法利用 NixOS 的 binary
cache需要自己编译所有内容交叉编译也有 cache但是里面基本没啥东西)
2. 优点是不需要指令集模拟,性能高
如果使用方法一,则需要在构建机的 NixOS 配置中启用 aarch64 架构的 binfmt_misc
@@ -17,8 +16,8 @@
## 交叉编译
nixpkgs 包含了一系列预定义好的交叉编译工具链,其名为 `pkgsCross`,我们先通过 `nix repl`
看看有哪些工具链:
nixpkgs 包含了一系列预定义好的交叉编译工具链,其名为 `pkgsCross`,我们先通过 `nix repl`
看看有哪些工具链:
```shell
nix repl '<nixpkgs>'
@@ -63,8 +62,8 @@ pkgsCross.mipsel-linux-gnu
pkgsCross.mmix
```
如果想将一个 flake 全局的 `pkgs` 设置为交叉编译工具链,只需要在 `flake.nix` 中添加一个
Module示例如下
如果想将一个 flake 全局的 `pkgs` 设置为交叉编译工具链,只需要在 `flake.nix`
中添加一个Module示例如下
```nix{14-20}
{
@@ -95,16 +94,15 @@ Module示例如下
}
```
模块中的 `nixpkgs.crossSystem` 参数用于将 `pkgs` 设置为交叉编译工具链,这样构建出的内容全
都会是 `riscv64-linux` 架构的。
模块中的 `nixpkgs.crossSystem` 参数用于将 `pkgs`
设置为交叉编译工具链,这样构建出的内容全都会是 `riscv64-linux` 架构的。
## 通过模拟系统进行跨平台编译
第二种方法是通过模拟系统进行跨平台编译,这种方法不需要交叉编译工具链。
要使用这种方法,首先你的构建机需要在配置中启用 binfmt_misc 模块,如果你的构建机是 NixOS
将如下配置添加到你的 NixOS Module 即可启用 `aarch64-linux` 与 `riscv64-linux` 两种架构的模
拟构建系统:
要使用这种方法,首先你的构建机需要在配置中启用 binfmt_misc 模块,如果你的构建机是 NixOS将如下配置添加到你的 NixOS
Module 即可启用 `aarch64-linux` 与 `riscv64-linux` 两种架构的模拟构建系统:
```nix{6}
{ ... }:
@@ -140,31 +138,24 @@ Module示例如下
}
```
可以看到我们未添加任何额外的模块,仅仅是指定了 `system` 为 `riscv64-linux`. Nix 在构建时会
自动检测当前系统是否为 `riscv64-linux`,如果不是,它会自动通过 QEMU 模拟系统进行构建,对用
户而言这些底层操作完全是透明的。
可以看到我们未添加任何额外的模块,仅仅是指定了 `system` 为 `riscv64-linux`.
Nix 在构建时会自动检测当前系统是否为
`riscv64-linux`,如果不是,它会自动通过 QEMU 模拟系统进行构建,对用户而言这些底层操作完全是透明的。
## Linux binfmt_misc
前面只说了怎么用,如果你想了解更底层的细节,这里也简单介绍一下。
binfmt_misc 是 Linux 内核的一项功能全称是混杂二进制格式的内核支持Kernel Support for
miscellaneous Binary Formats它能够使 Linux 支持运行几乎任何 CPU 架构的程序,包括
X86_64、ARM64、RISCV64 等。
miscellaneous Binary
Formats它能够使 Linux 支持运行几乎任何 CPU 架构的程序,包括 X86_64、ARM64、RISCV64 等。
为了能够让 binfmt_misc 运行任意格式的程序,至少需要做到两点:特定格式二进制程序的识别方
式,以及其对应的解释器位置。虽然 binfmt_misc 听上去很强大,其实现的方式却意外地很容易理
解,类似于 bash 解释器通过脚本文件的第一行(如#!/usr/bin/python3得知该文件需要通过什么解
释器运行binfmt_misc 也预设了一系列的规则,如读取二进制文件头部特定位置的魔数,或者根据文
件扩展名(如.exe、.py以判断可执行文件的格式随后调用对应的解释器去运行该程序。Linux 默
认的可执行文件格式是 elf而 binfmt_misc 的出现拓宽了 Linux 的执行限制,将一点展开成一个
面,使得各种各样的二进制文件都能选择它们对应的解释器执行。
为了能够让 binfmt_misc 运行任意格式的程序,至少需要做到两点:特定格式二进制程序的识别方式,以及其对应的解释器位置。虽然 binfmt_misc 听上去很强大,其实现的方式却意外地很容易理解,类似于 bash 解释器通过脚本文件的第一行(如#!/usr/bin/python3得知该文件需要通过什么解释器运行binfmt_misc 也预设了一系列的规则,如读取二进制文件头部特定位置的魔数,或者根据文件扩展名(如.exe、.py以判断可执行文件的格式随后调用对应的解释器去运行该程序。Linux 默认的可执行文件格式是 elf而 binfmt_misc 的出现拓宽了 Linux 的执行限制,将一点展开成一个面,使得各种各样的二进制文件都能选择它们对应的解释器执行。
注册一种格式的二进制程序需要将一行有 `:name:type:offset:magic:mask:interpreter:flags` 格式
的字符串写入 `/proc/sys/fs/binfmt_misc/register` 中,格式的详细解释这里就略过了。
注册一种格式的二进制程序需要将一行有 `:name:type:offset:magic:mask:interpreter:flags`
格式的字符串写入 `/proc/sys/fs/binfmt_misc/register` 中,格式的详细解释这里就略过了。
由于人工写入上述 binfmt_misc 的注册信息比较麻烦,社区提供了一个容器来帮助我们自动注册,这
个容器就是 binfmt运行一下该容器就能安装各种格式的 binfmt_misc 模拟器了,举个例子:
由于人工写入上述 binfmt_misc 的注册信息比较麻烦,社区提供了一个容器来帮助我们自动注册,这个容器就是 binfmt运行一下该容器就能安装各种格式的 binfmt_misc 模拟器了,举个例子:
```shell
# 注册所有架构
@@ -174,20 +165,16 @@ podman run --privileged --rm tonistiigi/binfmt:latest --install all
docker run --privileged --rm tonistiigi/binfmt --install arm64,riscv64,arm
```
binfmt_misc 模块自 Linux 2.6.12-rc2 版本中引入先后经历了几次功能上的略微改动。Linux 4.8
中新增“F”fix binary固定二进制标志位使 mount 命名空间变更和 chroot 后的环境中依然能
够正常调用解释器执行二进制程序。由于我们需要构建多架构容器必须使用“F”标志位才能
binfmt_misc 在容器中正常工作,因此内核版本需要在 4.8 以上才可以。
binfmt_misc 模块自 Linux 2.6.12-rc2 版本中引入先后经历了几次功能上的略微改动。Linux
4.8 中新增“F”fix
binary固定二进制标志位使 mount 命名空间变更和 chroot 后的环境中依然能够正常调用解释器执行二进制程序。由于我们需要构建多架构容器必须使用“F”标志位才能 binfmt_misc 在容器中正常工作,因此内核版本需要在 4.8 以上才可以。
总的来说比起一般情况显式调用解释器去执行非原生架构程序binfmt_misc 产生的一个重要意义在
于透明性。有了 binfmt_misc 后,用户在执行程序时不需要再关心要用什么解释器去执行,好像任何
架构的程序都能够直接执行一样而可配置的“F”标志位更是锦上添花使解释器程序在安装时立即就
被加载进内存,后续的环境改变也不会影响执行过程。
总的来说比起一般情况显式调用解释器去执行非原生架构程序binfmt_misc 产生的一个重要意义在于透明性。有了 binfmt_misc 后用户在执行程序时不需要再关心要用什么解释器去执行好像任何架构的程序都能够直接执行一样而可配置的“F”标志位更是锦上添花使解释器程序在安装时立即就被加载进内存后续的环境改变也不会影响执行过程。
## 自定义构建工具链
有时候我们会需要使用自定义的工具链进行构建,比如使用自己编译的 gcc或者使用自己编译的
musl libc 等等,这种修改可以通过 overlays 来实现。
有时候我们会需要使用自定义的工具链进行构建,比如使用自己编译的 gcc或者使用自己编译的 musl
libc 等等,这种修改可以通过 overlays 来实现。
举个例子,我们来尝试下使用使用不同的 gcc 版本,通过 `nix repl` 来测试下:
@@ -241,11 +228,11 @@ nix-repl> pkgs.pkgsCross.riscv64.stdenv.cc
}
```
上面的方法会替换掉全局的 `pkgs.gcc`,很可能会导致大量的缓存失效,从而需要在本地本地构建非
常多的 Nix 包。
上面的方法会替换掉全局的
`pkgs.gcc`,很可能会导致大量的缓存失效,从而需要在本地本地构建非常多的 Nix 包。
为了避免这个问题,更好的办法是创建一个新的 `pkgs` 实例,仅在构建我们想修改的包时才使用这个
实例,`flake.nix` 示例如下:
为了避免这个问题,更好的办法是创建一个新的 `pkgs`
实例,仅在构建我们想修改的包时才使用这个实例,`flake.nix` 示例如下:
```nix{10-19,34-37}
{

View File

@@ -1,7 +1,7 @@
# Dev Environments
在 NixOS 上,我们有许多种安装开发环境的途径,最理想的方式当然是每个项目的开发环境都完全通
过它自己的 `flake.nix` 定义,但实际使用上这样做有些繁琐,每次都得弄个 `flake.nix` 出来再
在 NixOS 上,我们有许多种安装开发环境的途径,最理想的方式当然是每个项目的开发环境都完全通过它自己的
`flake.nix` 定义,但实际使用上这样做有些繁琐,每次都得弄个 `flake.nix` 出来再
`nix develop` 一下,对于一些临时项目或者只是想简单看看代码的情况,这样做显然有些大材小用。
一个折衷的方案是将开发环境分为三个层次:
@@ -10,45 +10,40 @@
- 通用的开发工具:`git``vim``emacs``tmux` 等等。
- 常见语言的 SDK 与包管理器:`rust``openjdk``python``go` 等等。
1. **IDE 环境**
- 以 neovim 为例home-manager 为 neovim 做了一个 wrapper 用于将 neovim 自身的依赖封装
到它本身的环境中,避免污染全局环境。
- 可通过 `programs.neovim.extraPackages` 参数将 neovim 的插件依赖加入到 neovim 的环境
中,保证 IDE 本身能正常运行。
- 以 neovim 为例home-manager 为 neovim 做了一个 wrapper 用于将 neovim 自身的依赖封装到它本身的环境中,避免污染全局环境。
- 可通过 `programs.neovim.extraPackages`
参数将 neovim 的插件依赖加入到 neovim 的环境中,保证 IDE 本身能正常运行。
- 但如果你有多个 IDE如 emacs 跟 neovim它们常常会依赖许多相同的程序譬如 lsp,
tree-sitter, debugger, formatter 等),为了方便管理,可以将这些共享的依赖放到全局。但
要注意可能会跟全局环境中的其他程序产生依赖冲突(尤其是 python 包,比较容易冲突)。
tree-sitter, debugger,
formatter 等),为了方便管理,可以将这些共享的依赖放到全局。但要注意可能会跟全局环境中的其他程序产生依赖冲突(尤其是 python 包,比较容易冲突)。
1. **项目环境**:每个项目都可以通过 `flake.nix` 定义自己的开发环境(`devShells`)。
- 为了简便,可以提前为常用语言创建一些通用的 `flake.nix` 模板,在需要的时候复制模板改一
改就能用。
- 项目环境的优先级是最高的(会被加到 PATH 最前面),其中的依赖会覆盖掉全局环境中同名的
依赖程序。所以你可以通过项目的 `flake.nix` 来控制项目的依赖版本,不受全局环境的影响。
- 为了简便,可以提前为常用语言创建一些通用的 `flake.nix`
模板,在需要的时候复制模板改一改就能用。
- 项目环境的优先级是最高的(会被加到 PATH 最前面),其中的依赖会覆盖掉全局环境中同名的依赖程序。所以你可以通过项目的
`flake.nix` 来控制项目的依赖版本,不受全局环境的影响。
## 开发环境的配置模板
前面我们已经学习了构建开发环境的实现原理,但是每次都要自己写一堆重复性较高的 `flake.nix`
略显繁琐。
前面我们已经学习了构建开发环境的实现原理,但是每次都要自己写一堆重复性较高的
`flake.nix`略显繁琐。
幸运的是,社区已经有人为我们做好了这件事,如下这个仓库中包含了绝大多数编程语言的开发环境模
板,直接复制粘贴下来就能用:
幸运的是,社区已经有人为我们做好了这件事,如下这个仓库中包含了绝大多数编程语言的开发环境模板,直接复制粘贴下来就能用:
- [MordragT/nix-templates](https://github.com/MordragT/nix-templates)
- [the-nix-way/dev-templates](https://github.com/the-nix-way/dev-templates)
如果你觉得 `flake.nix` 的结构还是太复杂了,希望能有更简单的方法,也可以考虑使用下面这个项
目,它对 Nix 做了更彻底的封装,对用户提供了更简单的定义:
如果你觉得 `flake.nix`
的结构还是太复杂了,希望能有更简单的方法,也可以考虑使用下面这个项目,它对 Nix 做了更彻底的封装,对用户提供了更简单的定义:
- [cachix/devenv](https://github.com/cachix/devenv)
如果你连任何一行 nix 代码都不想写,只想以最小的代价获得一个可复现的开发环境,这里也有一个
或许能符合你需求的工具:
如果你连任何一行 nix 代码都不想写,只想以最小的代价获得一个可复现的开发环境,这里也有一个或许能符合你需求的工具:
- [jetpack-io/devbox](https://github.com/jetpack-io/devbox)
## Python 开发环境
Python 的开发环境比 Java/Go 等语言要麻烦许多,因为它默认就往全局环境装软件,要往当前项目
还必须得先创建虚拟环境JS/Go 等语言里可没虚拟环境这种幺蛾子)。这对 Nix 而言是非常不
友好的行为。
Python 的开发环境比 Java/Go 等语言要麻烦许多,因为它默认就往全局环境装软件,要往当前项目还必须得先创建虚拟环境JS/Go 等语言里可没虚拟环境这种幺蛾子)。这对 Nix 而言是非常不友好的行为。
Python 的 pip 默认会将软件安装到全局,在 NixOS 中 `pip install` 会直接报错:
@@ -67,14 +62,12 @@ note: If you believe this is a mistake, please contact your Python installation
hint: See PEP 668 for the detailed specification.
```
根据错误信息,`pip install` 直接被 NixOS 禁用掉了,测试了 `pip install --user` 也同样被禁
用。为了提升环境的可复现能力Nix 把它们全部废掉了。即使我们通过 `mkShell` 等方式创建一个
新环境,这些命令照样会报错(猜测是 Nixpkgs 中的 pip 命令本身就被魔改了,只要是跑 `install`
等修改指令就直接嘎掉)。
根据错误信息,`pip install` 直接被 NixOS 禁用掉了,测试了 `pip install --user`
也同样被禁用。为了提升环境的可复现能力Nix 把它们全部废掉了。即使我们通过 `mkShell`
等方式创建一个新环境,这些命令照样会报错(猜测是 Nixpkgs 中的 pip 命令本身就被魔改了,只要是跑
`install` 等修改指令就直接嘎掉)。
但是很多项目的安装脚本都是基于 pip 的,这导致这些脚本都不能直接使用,而且另一方面 nixpkgs
中的内容有限,很多 pypi 中的包里边都没有,还得自己打包,相对麻烦很多,也加重了用户的心智负
担。
但是很多项目的安装脚本都是基于 pip 的,这导致这些脚本都不能直接使用,而且另一方面 nixpkgs 中的内容有限,很多 pypi 中的包里边都没有,还得自己打包,相对麻烦很多,也加重了用户的心智负担。
解决方案之一是改用 `venv` 虚拟环境,在虚拟环境里当然就能正常使用 pip 等命令了:
@@ -85,22 +78,21 @@ source ./env/bin/activate
或者使用第三方工具 `virtualenv`,缺点是这个需要额外安装。
这样用 python 直接创建的 venv对一些人而言可能还是没有安全感仍然希望将这个虚拟环境也弄
`/nix/store` 里使其不可变,通过 nix 直接安装 `requirements.txt` 或者 `poetry.toml` 中的
依赖项。这当然是可行的,有现成的 Nix 封装工具帮我们干这个活:
这样用 python 直接创建的 venv对一些人而言可能还是没有安全感仍然希望将这个虚拟环境也弄
`/nix/store` 里使其不可变,通过 nix 直接安装 `requirements.txt` 或者 `poetry.toml`
中的依赖项。这当然是可行的,有现成的 Nix 封装工具帮我们干这个活:
> 注意即使是在这俩环境中,直接跑 `pip install` 之类的安装命令仍然是会失败的,必须通过
> `flake.nix` 来安装 Python 依赖!因为数据还是在 `/nix/store` 中,这类修改命令必须在 Nix的
> 构建阶段才能执行...
> `flake.nix` 来安装 Python 依赖!因为数据还是在 `/nix/store`
> 中,这类修改命令必须在 Nix的构建阶段才能执行...
- [python venv demo](https://github.com/MordragT/nix-templates/blob/master/python-venv/flake.nix)
- [poetry2nix](https://github.com/nix-community/poetry2nix)
这俩工具的好处是,能利用上 Nix Flakes 的锁机制来提升可复现能力,缺点是多了一层封装,底层变
得更复杂了。
这俩工具的好处是,能利用上 Nix
Flakes 的锁机制来提升可复现能力,缺点是多了一层封装,底层变得更复杂了。
最后,在一些更复杂的项目上,上述两种方案可能都行不通,这时候最佳的解决方案,就是改用容器
了,比如 Docker、Podman 等,容器的限制没 Nix 这么严格,能提供最佳的兼容性。
最后,在一些更复杂的项目上,上述两种方案可能都行不通,这时候最佳的解决方案,就是改用容器了,比如 Docker、Podman 等,容器的限制没 Nix 这么严格,能提供最佳的兼容性。
## Go 开发环境

View File

@@ -2,21 +2,18 @@
分布式构建可以通过多台机器来分担本地的编译压力,加快构建速度。
NixOS 官方的 cache.nixos.org 中提供了绝大多数 X86_64 架构的缓存,因此对于普通 X86_64 的用
户,一般不需要分布式构建。
NixOS 官方的 cache.nixos.org 中提供了绝大多数 X86_64 架构的缓存,因此对于普通 X86_64 的用户,一般不需要分布式构建。
分布式构建只在没有缓存可用的场景下才有较大应用价值,主要有这几种应用场景:
1. RISC-V 或 ARM64 架构的用户(尤其是 RISC-V因为官方缓存仓库中这两个架构的缓存很少
致经常需要大量本地编译。
2. 对系统进行大量定制的用户,因为官方缓存仓库中的 packages 都是默认配置,如果你改了构建参
数,那么官方缓存就不适用了,这时候就需要本地编译。
1. RISC-V 或 ARM64 架构的用户(尤其是 RISC-V因为官方缓存仓库中这两个架构的缓存很少致经常需要大量本地编译。
2. 对系统进行大量定制的用户,因为官方缓存仓库中的 packages 都是默认配置,如果你改了构建参数,那么官方缓存就不适用了,这时候就需要本地编译。
1. 比如嵌入式场景下往往对底层内核、驱动等有定制需求,导致需要本地编译。
## 配置分布式构建
官方没有详细文档讲这个,我在文末列出了一些建议阅读的参考文档,同时如下是我的分布式构建配置
一个NixOS Module
官方没有详细文档讲这个,我在文末列出了一些建议阅读的参考文档,同时如下是我的分布式构建配置一个NixOS
Module
```nix
{ ... }: {
@@ -136,14 +133,10 @@ NixOS 官方的 cache.nixos.org 中提供了绝大多数 X86_64 架构的缓存
目前我观察到的问题有:
1. 无法在构建时指定使用哪些主机,只能在配置文件中指定一个主机列表,然后 nix 会自动选择可用
的主机
2. 在选择主机时,我发现 Nix 总是优先选择远程主机,而我本地主机的性能最强,这导致本地主机的
CPU 无法充分利用
3. 多机远程构建是以 Derivation 为单位的,因此在构建一些比较大的包时,其他机器可能会空闲很
久,一直等这个大包构建完毕,这导致了资源的浪费。
1. 在构建的 packages 较多并且可以并行执行时,可以轻松将所有主机的 CPU 都用上,这确实非
常爽。
1. 无法在构建时指定使用哪些主机,只能在配置文件中指定一个主机列表,然后 nix 会自动选择可用的主机。
2. 在选择主机时,我发现 Nix 总是优先选择远程主机,而我本地主机的性能最强,这导致本地主机的 CPU 无法充分利用
3. 多机远程构建是以 Derivation 为单位的,因此在构建一些比较大的包时,其他机器可能会空闲很久,一直等这个大包构建完毕,这导致了资源的浪费。
1. 在构建的 packages 较多并且可以并行执行时,可以轻松将所有主机的 CPU 都用上,这确实非常爽
## References

View File

@@ -1,16 +1,14 @@
# 在 NixOS 上进行开发工作
由于 NixOS 自身可复现的特性,它非常适合用于搭建开发环境。但是如果你想直接将在其他发行版上
的环境搭建经验用在 NixOS 上,可能会遇到许多问题,因为 NixOS 有自己的一套逻辑在,下面我们先
对此稍作说明。
由于 NixOS 自身可复现的特性,它非常适合用于搭建开发环境。但是如果你想直接将在其他发行版上的环境搭建经验用在 NixOS 上,可能会遇到许多问题,因为 NixOS 有自己的一套逻辑在,下面我们先对此稍作说明。
在本章中我们先学习一下 Nix Flakes 开发环境的实现原理,后面的章节再按使用场景介绍一些更具体
的内容。
在本章中我们先学习一下 Nix
Flakes 开发环境的实现原理,后面的章节再按使用场景介绍一些更具体的内容。
## 通过 `nix shell` 创建开发环境
在 NixOS 上,最简单的创建开发环境的方法是使用 `nix shell`,它会创建一个含有指定 Nix 包的
shell 环境。
在 NixOS 上,最简单的创建开发环境的方法是使用
`nix shell`,它会创建一个含有指定 Nix 包的 shell 环境。
示例:
@@ -51,8 +49,7 @@ Hello, world!
为了更好的使用上述两个功能,我们先来看看它们的原理。
[`pkgs.mkShell` 的源码](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/pkgs/build-support/mkshell/default.nix)如
下:
[`pkgs.mkShell` 的源码](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/pkgs/build-support/mkshell/default.nix)如下:
```nix
{ lib, stdenv, buildEnv }:
@@ -147,8 +144,8 @@ stdenv.mkDerivation ({
```
建个空文件夹,将上面的配置保存为 `flake.nix`,然后执行 `nix develop`(或者更精确点,可以用
`nix develop .#default`),首先会打印出当前 nodejs 的版本,之后 `node` `pnpm` `yarn` 等命
令就都能正常使用了。
`nix develop .#default`),首先会打印出当前 nodejs 的版本,之后 `node` `pnpm` `yarn`
等命令就都能正常使用了。
## 在开发环境中使用 zsh/fish 等其他 shell
@@ -199,11 +196,12 @@ stdenv.mkDerivation ({
`pkgs.mkShell` 创建的 derivation 不能直接使用,必须通过 `nix develop` 进入到该环境中。
实际上我们也可以通过 `pkgs.stdenv.mkDerivation` 来创建一个包含所需软件包的 shell wrapper,
这样就能直接通过执行运行该 wrapper 来进入到该环境中。
实际上我们也可以通过 `pkgs.stdenv.mkDerivation` 来创建一个包含所需软件包的 shell
wrapper, 这样就能直接通过执行运行该 wrapper 来进入到该环境中。
直接使用 `mkDerivation` 略显繁琐Nixpkgs 提供了一些更简单的函数来帮助我们创建这类
wrapper比如 `pkgs.runCommand`.
直接使用 `mkDerivation`
略显繁琐Nixpkgs 提供了一些更简单的函数来帮助我们创建这类 wrapper比如
`pkgs.runCommand`.
示例:
@@ -246,8 +244,9 @@ wrapper比如 `pkgs.runCommand`.
然后执行 `nix run .#dev` 或者 `nix shell .#dev --command 'dev-shell'`就能进入一个nushell
session可以在其中正常使用 `node` `pnpm` 命令.
这种方式生成的 wrapper 是一个可执行文件,它实际不依赖 `nix run` 命令,比如说我们可以直接通
过 NixOS 的 `environment.systemPackages` 来安装这个 wrapper然后直接执行它
这种方式生成的 wrapper 是一个可执行文件,它实际不依赖 `nix run`
命令,比如说我们可以直接通过 NixOS 的 `environment.systemPackages`
来安装这个 wrapper然后直接执行它
```nix
{pkgs, lib, ...}:{
@@ -274,9 +273,9 @@ session可以在其中正常使用 `node` `pnpm` 命令.
}
```
将上述配置添加到任一 NixOS Module 中,再通过 `sudo nixos-rebuild switch` 部署后,就能直接
通过 `dev-shell` 命令进入到该开发环境,这就是 `pkgs.runCommand` 相比 `pkgs.mkShell` 的特别
之处。
将上述配置添加到任一 NixOS Module 中,再通过 `sudo nixos-rebuild switch`
部署后,就能直接通过 `dev-shell` 命令进入到该开发环境,这就是 `pkgs.runCommand` 相比
`pkgs.mkShell` 的特别之处。
相关源代码:
@@ -296,8 +295,9 @@ Synopsis
# ......
```
可以看到 `nix develop` 接受的参数是 `installable`,这说明我们可以通过它进入任何一个
installable 的 Nix 包的开发环境,而不仅仅是 `pkgs.mkShell` 创建的环境。
可以看到 `nix develop` 接受的参数是
`installable`,这说明我们可以通过它进入任何一个 installable 的 Nix 包的开发环境,而不仅仅是
`pkgs.mkShell` 创建的环境。
默认情况下,`nix develop` 命令会尝试 flake outputs 中的如下属性:
@@ -353,8 +353,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
此外我们还可以正常调用 `hello` 这个 Nix 包的各构建阶段命令:
> 提前说明下,一个 Nix 包的所有构建阶段及其默认的执行顺序
> 为:`$prePhases unpackPhase patchPhase $preConfigurePhases configurePhase $preBuildPhases buildPhase checkPhase $preInstallPhases installPhase fixupPhase installCheckPhase $preDistPhases distPhase $postPhases`
> 提前说明下,一个 Nix 包的所有构建阶段及其默认的执行顺序为:`$prePhases unpackPhase patchPhase $preConfigurePhases configurePhase $preBuildPhases buildPhase checkPhase $preInstallPhases installPhase fixupPhase installCheckPhase $preDistPhases distPhase $postPhases`
```shell
# 解压源码包
@@ -412,13 +411,12 @@ ryan in 🌐 aquamarine in /tmp/xxx/hello-2.12.1 via C v12.3.0-gcc via ❄️ i
Hello, world!
```
这种用法的主要应用场景是调试某个 Nix 包的构建过程,或者在某个 Nix 包的构建环境中执行一些命
令。
这种用法的主要应用场景是调试某个 Nix 包的构建过程,或者在某个 Nix 包的构建环境中执行一些命令。
## `nix build`
`nix build` 用于构建一个软件包,并在当前目录下创建一个名为 `result` 的符号链接,链接到该构
建结果。
`nix build` 用于构建一个软件包,并在当前目录下创建一个名为 `result`
的符号链接,链接到该构建结果。
一个示例:
@@ -455,19 +453,19 @@ nix build "nixpkgs#ponysay"
## 使用 `nix profile` 分别管理日常娱乐环境跟开发环境
`nix profile` 是 NixOS 中用于管理用户环境的工具,它可以用于创建管理多个用户环境,并在需要
时切换到不同的环境。
`nix profile`
是 NixOS 中用于管理用户环境的工具,它可以用于创建管理多个用户环境,并在需要时切换到不同的环境。
`nix develop` 不同,`nix profile` 管理的是用户级别的系统环境,而不是临时创建的一个
shell 环境,因此它对 Jetbrains IDE / VSCode 等 IDE 的兼容性会好很多,不会出现无法在 IDE 内
使用我们配置好的开发环境的情况。
`nix develop` 不同,`nix profile`
管理的是用户级别的系统环境,而不是临时创建的一个 shell 环境,因此它对 Jetbrains IDE /
VSCode 等 IDE 的兼容性会好很多,不会出现无法在 IDE 内使用我们配置好的开发环境的情况。
TODO 未完待续
## 其他命令
其他还有些 `nix flake init` 之类的命令,请自行查阅 [New Nix Commands][New Nix Commands]
习研究,这里就不详细介绍了。
其他还有些 `nix flake init` 之类的命令,请自行查阅 [New Nix Commands][New Nix Commands]
习研究,这里就不详细介绍了。
## References

View File

@@ -37,8 +37,8 @@ TODO
## Fetchers {#fetchers}
构建输入除了直接来自文件系统路径之外,还可以通过 Fetchers 来获取Fetcher 是一种特殊的函
数,它的输入是一个 attribute set输出是 Nix Store 中的一个系统路径。
构建输入除了直接来自文件系统路径之外,还可以通过 Fetchers 来获取Fetcher 是一种特殊的函数,它的输入是一个 attribute
set输出是 Nix Store 中的一个系统路径。
Nix 提供了四个内置的 Fetcher分别是
@@ -59,26 +59,24 @@ builtins.fetchTarball "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc6343
## Derivations {#derivations}
> 官方 Nixpkgs 包仓库中的软件包已经能满足绝大部分用户的使用,在学习 NixOS 的前期不太需要深
> 入了解 Derivation 的使用细节,有个印象就行。本书会在后面
> [Nix 软件打包入门](../development/packaging-101.md) 中详细介绍相关内容,这里仅做简要介
> 绍。
> 官方 Nixpkgs 包仓库中的软件包已经能满足绝大部分用户的使用,在学习 NixOS 的前期不太需要深入了解 Derivation 的使用细节,有个印象就行。本书会在后面
> [Nix 软件打包入门](../development/packaging-101.md)
> 中详细介绍相关内容,这里仅做简要介绍。
Derivation 描述了如何构建一个软件包,是一个软件包构建流程的 Nix 语言描述,它声明了构建时需
要有哪些依赖项、需要什么构建工具链、要设置哪些环境变量、哪些构建参数、先干啥后干啥等等。
Derivation 描述了如何构建一个软件包,是一个软件包构建流程的 Nix 语言描述,它声明了构建时需要有哪些依赖项、需要什么构建工具链、要设置哪些环境变量、哪些构建参数、先干啥后干啥等等。
Derivation 的构建结果是一个 Store Object其中包含了软件包的所有二进制程序、配置文件等等内
容。Store Object 的存放路径格式为 `/nix/store/<hash>-<name>`,其中 `<hash>` 是构建结果的
hash 值,`<name>` 是它的名字。路径 hash 值确保了每个构建结果都是唯一的,因此可以多版本共
存,而且不会出现依赖冲突的问题。
Derivation 的构建结果是一个 Store
Object其中包含了软件包的所有二进制程序、配置文件等等内容。Store Object 的存放路径格式为
`/nix/store/<hash>-<name>`,其中 `<hash>` 是构建结果的 hash 值,`<name>`
是它的名字。路径 hash 值确保了每个构建结果都是唯一的,因此可以多版本共存,而且不会出现依赖冲突的问题。
`/nix/store` 是一个特殊的文件路径,它被称为 Store存放所有的 Store Objects这个路径被设
置为只读,只有 Nix 本身才能修改这个路径下的内容,以保证系统的可复现性。
`/nix/store` 是一个特殊的文件路径,它被称为 Store存放所有的 Store
Objects这个路径被设置为只读,只有 Nix 本身才能修改这个路径下的内容,以保证系统的可复现性。
Derivation 实质上只是一个 attribute setNix 底层会使用内置函数 `builtins.derivation` 将这
个 attribute set 构建为一个 Store Object。我们实际编写 Derivation 时,通常使用的是
`stdenv.mkDerivation`,它是前述内置函数 `builtins.derivation` 的 Nix 语言 wrapper屏蔽了
底层的细节,简化了用法。
Derivation 实质上只是一个 attribute setNix 底层会使用内置函数 `builtins.derivation`
将这个 attribute set 构建为一个 Store Object。我们实际编写 Derivation 时,通常使用的是
`stdenv.mkDerivation`,它是前述内置函数 `builtins.derivation`
的 Nix 语言 wrapper屏蔽了底层的细节,简化了用法。
一个简单的 Derivation 如下,它声明了一个名为 hello 的应用程序(摘抄自
[nixpkgs/pkgs/hello](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/pkgs/applications/misc/hello/default.nix)

View File

@@ -2,25 +2,16 @@
## NixOS 的回滚能力与 btrfs / zfs 系统快照回滚有何不同?
很多使用 Arch Linux / Ubuntu 等常规 Linux 发行版的用户,习惯于使用 btrfs / zfs 等文件系统
提供的快照作为「后悔药」,这样系统出了问题能回滚修复。而本书介绍的 NixOS 也提供了系统状态
回滚能力,因此很容易会有这样的疑问:这两种系统回滚功能有何不同?
很多使用 Arch Linux / Ubuntu 等常规 Linux 发行版的用户,习惯于使用 btrfs /
zfs 等文件系统提供的快照作为「后悔药」,这样系统出了问题能回滚修复。而本书介绍的 NixOS 也提供了系统状态回滚能力,因此很容易会有这样的疑问:这两种系统回滚功能有何不同?
这里简单解释下,主要区别在于,快照内容不包含如何从零构建这个快照的「知识」,是**不可解释
的**,而且其内容与当前硬件环境强相关,很难在其他机器上复现。
这里简单解释下,主要区别在于,快照内容不包含如何从零构建这个快照的「知识」,是**不可解释的**,而且其内容与当前硬件环境强相关,很难在其他机器上复现。
而 NixOS 的配置是一份从零构建出一个一模一样的 OS 的「知识」,是**可解释的**,而且可以通过
简单几行命令就自动完成这个构建。NixOS 配置本身既是一份记录你的 NixOS 系统都做过哪些变更的
文档,也可直接用于自动构建出你当前的 NixOS 系统。
而 NixOS 的配置是一份从零构建出一个一模一样的 OS 的「知识」,是**可解释的**,而且可以通过简单几行命令就自动完成这个构建。NixOS 配置本身既是一份记录你的 NixOS 系统都做过哪些变更的文档,也可直接用于自动构建出你当前的 NixOS 系统。
NixOS 的配置文件就像是程序的**源代码**,只要源代码没丢,修改程序、审查程序,或者重新构建出
一个一模一样的程序都很简单。而系统快照就像是源代码编译出来的二进制程序,要对它做修改、审
查,都要难得多。而且这个快照很大,分享或者迁移它的成本都要比源代码高得多。
NixOS 的配置文件就像是程序的**源代码**,只要源代码没丢,修改程序、审查程序,或者重新构建出一个一模一样的程序都很简单。而系统快照就像是源代码编译出来的二进制程序,要对它做修改、审查,都要难得多。而且这个快照很大,分享或者迁移它的成本都要比源代码高得多。
但这并不是说有了 NixOS 就不需要系统快照了,本书第一章就介绍了 NixOS 只能保证在声明式配置中
声明的所有内容都是可复现的,而其他未声明式配置覆盖到的系统状态是不受它管辖的。比如
MySQL/PostgreSQL 的动态数据、用户上传的文件、系统日志等等,用户 Home 目录下的视频、音乐、
图片等等,这些内容都还是需要文件系统快照或者其他手段来备份。
但这并不是说有了 NixOS 就不需要系统快照了,本书第一章就介绍了 NixOS 只能保证在声明式配置中声明的所有内容都是可复现的,而其他未声明式配置覆盖到的系统状态是不受它管辖的。比如 MySQL/PostgreSQL 的动态数据、用户上传的文件、系统日志等等,用户 Home 目录下的视频、音乐、图片等等,这些内容都还是需要文件系统快照或者其他手段来备份。
## Nix 与 Ansible 等传统的系统运维工具相比有何优劣?
@@ -30,48 +21,32 @@ Nix 不仅可用于管理桌面电脑的环境,也有很多人用它批量管
Nix 与 Ansible 这类被广泛应用的传统工具比,主要优势就在:
1. Ansible 这类工具一个最大的问题就是,它每次部署都是基于系统当前状态的增量修改。而系统的
当前状态就如同前面提到的系统快照,是不可解释的,也很难复现。而 NixOS 是通过配置文件声明
系统的目标状态,可以做到部署结果与系统当前状态无关,重复部署也不会导致任何问题
2. Nix Flakes 通过一个版本锁文件 `flake.lock` 锁定了所有依赖的 hash 值、版本号、数据源等信
息,这极大地提升了系统的可复现能力。而传统的 Ansible 等工具没有此功能,所以它们的可复现
能力很差
1. 这也是为什么 Docker 这么受欢迎的原因——它以较低的代价提供了 Ansible 等传统运维工具提
供不了的**可在各种机器上复现的系统环境**。
3. Nix 通过一层声明式的抽象屏蔽了其底层的实现细节,使用户只需要关心自己最核心的需求,从而
带来了高度便捷的系统自定义能力。而 Ansible 这类工具的抽象能力要弱得多。
1. 如果你有使用过 terraform/kubernetes 等声明式配置工具,应该很容易理解这一点。需求越是
复杂的情况下,声明式配置带来的好处就越大。
1. Ansible 这类工具一个最大的问题就是,它每次部署都是基于系统当前状态的增量修改。而系统的当前状态就如同前面提到的系统快照,是不可解释的,也很难复现。而 NixOS 是通过配置文件声明系统的目标状态,可以做到部署结果与系统当前状态无关,重复部署也不会导致任何问题。
2. Nix Flakes 通过一个版本锁文件 `flake.lock`
锁定了所有依赖的 hash 值、版本号、数据源等信息,这极大地提升了系统的可复现能力。而传统的 Ansible 等工具没有此功能,所以它们的可复现能力很差
1. 这也是为什么 Docker 这么受欢迎的原因——它以较低的代价提供了 Ansible 等传统运维工具提供不了的**可在各种机器上复现的系统环境**。
3. Nix 通过一层声明式的抽象屏蔽了其底层的实现细节,使用户只需要关心自己最核心的需求,从而带来了高度便捷的系统自定义能力。而 Ansible 这类工具的抽象能力要弱得多。
1. 如果你有使用过 terraform/kubernetes 等声明式配置工具,应该很容易理解这一点。需求越是复杂的情况下,声明式配置带来的好处就越大
## Nix 与 Docker 容器技术相比有何优势?
Nix 与以 Docker 为代表的容器技术的应用场景也存在一定重合,比如说:
1. 有很多人用 Nix 来管理开发编译环境,本书就对此做过介绍。但另一方面也有像
[Dev Containers](https://github.com/devcontainers/spec) 这种基于容器搭建开发环境的技
术,而且非常流行。
2. 目前整个 DevOps/SRE 领域基本已经是基于 Dockerfile 的容器技术的天下,容器中常用
Ubuntu/Debian 等发行版,宿主机也同样有很多成熟的发行版可选,改用 NixOS 有什么明显的优势
呢?
[Dev Containers](https://github.com/devcontainers/spec)
这种基于容器搭建开发环境的技术,而且非常流行。
2. 目前整个 DevOps/SRE 领域基本已经是基于 Dockerfile 的容器技术的天下,容器中常用 Ubuntu/Debian 等发行版,宿主机也同样有很多成熟的发行版可选,改用 NixOS 有什么明显的优势呢?
其中第一点「管理开发编译环境」Nix 创建的开发环境体验非常接近直接在宿主机进行开发,这要比
Dev Containers 好很多,举例如下:
其中第一点「管理开发编译环境」Nix 创建的开发环境体验非常接近直接在宿主机进行开发,这要比 Dev
Containers 好很多,举例如下:
1. Nix 不使用名字空间进行文件系统、网络环境等各方面的隔离,在 Nix 创建的开发环境中也可以很
方便地与宿主机文件系统(包括 /dev 宿主机外接设备)、网络环境等等进行交互。而容器要通过
各种映射才能宿主机文件系统互通,即使这样也可能会遇到一些文件权限问题。
2. 因为没做啥强隔离Nix 开发环境对 GUI 程序的支持也没任何问题,在这个环境中跑个 GUI 程序
的体验就跟在系统环境中跑个 GUI 程序没任何区别。
1. Nix 不使用名字空间进行文件系统、网络环境等各方面的隔离,在 Nix 创建的开发环境中也可以很方便地与宿主机文件系统(包括 /dev 宿主机外接设备)、网络环境等等进行交互。而容器要通过各种映射才能宿主机文件系统互通,即使这样也可能会遇到一些文件权限问题。
2. 因为没做啥强隔离Nix 开发环境对 GUI 程序的支持也没任何问题,在这个环境中跑个 GUI 程序的体验就跟在系统环境中跑个 GUI 程序没任何区别。
也就是说Nix 能提供最接近宿主机的开发体验,不存在什么强隔离,开发人员能在这个环境中使用各
种熟悉的开发调试工具,过往的开发经验基本都能无痛迁移过来。而如果使用 Dev Containers那开
发人员很可能会遭遇强隔离导致的各种文件系统不互通、网络环境问题、用户权限问题、无法使用 GUI
调试工具等等各种毛病。
也就是说Nix 能提供最接近宿主机的开发体验,不存在什么强隔离,开发人员能在这个环境中使用各种熟悉的开发调试工具,过往的开发经验基本都能无痛迁移过来。而如果使用 Dev
Containers那开发人员很可能会遭遇强隔离导致的各种文件系统不互通、网络环境问题、用户权限问题、无法使用 GUI 调试工具等等各种毛病。
而如果我们决定了使用 Nix 来管理所有的开发环境,那么在构建 Docker 容器时也基于 Nix 去构建,
显然能提供最强的一致性,同时所有环境都统一了技术架构,这也能明显降低整个基础设施的维护成
本。这就回答了前面提到的第二点,在使用 Nix 管理开发环境的前提下,容器基础镜像与云服务器都
使用 NixOS 会存在明显的优势。
而如果我们决定了使用 Nix 来管理所有的开发环境,那么在构建 Docker 容器时也基于 Nix 去构建,显然能提供最强的一致性,同时所有环境都统一了技术架构,这也能明显降低整个基础设施的维护成本。这就回答了前面提到的第二点,在使用 Nix 管理开发环境的前提下,容器基础镜像与云服务器都使用 NixOS 会存在明显的优势。
## error: collision between `...` and `...`

View File

@@ -3,26 +3,18 @@
## 优点 {#advantages}
- **声明式配置OS as Code**
- NixOS 使用声明式配置来管理整个系统环境,可以直接用 Git 管理这些配置,只要你的配置文件
不丢,系统就可以随时还原到任一历史状态(前面提过了,只有在 Nix 配置文件中声明了的状态
才可被 NixOS 还原)。
- Nix Flakes 还使用了 `flake.lock` 作为版本锁文件,它记录了所有相关数据的数据源地
址、hash 值,这极大地提升了 Nix 的可复现能力(或者说增强了构建结果的一致性,这个设计实
际是从 cargo/npm 等一些编程语言的包管理设计中借鉴来的)。
- NixOS 使用声明式配置来管理整个系统环境,可以直接用 Git 管理这些配置,只要你的配置文件不丢,系统就可以随时还原到任一历史状态(前面提过了,只有在 Nix 配置文件中声明了的状态才可被 NixOS 还原)。
- Nix Flakes 还使用了 `flake.lock`
作为版本锁文件它记录了所有相关数据的数据源地址、hash 值,这极大地提升了 Nix 的可复现能力(或者说增强了构建结果的一致性,这个设计实际是从 cargo/npm 等一些编程语言的包管理设计中借鉴来的)。
- **高度便捷的系统自定义能力**
- 通过改几行配置,就可以简单地更换系统的各种组件。这是因为 Nix 将底层的复杂操作全部封装
在了 Nix 软件包中,只给用户提供了简洁且必要的声明式参数
- 而且这种修改非常安全,你可以很方便地来回切换各种桌面环境(比如 gnome/kde/i3/sway
乎不会遇到坑。
- 通过改几行配置,就可以简单地更换系统的各种组件。这是因为 Nix 将底层的复杂操作全部封装在了 Nix 软件包中,只给用户提供了简洁且必要的声明式参数。
- 而且这种修改非常安全,你可以很方便地来回切换各种桌面环境(比如 gnome/kde/i3/sway几乎不会遇到坑
- **可回滚**
- 可以随时回滚到任一历史环境NixOS 甚至默认将所有旧版本都加入到了启动项,确保系统滚挂了
也能随时回退。所以 Nix 也被认为是最稳定的包管理方式。
- 可以随时回滚到任一历史环境NixOS 甚至默认将所有旧版本都加入到了启动项,确保系统滚挂了也能随时回退。所以 Nix 也被认为是最稳定的包管理方式。
- **没有依赖冲突问题**
- 因为 Nix 中每个软件包都拥有唯一的 hash其安装路径中也会包含这个 hash 值,因此可以多版
本共存。
- 因为 Nix 中每个软件包都拥有唯一的 hash其安装路径中也会包含这个 hash 值,因此可以多版本共存。
- **社区很活跃,第三方项目也挺丰富**
- 官方包仓库 nixpkgs 贡献者众多,也有很多人分享自己的 Nix 配置,一遍浏览下来,整个生态给
我一种发现新大陆的兴奋感。
- 官方包仓库 nixpkgs 贡献者众多,也有很多人分享自己的 Nix 配置,一遍浏览下来,整个生态给我一种发现新大陆的兴奋感。
<figure>
<img src="/nixos-bootloader.avif">
@@ -39,37 +31,28 @@
## 缺点 {#disadvantages}
- **学习成本高**:
- 如果你希望系统完全可复现,并且避免各种不当使用导致的坑,那就需要学习了解 Nix 的整个设
计,并以声明式的方式管理系统,不能无脑 `nix-env -i`(这类似 `apt-get install`)。
- 如果你希望系统完全可复现,并且避免各种不当使用导致的坑,那就需要学习了解 Nix 的整个设计,并以声明式的方式管理系统,不能无脑
`nix-env -i`(这类似 `apt-get install`)。
- **文档混乱**:
- 首先 Nix Flakes 目前仍然是实验性特性,介绍它本身的文档目前比较匮乏, Nix 社区绝大多数文
档都只介绍了旧的 `/etc/nixos/configuration.nix`,想直接从 Nix Flakes(`flake.nix`) 开始
学习的话,需要参考大量旧文档,从中提取出自己需要的内容。另外一些 Nix 当前的核心功能,
官方文档都语焉不详(比如 `imports` 跟 Nixpkgs Module System想搞明白基本只能看源码
了...
- 首先 Nix Flakes 目前仍然是实验性特性,介绍它本身的文档目前比较匮乏,
Nix 社区绝大多数文档都只介绍了旧的 `/etc/nixos/configuration.nix`,想直接从 Nix
Flakes(`flake.nix`) 开始学习的话,需要参考大量旧文档,从中提取出自己需要的内容。另外一些 Nix 当前的核心功能,官方文档都语焉不详(比如
`imports` 跟 Nixpkgs Module System想搞明白基本只能看源码了...
- **比较吃硬盘空间**:
- 为了保证系统可以随时回退nix 默认总是保留所有历史环境,这会使用更多的硬盘空间。
- 多使用的这这些空间,在桌面电脑上可能不是啥事,但是在资源受限的云服务器上可能会是个问
题。
- 多使用的这这些空间,在桌面电脑上可能不是啥事,但是在资源受限的云服务器上可能会是个问题。
- **报错信息比较隐晦**:
-
于[Nixpkgs 中模块系统](../other-usage-of-flakes/module-system.md)的[复杂的 Merging 算法](https://discourse.nixos.org/t/best-resources-for-learning-about-the-nixos-module-system/1177/4)
导致 NixOS 的报错信息相当糟糕。在很多情况下,不管你加不加 `--show-trace`,它都只会告诉
你代码有错误(最常见且最迷惑的报错信息是
[Infinite recursion encountered](https://discourse.nixos.org/t/infinite-recursion-encountered-by-making-module-configurable/23508/2)
但究竟是错在哪?类型系统说我也不知道,这得你自己慢慢找了。目前我的经验来说,**遇到这种
报错信息没任何意义的 bug最简单有效的解决方法是用「二分法」一点点还原代码**。
-于[Nixpkgs 中模块系统](../other-usage-of-flakes/module-system.md)的[复杂的 Merging 算法](https://discourse.nixos.org/t/best-resources-for-learning-about-the-nixos-module-system/1177/4),导致 NixOS 的报错信息相当糟糕。在很多情况下,不管你加不加
`--show-trace`,它都只会告诉你代码有错误(最常见且最迷惑的报错信息是
[Infinite recursion encountered](https://discourse.nixos.org/t/infinite-recursion-encountered-by-making-module-configurable/23508/2)),但究竟是错在哪?类型系统说我也不知道,这得你自己慢慢找了。目前我的经验来说,**遇到这种报错信息没任何意义的 bug最简单有效的解决方法是用「二分法」一点点还原代码**。
- 这个问题应该是 NixOS 目前最大的痛点之一。
- **底层实现更复杂**
- Nix 多做了一层声明式的抽象带来了诸多好处,而其代价就是底层的代码实现会比传统的命令式工
具中的同类代码更复杂
- 这导致实现难度更高,对底层做些修改也会更麻烦。不过这个问题带来的负担主要是在 Nix 软件
包的维护者身上,普通用户接触底层比较少,负担也就小很多。
- Nix 多做了一层声明式的抽象带来了诸多好处,而其代价就是底层的代码实现会比传统的命令式工具中的同类代码更复杂。
- 这导致实现难度更高,对底层做些修改也会更麻烦。不过这个问题带来的负担主要是在 Nix 软件包的维护者身上,普通用户接触底层比较少,负担也就小很多
## 简单总结下 {#summary}
总的来说,我觉得 NixOS 适合那些有一定 Linux 使用经验与编程经验,并且希望对自己的系统拥有更
强掌控力的开发者。
总的来说,我觉得 NixOS 适合那些有一定 Linux 使用经验与编程经验,并且希望对自己的系统拥有更强掌控力的开发者。
我不推荐没有任何 Linux 使用经验的新人直接上手 NixOS那可能会是一段糟糕的旅程。

View File

@@ -2,60 +2,39 @@
# Nix 与 NixOS 简介
Nix 是一个声明式的软件包管理器,用户需要通过某些配置声明好期望的环境状态,而 Nix 负责达成
这个目标。
Nix 是一个声明式的软件包管理器,用户需要通过某些配置声明好期望的环境状态,而 Nix 负责达成这个目标。
> 简单解释下什么是「声明式配置」,它是指用户只需要声明好自己想要的结果——比如说希望将 i3 桌
> 面替换成 sway 桌面Nix 就会帮用户达成这个目标。用户不需要关心底层细节(比如说 sway 需要
> 安装哪些软件包,哪些 i3 相关的软件包需要卸载掉,哪些系统配置或环境变量需要针对 sway 做调
> 整、如果使用了 Nvidia 显卡 Sway 参数要做什么调整才能正常运行等等Nix 会自动帮用户处理
> 这些细节(当然这有个前提,就是 sway 跟 i3 相关的 nix 包设计良好)。
> 简单解释下什么是「声明式配置」,它是指用户只需要声明好自己想要的结果——比如说希望将 i3 桌面替换成 sway 桌面Nix 就会帮用户达成这个目标。用户不需要关心底层细节(比如说 sway 需要安装哪些软件包,哪些 i3 相关的软件包需要卸载掉,哪些系统配置或环境变量需要针对 sway 做调整、如果使用了 Nvidia 显卡 Sway 参数要做什么调整才能正常运行等等Nix 会自动帮用户处理这些细节(当然这有个前提,就是 sway 跟 i3 相关的 nix 包设计良好)。
而基于 Nix 包管理器构建的 Linux 发行版 NixOS可以简单用 OS as Code 来形容,它通过声明式的
Nix 配置文件来描述整个操作系统的状态。
而基于 Nix 包管理器构建的 Linux 发行版 NixOS可以简单用 OS as
Code 来形容,它通过声明式的 Nix 配置文件来描述整个操作系统的状态。
一个操作系统中有各种各样的软件包、配置文件、文本或二进制的数据,这些都是系统当前的状态,而
声明式的配置能够管理到的,只是其中静态的那一部分。而那些动态的数据——比如说
PostgreSQL/MySQL/MongoDB 的数据,显然是无法通过声明式配置管理的(总不能每次部署都直接删除
掉所有未在配置中声明的新数据吧)。因此 NixOS 实际也只支持通过声明式配置管理系统的部分状
态,上面提到的各种动态数据,以及用户 Home 目录中的所有内容,都不受它管控。在你将 NixOS 切
换到上一个版本时NixOS 不会对这些不受它管理的数据做任何操作。
一个操作系统中有各种各样的软件包、配置文件、文本或二进制的数据,这些都是系统当前的状态,而声明式的配置能够管理到的,只是其中静态的那一部分。而那些动态的数据——比如说 PostgreSQL/MySQL/MongoDB 的数据,显然是无法通过声明式配置管理的(总不能每次部署都直接删除掉所有未在配置中声明的新数据吧)。因此 NixOS 实际也只支持通过声明式配置管理系统的部分状态,上面提到的各种动态数据,以及用户 Home 目录中的所有内容,都不受它管控。在你将 NixOS 切换到上一个版本时NixOS 不会对这些不受它管理的数据做任何操作。
但是用户的 Home 目录中实际包含了许多重要的配置文件(或者叫
[Dotfiles](https://wiki.archlinux.org/title/Dotfiles)),用户当然会希望能使用 Nix 将它们给
管理起来。另一个重要的社区项目
[home-manager](https://github.com/nix-community/home-manager) 就填补了这块缺失,它被设计用
于管理用户 Home 目录中的配置文件以及用户级别的软件包。
[Dotfiles](https://wiki.archlinux.org/title/Dotfiles)),用户当然会希望能使用 Nix 将它们给管理起来。另一个重要的社区项目
[home-manager](https://github.com/nix-community/home-manager)
就填补了这块缺失,它被设计用于管理用户 Home 目录中的配置文件以及用户级别的软件包。
因为 Nix 声明式、可复现的特性Nix 不仅可用于管理桌面电脑的环境,也有很多人用它管理开发编
译环境、云上虚拟机、容器镜像构建等等Nix 官方的 [NixOps](https://github.com/NixOS/nixops)
与社区的 [colmena](https://github.com/zhaofengli/colmena) 都是基于 Nix 实现的运维工具。
因为 Nix 声明式、可复现的特性Nix 不仅可用于管理桌面电脑的环境,也有很多人用它管理开发编译环境、云上虚拟机、容器镜像构建等等Nix 官方的
[NixOps](https://github.com/NixOS/nixops) 与社区的
[colmena](https://github.com/zhaofengli/colmena) 都是基于 Nix 实现的运维工具。
## 为什么选择 NixOS
好几年前就听说过 Nix 包管理器,它用 Nix 语言编写配置来管理系统依赖,此外基于 Nix 包管理器
设计的 Linux 发行版 NixOS还能随时将系统回滚到任一历史状态额实际上这个回滚有些限制
面提过了)。 虽然听着很牛,但是不仅要多学一门语言,装个包还得写代码,当时觉得太麻烦就没研
究。
好几年前就听说过 Nix 包管理器,它用 Nix 语言编写配置来管理系统依赖,此外基于 Nix 包管理器设计的 Linux 发行版 NixOS还能随时将系统回滚到任一历史状态额实际上这个回滚有些限制前面提过了。 虽然听着很牛,但是不仅要多学一门语言,装个包还得写代码,当时觉得太麻烦就没研究。
但是我最近在使用 EndeavourOS 时遇到了一系列麻烦事儿,花了大量的精力去解决,搞得我精疲力
尽。我仔细一想,遇到的这些问题归根结底还是系统没有版本控制跟回滚机制,导致出了问题不能还
原,就必须得各种查资料找 Bug手动修复系统状态。
但是我最近在使用 EndeavourOS 时遇到了一系列麻烦事儿,花了大量的精力去解决,搞得我精疲力尽。我仔细一想,遇到的这些问题归根结底还是系统没有版本控制跟回滚机制,导致出了问题不能还原,就必须得各种查资料找 Bug手动修复系统状态。
所以我就决定干脆换成 NixOS.
切换到 NixOS 后,我对它那是相当的满意,腰也不疼了,背也不酸了... 最惊艳的是,现在我可以通
过仅仅一行命令(`sudo nixos-rebuild switch --flake .`),就能在一台全新安装的 NixOS 主机上
还原我的整个 i3 桌面环境以及所有我的常用软件!
切换到 NixOS 后,我对它那是相当的满意,腰也不疼了,背也不酸了... 最惊艳的是,现在我可以通过仅仅一行命令(`sudo nixos-rebuild switch --flake .`),就能在一台全新安装的 NixOS 主机上还原我的整个 i3 桌面环境以及所有我的常用软件!
NixOS 的回滚能力与可复现能力给了我非常大的底气,我现在再也不用怕把系统搞挂了(挂了直接回滚
就恢复了),于是我又在 NixOS 尝试了 Hyprland, Waybar 等等许多新鲜玩意儿~ 在以前
EndeavourOS 上我肯定是不太敢这么玩的,因为万一要是把系统玩出毛病了,就必须手动排查问题、修
复系统状态,那可是相当麻烦。
NixOS 的回滚能力与可复现能力给了我非常大的底气,我现在再也不用怕把系统搞挂了(挂了直接回滚就恢复了),于是我又在 NixOS 尝试了 Hyprland,
Waybar 等等许多新鲜玩意儿~ 在以前 EndeavourOS 上我肯定是不太敢这么玩的,因为万一要是把系统玩出毛病了,就必须手动排查问题、修复系统状态,那可是相当麻烦。
随着我对 NixOS 与 Nix 的使用越来越深入,我发现它还非常适合用于同步管理多台主机的配置。目前
我的个人配置 [ryan4yin/nix-config](https://github.com/ryan4yin/nix-config) 同步管理了许多
主机的配置:
随着我对 NixOS 与 Nix 的使用越来越深入,我发现它还非常适合用于同步管理多台主机的配置。目前我的个人配置
[ryan4yin/nix-config](https://github.com/ryan4yin/nix-config) 同步管理了许多主机的配置:
- 桌面电脑
- 一台 Macbook Pro 2022 (M2 aarch64)
@@ -65,6 +44,5 @@ EndeavourOS 上我肯定是不太敢这么玩的,因为万一要是把系统
- 10+ 台 NixOS 虚拟机(amd64)
- 若干块 aarch64 跟 riscv64 的开发板
其中三台桌面电脑的开发环境都通过 Home Manager 管理,主要配置完全共用,在任意一台主机上修改
的配置,可以通过 Git 无缝同步到其他主机上。NixOS 几乎完全帮我屏蔽了三台机器底层的 OS 与芯
片架构差异,体验非常丝滑!
其中三台桌面电脑的开发环境都通过 Home
Manager 管理,主要配置完全共用,在任意一台主机上修改的配置,可以通过 Git 无缝同步到其他主机上。NixOS 几乎完全帮我屏蔽了三台机器底层的 OS 与芯片架构差异,体验非常丝滑!

View File

@@ -3,19 +3,17 @@
Nix 有多种安装方式:
1. 以包管理器的形式安装到 MacOS/Linux/WSL 三种系统上
2. 也可以直接安装 NixOS这是 Nix 官方推出的一个 Linux 发行版,使用 Nix 包管理器来管理整个
系统环境。
2. 也可以直接安装 NixOS这是 Nix 官方推出的一个 Linux 发行版,使用 Nix 包管理器来管理整个系统环境。
本书主要介绍 NixOS 与 Flakes 的使用,因此不展开讨论。
NixOS 的安装不难,与许多传统发行版类似,它提供了一个对新手非常友好的 GUI 安装程序。请移步
[NixOS-CN 的系统安装教程](https://nixos-cn.org/tutorials/installation/) 查看详细的安装步
骤。
[NixOS-CN 的系统安装教程](https://nixos-cn.org/tutorials/installation/)
查看详细的安装步骤。
其他可能有用的参考资料:
1. [NixOS 官网](https://nixos.org/download.html)
1. [复用 flake 管理 NixOS WSL](https://zhuanlan.zhihu.com/p/627073511): 使用 WSL 的用户可
以参考下这篇文章
1. [复用 flake 管理 NixOS WSL](https://zhuanlan.zhihu.com/p/627073511): 使用 WSL 的用户可以参考下这篇文章
1. [ryan4yin/nix-darwin-kickstarter](https://github.com/ryan4yin/nix-darwin-kickstart):
macOS 用户可以通过这个模板仓库结合本书的内容来学习使用 Nix.

View File

@@ -1,12 +1,12 @@
# 添加二进制缓存服务器
前面介绍了 Nix Store 与二进制缓存的概念,这里我们来看看如何添加多个缓存服务器,以加速包的
下载速度。
前面介绍了 Nix
Store 与二进制缓存的概念,这里我们来看看如何添加多个缓存服务器,以加速包的下载速度。
## 为什么要添加缓存服务器 {#why-add-cache-servers}
Nix 提供的官方缓存服务器 <https://cache.nixos.org> 提供了绝大部分常用软件包的二进制缓存,
但它并不能满足所有用户的需求。在以下情况下,我们会需要添加额外的缓存服务器:
Nix 提供的官方缓存服务器 <https://cache.nixos.org>
提供了绝大部分常用软件包的二进制缓存,但它并不能满足所有用户的需求。在以下情况下,我们会需要添加额外的缓存服务器:
1. 添加一些第三方项目的缓存服务器,例如 nix-community 的缓存服务器
<https://nix-community.cachix.org> 提供了社区项目的二进制缓存,可以加速这些项目的构建。
@@ -17,21 +17,15 @@ Nix 提供的官方缓存服务器 <https://cache.nixos.org> 提供了绝大部
Nix 中通过如下几个 options 来配置缓存服务器:
1. [substituters](https://nixos.org/manual/nix/stable/command-ref/conf-file#conf-substituters):
它是一个字符串数组每个字符串都是一个缓存服务器的地址Nix 会按照数组中的顺序依次尝试
从这些服务器中查找缓存。
2. [trusted-public-keys](https://nixos.org/manual/nix/stable/command-ref/conf-file#conf-trusted-public-keys):
为了防范恶意攻击Nix 默认启用
1. [substituters](https://nixos.org/manual/nix/stable/command-ref/conf-file#conf-substituters): 它是一个字符串数组每个字符串都是一个缓存服务器的地址Nix 会按照数组中的顺序依次尝试从这些服务器中查找缓存。
2. [trusted-public-keys](https://nixos.org/manual/nix/stable/command-ref/conf-file#conf-trusted-public-keys): 为了防范恶意攻击Nix 默认启用
[require-sigs](https://nixos.org/manual/nix/stable/command-ref/conf-file#conf-require-sigs)
功能,只有附带了签名、且签名能被 `trusted-public-keys` 中的任意一个公钥验证通过的缓存,
才会被 Nix 使用。因此我们需要将 `substituters` 对应的公钥添加到 `trusted-public-keys`
中。
1. 国内的镜像源都是直接从官方缓存服务器中同步的,因此它们的公钥与官方缓存服务器的公钥是
一致的,我们可以直接使用官方缓存服务器的公钥,无需额外配置。
2. 这种完全依赖公钥机制的验证方式,实际是将安全责任转嫁给了用户。用户如果希望使用某个第
三方库,但又希望使用它的第三方缓存服务器加快构建速度,那就必须自己承担对应的安全风
险,自行决策是否将该缓存服务器的公钥添加进 `trusted-public-keys`。为了完全解决这个信
任问题Nix 推出了实验特性
功能,只有附带了签名、且签名能被 `trusted-public-keys`
中的任意一个公钥验证通过的缓存,才会被 Nix 使用。因此我们需要将 `substituters`
对应的公钥添加到 `trusted-public-keys` 中。
1. 国内的镜像源都是直接从官方缓存服务器中同步的,因此它们的公钥与官方缓存服务器的公钥是一致的,我们可以直接使用官方缓存服务器的公钥,无需额外配置。
2. 这种完全依赖公钥机制的验证方式,实际是将安全责任转嫁给了用户。用户如果希望使用某个第三方库,但又希望使用它的第三方缓存服务器加快构建速度,那就必须自己承担对应的安全风险,自行决策是否将该缓存服务器的公钥添加进
`trusted-public-keys`。为了完全解决这个信任问题Nix 推出了实验特性
[ca-derivations](https://wiki.nixos.org/wiki/Ca-derivations),它不依赖
`trusted-public-keys` 进行签名校验,有兴趣的可以自行了解。
@@ -40,18 +34,16 @@ Nix 中通过如下几个 options 来配置缓存服务器:
1.`/etc/nix/nix.conf` 中配置,这是全局配置,对所有用户生效。
1. 可在任一 NixOS Module 中通过 `nix.settings.substituters`
`nix.settings.trusted-public-keys` 来声明式地生成 `/etc/nix/nix.conf`.
2. 在 flake 项目的 `flake.nix` 中通过 `nixConfig.substituters` 来配置,此配置仅对当前
flake 生效。
3. 可通过 `nix` 指令的 `--option substituters="http://xxx"` 参数来临时设定,此配置仅对当前
指令生效。
2. 在 flake 项目的 `flake.nix` 中通过 `nixConfig.substituters`
来配置,此配置仅对当前 flake 生效。
3. 可通过 `nix` 指令的 `--option substituters="http://xxx"`
参数来临时设定,此配置仅对当前指令生效。
上面三种方式中,除了第一种全局配置外,其他两种都是临时配置。如果同时使用了多种方式,那么后
面的配置会直接覆盖前面的配置。
上面三种方式中,除了第一种全局配置外,其他两种都是临时配置。如果同时使用了多种方式,那么后面的配置会直接覆盖前面的配置。
但临时设置 `substituters` 存在安全风险,前面我们也解释了基于 `trusted-public-keys` 的安全
验证机制存在缺陷。将一个不可信的缓存服务器添加到 substituters 中,可能会导致包含恶意内容的
缓存被复制到 Nix Store 中。因此 Nix 对 substituters 的临时设置做出了限制,要想通过第二三种
方式设定 substituers前提是满足如下任意一个条件
但临时设置 `substituters` 存在安全风险,前面我们也解释了基于 `trusted-public-keys`
的安全验证机制存在缺陷。将一个不可信的缓存服务器添加到 substituters 中,可能会导致包含恶意内容的缓存被复制到 Nix
Store 中。因此 Nix 对 substituters 的临时设置做出了限制,要想通过第二三种方式设定 substituers前提是满足如下任意一个条件
1. [`/etc/nix/nix.conf` 中的 `trusted-users`](https://nixos.org/manual/nix/stable/command-ref/conf-file#conf-trusted-users)
参数列表中包含当前用户。
@@ -60,8 +52,9 @@ Nix 中通过如下几个 options 来配置缓存服务器:
基于上述信息,如下是上述三种配置方式的示例。
首先是通过 `nix.settings` 声明式地配置系统层面的 substituters 与 trusted-public-keys, 将如
下配置添加到 `/etc/nixos/configuration.nix` 或其他任一 NixOS Module 中即可:
首先是通过 `nix.settings`
声明式地配置系统层面的 substituters 与 trusted-public-keys, 将如下配置添加到
`/etc/nixos/configuration.nix` 或其他任一 NixOS Module 中即可:
```nix{7-27}
{
@@ -163,8 +156,7 @@ Nix 中通过如下几个 options 来配置缓存服务器:
sudo nixos-rebuild switch --option substituters "https://nix-community.cachix.org" --option trusted-public-keys "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
```
选择上述三种方案的任一一种进行配置并部署,部署成功之后,后续所有的包都会优先从国内镜像源查
找缓存。
选择上述三种方案的任一一种进行配置并部署,部署成功之后,后续所有的包都会优先从国内镜像源查找缓存。
> 如果你的系统 Hostname 不是 `my-nixos`,你需要在 `flake.nix` 中修改 `nixosConfigurations`
> 的名称,或者使用 `--flake /etc/nixos#my-nixos` 来指定配置名称。
@@ -173,12 +165,12 @@ sudo nixos-rebuild switch --option substituters "https://nix-community.cachix.or
前面提到的三种方式配置的 `substituters` 会相互覆盖,但比较理想的情况应该是:
1. 在系统层面的 `/etc/nix/nix.conf` 中仅配置最通用的 substituters 与 trusted-public-keys
例如官方缓存服务器与国内镜像源。
2. 在每个 flake 项目的 `flake.nix` 中配置该项目特有的 substituters 与
trusted-public-keys例如 nix-community 等非官方的缓存服务器。
3. 在构建 flake 项目时,应该将 `flake.nix` 与 `/etx/nix/nix.conf` 中配置的 substituters 与
trusted-public-keys **合并**使用。
1. 在系统层面的 `/etc/nix/nix.conf`
中仅配置最通用的 substituters 与 trusted-public-keys例如官方缓存服务器与国内镜像源。
2. 在每个 flake 项目的 `flake.nix`
中配置该项目特有的 substituters 与 trusted-public-keys例如 nix-community 等非官方的缓存服务器。
3. 在构建 flake 项目时,应该将 `flake.nix` 与 `/etx/nix/nix.conf`
中配置的 substituters 与 trusted-public-keys **合并**使用。
Nix 提供了
[`extra-` 前缀](https://nixos.org/manual/nix/stable/command-ref/conf-file.html?highlight=extra#file-format)
@@ -265,8 +257,9 @@ Nix 提供了
有些用户可能会希望能直接通过 HTTP/Socks5 代理来加速包下载,这里介绍下怎么设置。
直接在 Terminal 中使用 `export HTTPS_PROXY=http://127.0.0.1:7890` 这类方式是无法生效的,因
为 nix 实际干活的是一个叫 `nix-daemon` 的后台进程,而不是直接在 Terminal 中执行的命令。
直接在 Terminal 中使用 `export HTTPS_PROXY=http://127.0.0.1:7890`
这类方式是无法生效的,因为 nix 实际干活的是一个叫 `nix-daemon`
的后台进程,而不是直接在 Terminal 中执行的命令。
如果你只是临时需要使用代理,可以通过如下命令设置代理环境变量:
@@ -280,21 +273,17 @@ sudo systemctl daemon-reload
sudo systemctl restart nix-daemon
```
部署此配置后,可通过 `sudo cat /proc/$(pidof nix-daemon)/environ | tr '\0' '\n'` 查看
nix-daemon 进程的所有环境变量,确认环境变量的设置是否生效。
部署此配置后,可通过 `sudo cat /proc/$(pidof nix-daemon)/environ | tr '\0' '\n'`
查看 nix-daemon 进程的所有环境变量,确认环境变量的设置是否生效。
位于 `/run/systemd/system/nix-daemon.service.d/override.conf` 的设置会在系统重启后被自动删
除,或者你可以手动删除它并重启 nix-daemon 服务来恢复原始设置。
位于 `/run/systemd/system/nix-daemon.service.d/override.conf`
的设置会在系统重启后被自动删除,或者你可以手动删除它并重启 nix-daemon 服务来恢复原始设置。
如果你希望永久设置代理,建议将上述命令保存为 shell 脚本,在每次启动系统时运行一下。或者也
可以使用旁路网关或 TUN 等全局代理方案。
如果你希望永久设置代理,建议将上述命令保存为 shell 脚本,在每次启动系统时运行一下。或者也可以使用旁路网关或 TUN 等全局代理方案。
> 社区也有人通过 `systemd.services.nix-daemon.environment` 以声明式的方式为 nix-daemon 永
> 久设置代理但这种做法下一旦代理出了问题会非常麻烦nix-daemon 将无法正常工作,进而导致
> 大多数 nix 命令无法正常运行,而且 systemd 自身的配置被设置了只读保护,无法简单地修改配置
> 删除代理设置。因此不建议使用这种方式。
> 社区也有人通过 `systemd.services.nix-daemon.environment`
> 以声明式的方式为 nix-daemon 永久设置代理但这种做法下一旦代理出了问题会非常麻烦nix-daemon 将无法正常工作,进而导致大多数 nix 命令无法正常运行,而且 systemd 自身的配置被设置了只读保护,无法简单地修改配置删除代理设置。因此不建议使用这种方式。
> 使用一些商用代理或公共代理时你可能会遇到 GitHub 下载时报 HTTP 403 错误
> [nixos-and-flakes-book/issues/74](https://github.com/ryan4yin/nixos-and-flakes-book/issues/74)
> 可尝试通过更换代理服务器或者设置
> 使用一些商用代理或公共代理时你可能会遇到 GitHub 下载时报 HTTP
> 403 错误[nixos-and-flakes-book/issues/74](https://github.com/ryan4yin/nixos-and-flakes-book/issues/74)可尝试通过更换代理服务器或者设置
> [access-tokens](https://github.com/NixOS/nix/issues/6536) 来解决。

View File

@@ -2,27 +2,23 @@
## 简介
Nix 二进制缓存是 Nix Store 的一个实现,它不把数据存储在本地,而是存储在远程服务器上,方便
二进制缓存的多机共享。
Nix 二进制缓存是 Nix
Store 的一个实现,它不把数据存储在本地,而是存储在远程服务器上,方便二进制缓存的多机共享。
Nix 官方的二进制缓存服务器只提供了使用标准参数构建的二进制缓存。如果你自定义了构建参数,或
者你使用了 Nixpkgs 之外的软件包,那就会导致 Nix 找不到对应的二进制缓存,从而执行本地构建流
程。
Nix 官方的二进制缓存服务器只提供了使用标准参数构建的二进制缓存。如果你自定义了构建参数,或者你使用了 Nixpkgs 之外的软件包,那就会导致 Nix 找不到对应的二进制缓存,从而执行本地构建流程。
单纯依赖你本地的 Nix Store `/nix/store` 有时候会变得很痛苦,因为你需要在每台机器上重新构建
所有你自定义的这些软件包,这可能需要相当长的时间,而且构建过程可能会消耗大量内存。如果是在
Raspberry Pi 等性能较低的平台上使用 Nix这种情况会变得更加糟糕。
单纯依赖你本地的 Nix Store `/nix/store`
有时候会变得很痛苦,因为你需要在每台机器上重新构建所有你自定义的这些软件包,这可能需要相当长的时间,而且构建过程可能会消耗大量内存。如果是在 Raspberry
Pi 等性能较低的平台上使用 Nix这种情况会变得更加糟糕。
本文档将介绍如何使用 S3 服务(如 MinIO搭建你自己的 Nix 二进制缓存服务器,以便在多台机器
之间共享构建结果,从而解决上述问题。
本文档将介绍如何使用 S3 服务(如 MinIO搭建你自己的 Nix 二进制缓存服务器,以便在多台机器之间共享构建结果,从而解决上述问题。
## 准备工作
1. 一台 NixOS 主机
1. 部署好 MinIO 服务器
1. 如果没有,您可以参考 MinIO
的[官方部署指南](https://min.io/docs/minio/linux/operations/installation.html) 进行
部署。
1. 如果没有,您可以参考 MinIO 的[官方部署指南](https://min.io/docs/minio/linux/operations/installation.html)
进行部署。
1. MinIO 服务器需要具备有效的 TLS 数字证书,可以是公共证书也可以是私有证书。本文将使用
`https://minio.homelab.local` 加上私有证书作为示例。
1. 安装好 `minio-client`
@@ -65,8 +61,8 @@ nix run nixpkgs#pwgen -- -c -n -y -s -B 32 1
}
```
Nix 将直接与 S3 存储桶交互,因此我们都需要给所有需要访问 Nix 二进制缓存的机器配置好对应的
S3 凭据。创建 `~/.aws/credentials`,内容如下(请注意用前面 `pwgen` 命令生成的密码替换
Nix 将直接与 S3 存储桶交互,因此我们都需要给所有需要访问 Nix 二进制缓存的机器配置好对应的 S3 凭据。创建
`~/.aws/credentials`,内容如下(请注意用前面 `pwgen` 命令生成的密码替换
`<nixbuildersecret>`)。
```conf
@@ -127,15 +123,14 @@ mc admin policy add s3 nix-cache-write nix-cache-write.json
mc admin policy set s3 nix-cache-write user=nixbuilder
```
再允许匿名用户在不进行身份验证的情况下下载文件,这样所有 Nix 服务器就都能直接从这个 S3 缓
存拉数据了:
再允许匿名用户在不进行身份验证的情况下下载文件,这样所有 Nix 服务器就都能直接从这个 S3 缓存拉数据了:
```bash
mc anonymous set download s3/nix-cache
```
最后,添加 `nix-cache-info` 文件到 S3 桶根目录中Nix 需要这个文件记录一些二进制缓存相关的
信息:
最后,添加 `nix-cache-info`
文件到 S3 桶根目录中Nix 需要这个文件记录一些二进制缓存相关的信息:
```bash
cat > nix-cache-info <<EOF
@@ -150,11 +145,9 @@ mc cp ./nix-cache-info s3/nix-cache/nix-cache-info
## 生成签名密钥对
前面介绍了Nix 二进制缓存使用公钥签名机制校验数据的数据来源与完整性,因此我们还需要为我们
的 Nix 构建机生成一个密钥对用于二进制缓存的签名验证。
前面介绍了Nix 二进制缓存使用公钥签名机制校验数据的数据来源与完整性,因此我们还需要为我们的 Nix 构建机生成一个密钥对用于二进制缓存的签名验证。
密钥名称是任意的,但 NixOS 开发人员强烈建议使用缓存的域名后跟一个整数,这样如果密钥需要撤
销或重新生成,就可以递增末尾的整数。
密钥名称是任意的,但 NixOS 开发人员强烈建议使用缓存的域名后跟一个整数,这样如果密钥需要撤销或重新生成,就可以递增末尾的整数。
```bash
nix key generate-secret --key-name s3.homelab.local-1 > ~/.config/nix/secret.key

View File

@@ -1,15 +1,15 @@
# Nix Store 与二进制缓存
这里我们先简单介绍下 Nix Store、Nix 二进制缓存以及其他相关概念,但不涉及具体的配置与使用方
法,这些内容会在后续章节中详细介绍。
这里我们先简单介绍下 Nix
Store、Nix 二进制缓存以及其他相关概念,但不涉及具体的配置与使用方法,这些内容会在后续章节中详细介绍。
## Nix Store
Nix Store 是 Nix 包管理器的核心概念之一,它是一个只读文件系统,用于存储所有需要不可变这一
特性的文件,包括软件包的构建结果、软件包的元数据、软件包的所有构建输入等等。
Nix
Store 是 Nix 包管理器的核心概念之一,它是一个只读文件系统,用于存储所有需要不可变这一特性的文件,包括软件包的构建结果、软件包的元数据、软件包的所有构建输入等等。
Nix 包管理器使用 Nix 函数式语言来描述软件包及其依赖关系,每个软件包都被视为一个纯函数的输
出,软件包的构建结果被保存在 Nix Store 中。
Nix 包管理器使用 Nix 函数式语言来描述软件包及其依赖关系,每个软件包都被视为一个纯函数的输出,软件包的构建结果被保存在 Nix
Store 中。
Nix Store 中的数据具有固定的路径格式:
@@ -19,25 +19,23 @@ Nix Store 中的数据具有固定的路径格式:
store directory digest name
```
可以看到Nix Store 中的路径以一个哈希值digest为前缀后面跟着软件包的名称和版本号。这
个哈希值是基于软件包的所有输入信息(构建参数、依赖关系、依赖版本等等)计算出来的,任何构建
参数或依赖关系的变化都会导致哈希值的变化,从而保证了每个软件包路径的唯一性。再加上 Nix
Store 是一个只读文件系统,这就保证了软件包的不可变性,即软件包一旦构建完成,就不会再发生变
化。
可以看到Nix
Store 中的路径以一个哈希值digest为前缀后面跟着软件包的名称和版本号。这个哈希值是基于软件包的所有输入信息(构建参数、依赖关系、依赖版本等等)计算出来的,任何构建参数或依赖关系的变化都会导致哈希值的变化,从而保证了每个软件包路径的唯一性。再加上 Nix
Store 是一个只读文件系统,这就保证了软件包的不可变性,即软件包一旦构建完成,就不会再发生变化。
因为构建结果的存储路径是基于构建流程的所有输入信息计算出来的,**同样的输入信息会得到同样的
存储路径** 这种设计也被称为输入寻址模型_Input-addressed Model_
因为构建结果的存储路径是基于构建流程的所有输入信息计算出来的,**同样的输入信息会得到同样的存储路径**
这种设计也被称为输入寻址模型_Input-addressed Model_
### NixOS 如何使用 Nix Store
NixOS 的声明式配置将会计算出哪些软件包需要被安装,然后将这些软件包在 Nix Store 中的存储路
径软链接到 `/run/current-system` 中,再通过修改 `PATH` 等环境变量指向
`/run/current-system` 中对应的文件夹从而实现软件包的安装。每次部署时NixOS 会计算出新的
系统配置,清理掉旧的软链接,再重新创建新的软链接,从而确保系统环境与声明式配置一致。
NixOS 的声明式配置将会计算出哪些软件包需要被安装,然后将这些软件包在 Nix
Store 中的存储路径软链接到 `/run/current-system` 中,再通过修改 `PATH` 等环境变量指向
`/run/current-system`
中对应的文件夹从而实现软件包的安装。每次部署时NixOS 会计算出新的系统配置,清理掉旧的软链接,再重新创建新的软链接,从而确保系统环境与声明式配置一致。
home-manager 也是类似的,它会将用户配置的软件包软链接到
`/etc/profiles/per-user/your-username` 这个路径下,再通过修改 `PATH` 等环境变量指向这个路
径,从而实现用户软件包的安装。
`/etc/profiles/per-user/your-username` 这个路径下,再通过修改 `PATH`
等环境变量指向这个路径,从而实现用户软件包的安装。
```bash
# 查看环境中的 bash 来自哪个路径(使用 NixOS 安装)
@@ -63,12 +61,12 @@ lrwxrwxrwx 15 root root 76 1970年 1月 1日 /run/current-system/sw/bin/bash ->
lrwxrwxrwx 2 root root 72 1970年 1月 1日 /etc/profiles/per-user/ryan/bin/cowsay -> /nix/store/w2czyf82gxz4vy9kzsdhr88112bmc0c1-home-manager-path/bin/cowsay
```
`nix develop` 命令则是直接将软件包的存储路径添加到 `PATH` `LD_LIBRARY_PATH` 等环境变量
中,使新创建的 shell 环境中可以直接使用这些软件包或库。
`nix develop` 命令则是直接将软件包的存储路径添加到 `PATH` `LD_LIBRARY_PATH`
等环境变量中,使新创建的 shell 环境中可以直接使用这些软件包或库。
以本书的源码仓库
[ryan4yin/nixos-and-flakes-book](https://github.com/ryan4yin/nixos-and-flakes-book) 为例,
在该仓库中执行 `nix develop` 命令,再查看下 `PATH` 环境变量的内容:
[ryan4yin/nixos-and-flakes-book](https://github.com/ryan4yin/nixos-and-flakes-book)
为例,在该仓库中执行 `nix develop` 命令,再查看下 `PATH` 环境变量的内容:
```bash
nix develop
@@ -82,66 +80,54 @@ PATH=/nix/store/h13fnmpm8m28qypsba2xysi8a90crphj-pre-commit-3.6.0/bin:/nix/store
## Nix Store 的垃圾回收
Nix Store 是一个中心化的存储系统,所有的软件包构建输入跟输出都会被存储在这里。随着系统的使
Nix Store 中的软件包会越来越多,占用的磁盘空间也会越来越大。
Nix
Store 是一个中心化的存储系统所有的软件包构建输入跟输出都会被存储在这里。随着系统的使用Nix
Store 中的软件包会越来越多,占用的磁盘空间也会越来越大。
为了避免 Nix Store 无限制地增长Nix 包管理器为本地 Nix Store 提供了垃圾回收机制,用于清理
`/nix/store` 中的旧数据、回收存储空间。
根据
[Chapter 11. The Garbage Collector - nix pills](https://nixos.org/guides/nix-pills/garbage-collector)
的说法, `nix-store --gc` 命令会执行垃圾回收操作,它会递归遍历 `/nix/var/nix/gcroots/`
录下的所有软链接,找出所有被引用的软件包,然后将不再被引用的软件包删除。而
的说法, `nix-store --gc` 命令会执行垃圾回收操作,它会递归遍历 `/nix/var/nix/gcroots/`
录下的所有软链接,找出所有被引用的软件包,然后将不再被引用的软件包删除。而
`nix-collect-garbage --delete-old` 则更进一步,它会先删除掉所有旧的
[profiles](https://nixos.org/manual/nix/stable/command-ref/files/profiles),再执行
`nix-store --gc` 命令清理掉不再被引用的软件包。
需要注意的是,`nix build`, `nix develop` 等命令的构建结果并不会被自动添加到
`/nix/var/nix/gcroots/` 目录中,所以这些构建结果会被垃圾回收机制清理掉。你可以通过
`nix-instantiate``keep-outputs = true` 等手段来避免这种情况,但我目前觉得搭建一个自己
的二进制缓存服务器,然后在你在缓存服务器上配置一个较长的缓存时间(比如一年),将数据推送到
缓存服务器上,这样既可以在所有机器上共享构建结果,又可以避免本地构建结果被本地的垃圾回收机
制清理掉,一举两得。
`nix-instantiate``keep-outputs = true`
等手段来避免这种情况,但我目前觉得搭建一个自己的二进制缓存服务器,然后在你在缓存服务器上配置一个较长的缓存时间(比如一年),将数据推送到缓存服务器上,这样既可以在所有机器上共享构建结果,又可以避免本地构建结果被本地的垃圾回收机制清理掉,一举两得。
## 二进制缓存
Nix 包管理器与 Nix Store 的设计保证了软件包的不可变性,使得 Nix Store 中的构建结果可以直接
被在多台机器之间共享。只要这些机器使用了同样的输入信息构建软件包,它们就会得到相同的输出路
Nix 则可以据此直接复用其他机器上的构建结果,而不需要重新构建软件包,从而提升软件包的安
装速度。
Nix 包管理器与 Nix Store 的设计保证了软件包的不可变性,使得 Nix
Store 中的构建结果可以直接被在多台机器之间共享。只要这些机器使用了同样的输入信息构建软件包,它们就会得到相同的输出路Nix 则可以据此直接复用其他机器上的构建结果,而不需要重新构建软件包,从而提升软件包的安装速度。
Nix 二进制缓存就是基于这个特性而设计的,它实质也是 Nix Store 的一个实现,只不过它不把数据
存储在本地而是存储在远程服务器上。需要使用的时候Nix 包管理器会从远程服务器上下载对应的
构建结果到本地的 `/nix/store` 中,避免耗时的本地构建。
Nix 二进制缓存就是基于这个特性而设计的,它实质也是 Nix
Store 的一个实现,只不过它不把数据存储在本地而是存储在远程服务器上。需要使用的时候Nix 包管理器会从远程服务器上下载对应的构建结果到本地的
`/nix/store` 中,避免耗时的本地构建。
Nix 提供了官方二进制缓存服务器 <https://cache.nixos.org>,它缓存了 nixpkgs 中绝大部分
packages 在常用 CPU 指令集下的构建结果。当你在本地执行 Nix 构建指令时Nix 首先会尝试从缓
存服务器中查找对应的二进制缓存,如果查找到了,就会直接下载该缓存文件,跳过耗时的本地编译构
建从而大大提升构建速度。
Nix 提供了官方二进制缓存服务器
<https://cache.nixos.org>,它缓存了 nixpkgs 中绝大部分 packages 在常用 CPU 指令集下的构建结果。当你在本地执行 Nix 构建指令时Nix 首先会尝试从缓存服务器中查找对应的二进制缓存,如果查找到了,就会直接下载该缓存文件,跳过耗时的本地编译构建从而大大提升构建速度。
## Nix 二进制缓存的信任模型
**Input-addressed Model** **只保证同样的输入得到同样的输出路径,并不保证输出内容的唯一
性**。也就是说即使输入信息相同,多次构建同一个软件包得到的输出内容也可能不同。
**Input-addressed Model**
**只保证同样的输入得到同样的输出路径,并不保证输出内容的唯一性**。也就是说即使输入信息相同,多次构建同一个软件包得到的输出内容也可能不同。
虽然 Nix 已经通过在构建环境中默认禁用网络、使用固定的时间戳等方式尽量减少不确定性,但软件
包构建过程中仍然可能受到一些不可控因素的影响而产生不同的输出内容。这些不可控因素导致的输出
内容不同通常不会对软件包的功能产生影响,但却给二进制缓存的安全共享带来了挑战——输出内容的不
确定性使我们无法判断从缓存服务器中下载的二进制缓存是否真的是使用我们声明的输入信息构建的,
是否包含恶意内容。
虽然 Nix 已经通过在构建环境中默认禁用网络、使用固定的时间戳等方式尽量减少不确定性,但软件包构建过程中仍然可能受到一些不可控因素的影响而产生不同的输出内容。这些不可控因素导致的输出内容不同通常不会对软件包的功能产生影响,但却给二进制缓存的安全共享带来了挑战——输出内容的不确定性使我们无法判断从缓存服务器中下载的二进制缓存是否真的是使用我们声明的输入信息构建的,是否包含恶意内容。
Nix 包管理器目前给出的解决方案是——使用公私钥签名机制来验证二进制缓存的数据来源与完整性。这
种验证方式实际是将安全责任转嫁给了用户。用户如果希望使用某个非官方缓存服务器来加快某些软件
包的构建速度,那就必须将该缓存服务器的公钥添加进 `trusted-public-keys` 中,并自己承担对应
的安全风险——该缓存服务器提供的缓存数据可能夹带了私货(恶意内容)。
Nix 包管理器目前给出的解决方案是——使用公私钥签名机制来验证二进制缓存的数据来源与完整性。这种验证方式实际是将安全责任转嫁给了用户。用户如果希望使用某个非官方缓存服务器来加快某些软件包的构建速度,那就必须将该缓存服务器的公钥添加进
`trusted-public-keys`
中,并自己承担对应的安全风险——该缓存服务器提供的缓存数据可能夹带了私货(恶意内容)。
### Content-addressed model
[RFC062 - content-addressed store paths](https://github.com/NixOS/rfcs/blob/master/rfcs/0062-content-addressed-paths.md)
是社区在提升构建结果的一致性上的一次尝试。它提出了一种新的存储路径计算方式,即基于构建结果
outputs而不是输入信息inputs来计算最终的存储路径。这种设计可以保证构建结果的一致
性——如果构建结果不同,那么存储路径也会不同,从而避免了 input-addressed model 中存在的输出
内容不确定性问题。
是社区在提升构建结果的一致性上的一次尝试。它提出了一种新的存储路径计算方式,即基于构建结果outputs而不是输入信息inputs来计算最终的存储路径。这种设计可以保证构建结果的一致性——如果构建结果不同那么存储路径也会不同从而避免了 input-addressed
model 中存在的输出内容不确定性问题。
不过它目前还在实验性阶段,尚未被广泛应用。

View File

@@ -1,11 +1,12 @@
# 降级与升级软件包 {#rollback-package-version}
在使用 Nix Flakes 后,目前大家用得比较多的都是 `nixos-unstable` 分支的 nixpkgs有时候就会
遇到一些 bug比如我最近2023/5/6就遇到了
在使用 Nix Flakes 后,目前大家用得比较多的都是 `nixos-unstable`
分支的 nixpkgs有时候就会遇到一些 bug比如我最近2023/5/6就遇到了
[chrome/vscode 闪退的问题](https://github.com/swaywm/sway/issues/7562)。
这时候就需要退回到之前的版本,在 Nix Flakes 中,所有的包版本与 hash 值与其 input 数据源的
git commit 是一一对应的关系,因此回退某个包的到历史版本,就需要锁定其 input 数据源的 git
这时候就需要退回到之前的版本,在 Nix
Flakes 中,所有的包版本与 hash 值与其 input 数据源的 git
commit 是一一对应的关系,因此回退某个包的到历史版本,就需要锁定其 input 数据源的 git
commit.
为了实现上述需求,首先修改 `/etc/nixos/flake.nix`,示例内容如下(主要是利用 `specialArgs`
@@ -101,10 +102,9 @@ commit.
## 使用 Overlay 锁定软件包版本
上面介绍的方法非常适合用于普通的应用程序Application但有时候你可能会需要替换一些被这
些应用程序依赖的库Library。这时候就需要用到 [Overlays](../nixpkgs/overlays.md) 了!我
们可以通过 Overlay 来修改某个库的版本,这会导致 Nix 重新编译所有依赖于该库的软件包,但在锁
定一些比较底层的库版本时,这是一个非常好的方法。
上面介绍的方法非常适合用于普通的应用程序Application但有时候你可能会需要替换一些被这些应用程序依赖的库Library。这时候就需要用到
[Overlays](../nixpkgs/overlays.md)
了!我们可以通过 Overlay 来修改某个库的版本,这会导致 Nix 重新编译所有依赖于该库的软件包,但在锁定一些比较底层的库版本时,这是一个非常好的方法。
示例如下:
@@ -124,11 +124,11 @@ commit.
## 部署新配置
配置完成后,通过 `sudo nixos-rebuild switch` 部署即可将 firefox/chrome/vscode 三个软件包回
退到 stable 分支的版本。
配置完成后,通过 `sudo nixos-rebuild switch`
部署即可将 firefox/chrome/vscode 三个软件包回退到 stable 分支的版本。
> 根据 @fbewivpjsbsby 补充的文章
> [1000 instances of nixpkgs](https://discourse.nixos.org/t/1000-instances-of-nixpkgs/17347)
> 在子模块或者子 flakes 中用 `import` 来定制 `nixpkgs` 不是一个好的习惯,因为每次 `import`
> 都会重新求值并产生一个新的 nixpkgs 实例,在配置越来越多时会导致构建时间变长、内存占用变
> 大。所以这里改为了在 `flake.nix` 中创建所有 nixpkgs 实例。
> [1000 instances of nixpkgs](https://discourse.nixos.org/t/1000-instances-of-nixpkgs/17347)在子模块或者子 flakes 中用
> `import` 来定制 `nixpkgs` 不是一个好的习惯,因为每次 `import`
> 都会重新求值并产生一个新的 nixpkgs 实例,在配置越来越多时会导致构建时间变长、内存占用变大。所以这里改为了在
> `flake.nix` 中创建所有 nixpkgs 实例。

View File

@@ -1,16 +1,14 @@
# 开始使用 NixOS
了解了 Nix 语言的基本用法之后,我们就可以开始使用 Nix 语言来配置 NixOS 系统了。NixOS 的系
统配置路径为 `/etc/nixos/configuration.nix`,它包含系统的所有声明式配置,如时区、语言、键
盘布局、网络、用户、文件系统、启动项、桌面环境等等。
了解了 Nix 语言的基本用法之后,我们就可以开始使用 Nix 语言来配置 NixOS 系统了。NixOS 的系统配置路径为
`/etc/nixos/configuration.nix`,它包含系统的所有声明式配置,如时区、语言、键盘布局、网络、用户、文件系统、启动项、桌面环境等等。
如果想要以可复现的方式修改系统的状态(这也是最推荐的方式),就需要手工修改
`/etc/nixos/configuration.nix` 文件,然后执行 `sudo nixos-rebuild switch` 命令来应用配置,
此命令会根据配置文件生成一个新的系统环境,并将新的环境设为默认环境。同时上一个系统环境会被
保留,而且会被加入到 grub 的启动项中,这确保了即使新的环境不能启动,也能随时回退到旧环境。
`/etc/nixos/configuration.nix` 文件,然后执行 `sudo nixos-rebuild switch`
命令来应用配置,此命令会根据配置文件生成一个新的系统环境,并将新的环境设为默认环境。同时上一个系统环境会被保留,而且会被加入到 grub 的启动项中,这确保了即使新的环境不能启动,也能随时回退到旧环境。
另一方面,`/etc/nixos/configuration.nix` 是传统的 Nix 配置方式,它依赖 `nix-channel` 配置
的数据源,也没有任何版本锁定机制,实际无法确保系统的可复现性。 **更推荐使用的是 Nix
另一方面,`/etc/nixos/configuration.nix` 是传统的 Nix 配置方式,它依赖 `nix-channel`
配置的数据源,也没有任何版本锁定机制,实际无法确保系统的可复现性。 **更推荐使用的是 Nix
Flakes**,它可以确保系统的可复现性,同时也可以很方便地管理系统的配置。
我们下面首先介绍下通过 NixOS 默认的配置方式来管理系统,然后再过渡到更先进的 Nix Flakes.
@@ -22,8 +20,8 @@ Flakes**,它可以确保系统的可复现性,同时也可以很方便地管
简单起见我们先使用这种方式来配置系统,后面会介绍 Flake 的使用。
比如要启用 ssh 并添加一个用户 ryan只需要在 `/etc/nixos/configuration.nix` 中添加如下配
置:
比如要启用 ssh 并添加一个用户 ryan只需要在 `/etc/nixos/configuration.nix`
中添加如下配置:
```nix{14-38}
# Edit this configuration file to define what should be installed on
@@ -71,8 +69,8 @@ Flakes**,它可以确保系统的可复现性,同时也可以很方便地管
这里我启用了 openssh 服务,为 ryan 用户添加了 ssh 公钥,并禁用了密码登录。
现在运行 `sudo nixos-rebuild switch` 部署修改后的配置,之后就可以通过 ssh 密钥远程登录到我
的这台主机了。
现在运行 `sudo nixos-rebuild switch`
部署修改后的配置,之后就可以通过 ssh 密钥远程登录到我的这台主机了。
> 如果你在部署配置时遇到了任何错误,都可以尝试在 `nixos-rebuild` 命令后面添加
> `--show-trace --print-build-logs --verbose` 参数来获取详细的错误信息。
@@ -82,8 +80,8 @@ Flakes**,它可以确保系统的可复现性,同时也可以很方便地管
`/etc/nixos/configuration.nix` 的所有配置项,可以在这几个地方查到:
- 直接 Google比如 `Chrome NixOS` 就能找到 Chrome 相关的配置项,一般 NixOS Wiki 或
nixpkgs 仓库源码的排名会比较靠前。
- 直接 Google比如 `Chrome NixOS` 就能找到 Chrome 相关的配置项,一般 NixOS
Wiki 或 nixpkgs 仓库源码的排名会比较靠前。
- 在 [NixOS Options Search](https://search.nixos.org/options) 中搜索关键字
- 系统级别的配置,可以考虑在
[Configuration - NixOS Manual](https://nixos.org/manual/nixos/unstable/index.html#ch-configuration)

View File

@@ -1,99 +1,96 @@
# Flakes 简介
Flakes 实验特性是 Nix 项目的一项重大进展,它引入了一种管理 Nix 表达式之间的依赖关系的策
略,提高了 Nix 生态系统中的可复现性、可组合性和可用性。虽然 Flakes 仍然是一个试验性的功
能,但已经被 Nix 社区广泛采用。[^1]
Flakes 实验特性是 Nix 项目的一项重大进展,它引入了一种管理 Nix 表达式之间的依赖关系的策略,提高了 Nix 生态系统中的可复现性、可组合性和可用性。虽然 Flakes 仍然是一个试验性的功能,但已经被 Nix 社区广泛采用。[^1]
Flakes 特性是 Nix 项目中最有意义的变化之一。[^2]
简单的说,如果你写过点 JavaScript/Go/Rust/Python那你应该对
`package.json`/`go.mod`/`Cargo.toml`/`pyproject.toml` 这些文件不陌生,在这些编程语言中,这
些文件用来描述软件包之间的依赖关系,以及如何构建项目。同样的,这些编程语言的包管理器还通过
`package-lock.json`/`go.sum`/`Cargo.lock`/`poetry.lock` 这些文件来锁定依赖的版本,以保证项
目的可复现性。
`package.json`/`go.mod`/`Cargo.toml`/`pyproject.toml`
这些文件不陌生,在这些编程语言中,这些文件用来描述软件包之间的依赖关系,以及如何构建项目。同样的,这些编程语言的包管理器还通过
`package-lock.json`/`go.sum`/`Cargo.lock`/`poetry.lock`
这些文件来锁定依赖的版本,以保证项目的可复现性。
Flakes 就是从上述这类编程语言的包管理器中借鉴了这种描述依赖关系与锁定依赖版本的思路,以提
高 Nix 生态系统中的可复现性、可组合性和可用性。
Flakes 就是从上述这类编程语言的包管理器中借鉴了这种描述依赖关系与锁定依赖版本的思路,以提高 Nix 生态系统中的可复现性、可组合性和可用性。
Flakes 提供了 `flake.nix`,它类似 `package.json`,用来描述 Nix 包之间的依赖关系,以及如何
构建项目。同时它还提供了 `flake.lock`,这是一个类似 `package-lock.json` 的文件,用来锁定依
赖的版本,以保证项目的可复现性。
Flakes 提供了 `flake.nix`,它类似
`package.json`,用来描述 Nix 包之间的依赖关系,以及如何构建项目。同时它还提供了
`flake.lock`,这是一个类似 `package-lock.json`
的文件,用来锁定依赖的版本,以保证项目的可复现性。
另一方面Flakes 实验特性并没有破坏 Nix 用户层面的原有设计,它新引入的
`flake.nix`/`flake.lock` 两个文件只是其他 Nix 配置的一个 Wrapper在后面的章节的学习中我们
将会看到Flakes 特性是在 Nix 原有设计的基础上提供了一种新的、更方便的管理 Nix 表达式之间
的依赖关系的方式。
`flake.nix`/`flake.lock`
两个文件只是其他 Nix 配置的一个 Wrapper在后面的章节的学习中我们将会看到Flakes 特性是在 Nix 原有设计的基础上提供了一种新的、更方便的管理 Nix 表达式之间的依赖关系的方式。
## 注意事项 <Badge type="danger" text="caution" />
Flakes 带来的好处是显而易见的,整个 NixOS 社区都很喜欢它,目前超过半数的用户已经在大量使用
Flakes[^3],因此我们可以相当确定 Flakes 不会被废弃。
Flakes 带来的好处是显而易见的,整个 NixOS 社区都很喜欢它,目前超过半数的用户已经在大量使用 Flakes[^3],因此我们可以相当确定 Flakes 不会被废弃。
:warning:但是 Flakes 目前仍然存在一些问题因此在将它推向稳定的过程中Nix 可能会引入一些
不兼容的改动,这个改动的大小目前还无法确定。
总的来说,我仍然推荐大家使用 Flakes毕竟这本书本身也是围绕 NixOS 与 Flakes 编写的,但是也
要做好准备——未来可能需要解决一些不兼容变更带来的问题。
:warning:但是 Flakes 目前仍然存在一些问题因此在将它推向稳定的过程中Nix 可能会引入一些不兼容的改动,这个改动的大小目前还无法确定。
总的来说,我仍然推荐大家使用 Flakes毕竟这本书本身也是围绕 NixOS 与 Flakes 编写的,但是也要做好准备——未来可能需要解决一些不兼容变更带来的问题。
## Flakes 何时会成为稳定特性? {#when-will-flakes-stablized}
我深入了解了下 Flakes 现状与未来计划相关的资料,大概有这些:
- [[RFC 0136] A Plan to Stabilize Flakes and the New CLI Incrementally](https://github.com/NixOS/rfcs/pull/136):
一份渐进式地将 Flakes 与 new CLI 两个实验性特性推向稳定的 RFC已 Merge.
- [CLI stabilization effort](https://github.com/NixOS/nix/issues/7701): 一份追踪 New CLI
稳定化工作进度的 Issue.
- [Why are flakes still experimental? - NixOS Discourse](https://discourse.nixos.org/t/why-are-flakes-still-experimental/29317):
最近的一次关于 Flakes 稳定性的讨论,可以看到大家的疑惑,以及社区对 Flakes 的态度。
- [[RFC 0136] A Plan to Stabilize Flakes and the New CLI Incrementally](https://github.com/NixOS/rfcs/pull/136): 一份渐进式地将 Flakes 与 new
CLI 两个实验性特性推向稳定的 RFC已 Merge.
- [CLI stabilization effort](https://github.com/NixOS/nix/issues/7701): 一份追踪 New
CLI 稳定化工作进度的 Issue.
- [Why are flakes still experimental? - NixOS Discourse](https://discourse.nixos.org/t/why-are-flakes-still-experimental/29317): 最近的一次关于 Flakes 稳定性的讨论,可以看到大家的疑惑,以及社区对 Flakes 的态度。
- [Flakes are such an obviously good thing - Graham Christensen](https://grahamc.com/blog/flakes-are-an-obviously-good-thing/):
NixOS 社区成员的文章,记录了他对 Flakes 的看法,以及对社区当初添加 Flakes 特性时的不当举
措的懊悔
- [ teaching Nix 3 CLI and Flakes #281 - nix.dev](https://github.com/NixOS/nix.dev/issues/281):
社区关于是否应该在 NixOS 官方文档中介绍 Flakes 的讨论,当前结论是官方文档不应该推广使用
unstable 功能。
NixOS 社区成员的文章,记录了他对 Flakes 的看法,以及对社区当初添加 Flakes 特性时的不当举措的懊悔。
- [ teaching Nix 3 CLI and Flakes #281 - nix.dev](https://github.com/NixOS/nix.dev/issues/281): 社区关于是否应该在 NixOS 官方文档中介绍 Flakes 的讨论,当前结论是官方文档不应该推广使用 unstable 功能
读完上述内容后,个人猜测,**Flakes 有可能(仅是可能)会在未来两年内成为稳定特性**。
## Nix 的新 CLI 与旧的 CLI {#the-new-cli-and-the-classic-cli}
Nix 于 2020 年推出了 `nix-command` & `flakes` 两个实验特性,它们提供了全新的命令行工具(即
New CLI、标准的 Nix 包结构定义(即 Flakes 特性)、类似 cargo/npm 的 `flake.lock` 版本锁
文件等等。这两个特性极大地增强了 Nix 的能力因此虽然至今2024/2/1它们仍然是实验性特
性,但是已经被 Nix 社区广泛使用。
Nix 于 2020 年推出了 `nix-command` & `flakes`
两个实验特性,它们提供了全新的命令行工具(即 New
CLI、标准的 Nix 包结构定义(即 Flakes 特性)、类似 cargo/npm 的 `flake.lock`
版本锁文件等等。这两个特性极大地增强了 Nix 的能力因此虽然至今2024/2/1它们仍然是实验性特性,但是已经被 Nix 社区广泛使用。
当前 Nix 的 New CLI`nix-command` 实验特性) 与 Flakes 实验特性是强绑定的关系,虽然现
在已经有明确的拆分计划正在推进中了,但要用 Flakes 基本上就必须得用 New CLI. 而本书作为一本
NixOS & Flakes 新手指南,就有必要介绍下 Flakes 实验特性所依赖的 New CLI 与旧的 CLI 的区
别。
当前 Nix 的 New CLI`nix-command`
实验特性) 与 Flakes 实验特性是强绑定的关系,虽然现在已经有明确的拆分计划正在推进中了,但要用 Flakes 基本上就必须得用 New
CLI. 而本书作为一本 NixOS & Flakes 新手指南,就有必要介绍下 Flakes 实验特性所依赖的 New
CLI 与旧的 CLI 的区别。
这里列举下在启用了 New CLI 与 Flakes(`nix-command` & `flakes`) 实验特性后,已经不需要用到
的旧的 Nix 命令行工具与相关概念。在查找资料时,如果看到它们直接忽略掉就行
`nix-collect-garbage` 除外,该命令目前暂无替代):
这里列举下在启用了 New CLI 与 Flakes(`nix-command` &
`flakes`) 实验特性后,已经不需要用到的旧的 Nix 命令行工具与相关概念。在查找资料时,如果看到它们直接忽略掉就行`nix-collect-garbage`
除外,该命令目前暂无替代):
1. `nix-channel`: 与 apt/yum/pacman 等其他 Linux 发行版的包管理工具类似,`nix-channel` 通过 stable/unstable channels如 apt/yum/管理诸如 nixpkgs 等 `inputs` 的版本。传统上,这为 Nix 语言提供了 `<nixpkgs>` 的引用来源。
1. 在 Flakes 中,`nix-channel` 的功能被 Flake 注册表(`nix registry`)取代,用于为交互式命令行(例如 `nix run nixpkgs#hello`)提供「全局的默认 nixpkgs」。当使用 `flake.nix` 时,`inputs` 的版本由 flake 自己管理。
2. Flakes 通过 `flake.nix` 中的 `inputs` 部分管理每个 Flake 中 nixpkgs 及其他 `inputs` 的版本,而非依赖全局状态
2. `nix-env`: 用于管理用户环境的软件包,是传统 Nix 的核心命令行工具。它从 `nix-channel`
义的数据源中安装软件包,所以安装的软件包版本受 channel 影响。
1. 通过 `nix-env` 安装的包不会被自动记录到 Nix 的声明式配置中,是完全脱离掌控的,无法在
其他主机上复现,且升级由 `nix-env` 安装的包时可能因未保存属性名产生不可预测的结果,因此不推荐使用
1. `nix-channel`: 与 apt/yum/pacman 等其他 Linux 发行版的包管理工具类似,`nix-channel`
通过 stable/unstable channels如 apt/yum/管理诸如 nixpkgs 等 `inputs`
的版本。传统上,这为 Nix 语言提供了 `<nixpkgs>` 的引用来源
1. 在 Flakes 中,`nix-channel`
的功能被 Flake 注册表(`nix registry`)取代,用于为交互式命令行(例如
`nix run nixpkgs#hello`)提供「全局的默认 nixpkgs」。当使用 `flake.nix` 时,`inputs`
的版本由 flake 自己管理
2. Flakes 通过 `flake.nix` 中的 `inputs` 部分管理每个 Flake 中 nixpkgs 及其他 `inputs`
的版本,而非依赖全局状态。
2. `nix-env`: 用于管理用户环境的软件包,是传统 Nix 的核心命令行工具。它从 `nix-channel`
定义的数据源中安装软件包,所以安装的软件包版本受 channel 影响。
1. 通过 `nix-env`
安装的包不会被自动记录到 Nix 的声明式配置中,是完全脱离掌控的,无法在其他主机上复现,且升级由
`nix-env` 安装的包时可能因未保存属性名产生不可预测的结果,因此不推荐使用。
2. New CLI 中对应的命令为 `nix profile`,我个人不太推荐初学者直接尝试它.
3. `nix-shell`: nix-shell 用于创建一个临时的 shell 环境
1. 这玩意儿可能有点复杂了,因此在 New CLI 中它被拆分成了三个子命令 `nix develop`,
`nix shell` 以及 `nix run`,我们会在「[构建开发环境](../development/intro.md)」一章
详细介绍这三个命令。
4. `nix-build`: 用于构建 Nix 包,它会将构建结果放到 `/nix/store` 路径下,但是不会记录到
Nix 的声明式配置中。
`nix shell` 以及
`nix run`,我们会在「[构建开发环境](../development/intro.md)」一章详细介绍这三个命令。
4. `nix-build`: 用于构建 Nix 包,它会将构建结果放到 `/nix/store`
路径下,但是不会记录到 Nix 的声明式配置中。
1. 在 New CLI 中对应的命令为 `nix build`
5. `nix-collect-garbage`: 垃圾回收指令,用于清理 `/nix/store` 中未被使用的 Store Objects.
1. 在 New CLI 中有个相似的指令 `nix store gc --debug`,但它不会清理 profile 生成的历史
版本,因此此命令暂无替代。
1. 在 New CLI 中有个相似的指令
`nix store gc --debug`,但它不会清理 profile 生成的历史版本,因此此命令暂无替代。
6. 以及其他使用地较少的命令,就不一一列出了.
1. 详细的命令对比列表可以看看
[Try to explain nix commands](https://qiita-com.translate.goog/Sumi-Sumi/items/6de9ee7aab10bc0dbead?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en)
[^1]: [Flakes - NixOS Wiki](https://wiki.nixos.org/wiki/Flakes)
[^2]:
[Flakes are such an obviously good thing](https://grahamc.com/blog/flakes-are-an-obviously-good-thing/)

View File

@@ -13,39 +13,38 @@ $ tree
下面分别说明下这四个文件的功能:
- `flake.lock`: 自动生成的版本锁文件,它记录了整个 flake 所有输入的数据源、hash 值、版本
号,确保系统可复现。
- `flake.lock`: 自动生成的版本锁文件,它记录了整个 flake 所有输入的数据源、hash 值、版本号,确保系统可复现。
- `flake.nix`: flake 的入口文件,执行 `sudo nixos-rebuild switch` 时会识别并部署它。
- `configuration.nix`: 在 flake.nix 中被作为系统模块导入,目前所有系统级别的配置都写在此文
件中。
- `configuration.nix`: 在 flake.nix 中被作为系统模块导入,目前所有系统级别的配置都写在此文件中。
- 此配置文件中的所有配置项,参见官方文档
[Configuration - NixOS Manual](https://nixos.org/manual/nixos/unstable/index.html#ch-configuration)
- `home.nix`: 在 flake.nix 中被 home-manager 作为 ryan 用户的配置导入,也就是说它包含了
ryan 这个用户的所有 Home Manager 配置,负责管理其 Home 文件夹。
- `home.nix`: 在 flake.nix 中被 home-manager 作为 ryan 用户的配置导入,也就是说它包含了 ryan 这个用户的所有 Home
Manager 配置,负责管理其 Home 文件夹。
- 此配置文件的所有配置项,参见
[Appendix A. Configuration Options - Home Manager](https://nix-community.github.io/home-manager/options.xhtml)
通过修改上面几个配置文件就可以实现对系统与 Home 目录状态的修改。但是随着配置的增多,单纯依
`configuration.nix``home.nix` 会导致配置文件臃肿,难以维护。更好的解决方案是通过
Nix 的模块机制,将配置文件拆分成多个模块,分门别类地编写维护。
通过修改上面几个配置文件就可以实现对系统与 Home 目录状态的修改。但是随着配置的增多,单纯依
`configuration.nix``home.nix`
会导致配置文件臃肿,难以维护。更好的解决方案是通过 Nix 的模块机制,将配置文件拆分成多个模块,分门别类地编写维护。
Nix 语言提供了一个 [`import` 函数](https://nix.dev/tutorials/nix-language.html#import),它有一条特殊规则:
Nix 语言提供了一个
[`import` 函数](https://nix.dev/tutorials/nix-language.html#import),它有一条特殊规则:
> `import` 的参数如果为文件夹路径,那么会返回该文件夹下的 `default.nix` 文件的执行结果
Nixpkgs 模块系统提供了一个与之类似的 `imports` 参数,它可以接受一个 `.nix` 文件列表,并将
该列表中的所有配置**合并**Merge到当前的 attribute set 中。
Nixpkgs 模块系统提供了一个与之类似的 `imports` 参数,它可以接受一个 `.nix`
文件列表,并将该列表中的所有配置**合并**Merge到当前的 attribute set 中。
注意这里的用词是「**合并**」,它表明 `imports` 如果遇到重复的配置项,不会简单地按执行顺序互相
覆盖,而是更合理地处理。比如说我在多个 modules 中都定义了 `program.packages = [...]`,那么
`imports` 会将所有 modules 中的 `program.packages` 这个 list 合并。不仅 list 能被正确合
attribute set 能被正确合并,具体行为各位看官可以自行探索。
注意这里的用词是「**合并**」,它表明 `imports`
如果遇到重复的配置项,不会简单地按执行顺序互相覆盖,而是更合理地处理。比如说我在多个 modules 中都定义了
`program.packages = [...]`,那么 `imports` 会将所有 modules 中的 `program.packages`
这个 list 合并。不仅 list 能被正确合并,attribute
set 也能被正确合并,具体行为各位看官可以自行探索。
> 我只在
> [nixpkgs-unstable 官方手册 - evalModules parameters](https://nixos.org/manual/nixpkgs/unstable/#module-system-lib-evalModules-parameters)
> 中找到一句关于 `imports` 的描
> 述:`A list of modules. These are merged together to form the final configuration.`,可
> 以意会一下...Nix 的文档真的一言难尽...这么核心的参数文档就这么一句...
> 中找到一句关于 `imports`
> 的描述:`A list of modules. These are merged together to form the final configuration.`,可以意会一下...Nix 的文档真的一言难尽...这么核心的参数文档就这么一句...
我们可以借助 `imports` 参数,将 `home.nix``configuration.nix` 拆分成多个 `.nix` 文件。
@@ -109,16 +108,17 @@ Nixpkgs 模块系统提供了一个与之类似的 `imports` 参数,它可以
└── wallpaper.jpg # 桌面壁纸,在 i3wm 配置中被引用
```
Nix Flakes 对目录结构没有任何要求,你可以参考上面的例子,摸索出适合你自己的目录结构。其中
关键点就是通过 `imports` 参数导入其他 `.nix` 文件。
Nix
Flakes 对目录结构没有任何要求,你可以参考上面的例子,摸索出适合你自己的目录结构。其中关键点就是通过
`imports` 参数导入其他 `.nix` 文件。
## `lib.mkOverride`, `lib.mkDefault` and `lib.mkForce`
你可能会发现有些人在 Nix 文件中使用 `lib.mkDefault` `lib.mkForce` 来定义值,顾名思
义,`lib.mkDefault``lib.mkForce` 用于设置选项的默认值,或者强制设置选项的值。
你可能会发现有些人在 Nix 文件中使用 `lib.mkDefault` `lib.mkForce`
来定义值,顾名思义,`lib.mkDefault``lib.mkForce`
用于设置选项的默认值,或者强制设置选项的值。
直接这么说可能不太好理解,官方文档也没啥对这几个函数的详细解释,最直接的理解方法,是看源
码。
直接这么说可能不太好理解,官方文档也没啥对这几个函数的详细解释,最直接的理解方法,是看源码。
开个新窗口,输入命令 `nix repl -f '<nixpkgs>'` 进入 REPL 解释器,然后输入
`:e lib.mkDefault`,就可以看到 `lib.mkDefault` 的源码了(不太懂 `:e` 是干啥的?请直接输入
@@ -143,18 +143,17 @@ Nix Flakes 对目录结构没有任何要求,你可以参考上面的例子,
# ......
```
所以 `lib.mkDefault` 就是用于设置选项的默认值,它的优先级是 1000`lib.mkForce` 则用于
强制设置选项的值,它的优先级是 50。如果你直接设置选项的值那么它的优先级就是 1000
所以 `lib.mkDefault` 就是用于设置选项的默认值,它的优先级是 1000`lib.mkForce`
则用于强制设置选项的值,它的优先级是 50。如果你直接设置选项的值那么它的优先级就是 1000
`lib.mkDefault` 一样)。
`priority` 的值越低,它实际的优先级就越高,所以 `lib.mkForce` 的优先级比 `lib.mkDefault`
高。而如果你定义了多个优先级相同的值Nix 会报错说存在参数冲突,需要你手动解决。
这几个函数在模块化 NixOS 配置中非常有用因为你可以在低层级的模块base module中设置默认
然后在高层级的模块high-level module中设置优先级更高的值。
这几个函数在模块化 NixOS 配置中非常有用因为你可以在低层级的模块base
module中设置默认然后在高层级的模块high-level module中设置优先级更高的值。
举个例子,我在这里定义了一个默认
值:<https://github.com/ryan4yin/nix-config/blob/c515ea9/modules/nixos/core-server.nix#L32>
举个例子,我在这里定义了一个默认值:<https://github.com/ryan4yin/nix-config/blob/c515ea9/modules/nixos/core-server.nix#L32>
```nix{6}
{ lib, pkgs, ... }:
@@ -192,14 +191,12 @@ Nix Flakes 对目录结构没有任何要求,你可以参考上面的例子,
`lib.mkBefore` 跟 `lib.mkAfter` 用于设置**列表类型**的合并顺序,它们跟 `lib.mkDefault` 和
`lib.mkForce` 一样,也被用于模块化配置。
> 列表类型的定义我没找到官方文档,但我简单理解,应该就是合并结果与合并先后顺序有关的类型。
> 按这个理解list 跟 string 类型都是列表类型,实际测试这几个函数也确实能用在这两个类型
> 上。
> 列表类型的定义我没找到官方文档,但我简单理解,应该就是合并结果与合并先后顺序有关的类型。按这个理解list 跟 string 类型都是列表类型,实际测试这几个函数也确实能用在这两个类型上。
前面说了如果你定义了多个优先级相同的值Nix 会报错说存在参数冲突,需要你手动解决。
但是如果你定义的是**列表类型**的值Nix 就不会报错了,因为 Nix 会把你定义的多个值合并成一
个列表,而 `lib.mkBefore` 跟 `lib.mkAfter` 就是用于设置这个列表的合并顺序的。
但是如果你定义的是**列表类型**的值Nix 就不会报错了,因为 Nix 会把你定义的多个值合并成一个列表,而
`lib.mkBefore` 跟 `lib.mkAfter` 就是用于设置这个列表的合并顺序的。
还是先来看看源码,开个终端键入 `nix repl -f '<nixpkgs>'` 进入 REPL 解释器,然后输入
`:e lib.mkBefore`,就可以看到 `lib.mkBefore` 的源码了(不太懂 `:e` 是干啥的?请直接输入

View File

@@ -2,8 +2,8 @@
## Nixpkgs Module 结构的简单介绍 {#simple-introduction-to-nixpkgs-module-structure}
> 在后面的 [模块化 NixOS 配置](./modularize-the-configuration.md) 一节中会详细介绍这套模块
> 系统的工作方式,这里只介绍些基础知识。
> 在后面的 [模块化 NixOS 配置](./modularize-the-configuration.md)
> 一节中会详细介绍这套模块系统的工作方式,这里只介绍些基础知识。
为什么 `/etc/nixos/configuration.nix` 这个配置文件会符合 Nixpkgs Module 定义,从而能直接在
`flake.nix` 中引用它呢?可能会有读者觉得这有点出乎意料。
@@ -11,15 +11,13 @@
要理解这一点,我们得先了解下 Nixpkgs 模块系统的由来以及它的用途。
NixOS 的所有实现代码都存放在
[Nixpkgs/nixos](https://github.com/NixOS/nixpkgs/tree/master/nixos) 目录中,这些源码大都使
用 Nix 语言编写。为了编写维护如此多的 Nix 代码,并且使用户能灵活地自定义其 NixOS 系统的各
项功能,就必须要有一套 Nix 代码的模块化系统。
[Nixpkgs/nixos](https://github.com/NixOS/nixpkgs/tree/master/nixos)
目录中,这些源码大都使用 Nix 语言编写。为了编写维护如此多的 Nix 代码,并且使用户能灵活地自定义其 NixOS 系统的各项功能,就必须要有一套 Nix 代码的模块化系统。
这套 Nix 代码的模块系统的实现也同样存放在 Nixpkgs 仓库中,它主要被用于 NixOS 系统配置的模块
化,但也有其他的应用,比如 nix-darwin 跟 home-manager 都大量使用了这套模块系统。
这套 Nix 代码的模块系统的实现也同样存放在 Nixpkgs 仓库中,它主要被用于 NixOS 系统配置的模块化,但也有其他的应用,比如 nix-darwin 跟 home-manager 都大量使用了这套模块系统。
既然 NixOS 是基于这套模块系统构建的,那它的配置文件(包括 `/etc/nixos/configuration.nix`
是一个 Nixpkgs Module也就显得非常自然了。
既然 NixOS 是基于这套模块系统构建的,那它的配置文件(包括
`/etc/nixos/configuration.nix`是一个 Nixpkgs Module也就显得非常自然了。
在学习后面的内容之前,我们需要先简单了解下这套模块系统的工作方式。
@@ -40,8 +38,7 @@ NixOS 的所有实现代码都存放在
}
```
可以看到它的定义实际是一个 Nix 函数,该函数有 5 个**由模块系统自动生成、自动注入、无需额外
声明的参数**
可以看到它的定义实际是一个 Nix 函数,该函数有 5 个**由模块系统自动生成、自动注入、无需额外声明的参数**
1. `lib`: **nixpkgs 自带的函数库,提供了许多操作 Nix 表达式的实用函数**
- 详见 <https://nixos.org/manual/nixpkgs/stable/#id-1.4>
@@ -55,8 +52,8 @@ NixOS 的所有实现代码都存放在
- 它在
[nixpkgs/nixos/lib/eval-config-minimal.nix#L43](https://github.com/NixOS/nixpkgs/blob/nixos-25.05/nixos/lib/eval-config-minimal.nix#L43)
中被定义
- 通常被用于导入一些额外的 NixOS 模块NixOS 自动生成的 `hardware-configuration.nix`
基本都能看到它
- 通常被用于导入一些额外的 NixOS 模块NixOS 自动生成的 `hardware-configuration.nix`
基本都能看到它
## 传递非默认参数到模块系统中 {#pass-non-default-parameters-to-submodules}
@@ -78,20 +75,22 @@ Nixpkgs 的模块系统提供了两种方式来传递非默认参数:
[Appendix A. Configuration Options](https://nixos.org/manual/nixos/stable/options#opt-_module.args)
- Source Code: [nixpkgs/nixos-25.05/lib/modules.nix - _module.args]
总之,`specialArgs``_module.args` 需要的值都是一个 attribute set它们的功能也相同
是将其 attribute set 中的所有参数传递到所有子模块中。这两者的区别在于:
总之,`specialArgs``_module.args` 需要的值都是一个 attribute
set它们的功能也相同是将其 attribute
set 中的所有参数传递到所有子模块中。这两者的区别在于:
1. 在任何 Module 中都能使用 `_module.args` 这个 option通过它互相传递参数这要比只能在
`nixpkgs.lib.nixosSystem` 函数中使用的 `specialArgs` 更灵活。
1. `_module.args` 是在 Module 中声明使用的,因此必须在所有 Modules 都已经被求值后,才能使
用它。这导致**如果你在 `imports = [ ... ];` 中使用 `_module.args` 传递的参数,会报错
1. `_module.args`
是在 Module 中声明使用的,因此必须在所有 Modules 都已经被求值后,才能使用它。这导致**如果你在
`imports = [ ... ];` 中使用 `_module.args` 传递的参数,会报错
`infinite recursion`,这种场景下你必须改用 `specialArgs` 才行**。
我个人更喜欢 `specialArgs`,因为它更简单直接,用起来顺手些,另外 `_xxx` 这种命名风格就让人
感觉它是个内部用的东西,不太适合用在用户配置文件中。
我个人更喜欢 `specialArgs`,因为它更简单直接,用起来顺手些,另外 `_xxx`
这种命名风格就让人感觉它是个内部用的东西,不太适合用在用户配置文件中。
假设你想将某个依赖项传递到子模块中使用,可以使用 `specialArgs` 参数将 `inputs` 传递到所有
子模块中:
假设你想将某个依赖项传递到子模块中使用,可以使用 `specialArgs` 参数将 `inputs`
传递到所有子模块中:
```nix{13}
{
@@ -141,8 +140,8 @@ Nixpkgs 的模块系统提供了两种方式来传递非默认参数:
```
选择上述两种方式之一修改你的配置,然后在 `/etc/nixos/configuration.nix` 中就可以使用
`inputs` 这个参数了,模块系统会自动匹配到 `specialArgs` 中定义的 `inputs`,并将其注入到所
有需要该参数的子模块中:
`inputs` 这个参数了,模块系统会自动匹配到 `specialArgs` 中定义的
`inputs`,并将其注入到所有需要该参数的子模块中:
```nix{3}
# Nix 会通过名称匹配,
@@ -162,11 +161,10 @@ Nixpkgs 的模块系统提供了两种方式来传递非默认参数:
管系统最常见的需求就是装软件,我们在上一节已经见识过如何通过 `environment.systemPackages`
来安装 `pkgs` 中的包,这些包都来自官方的 nixpkgs 仓库。
现在我们学习下如何安装其他 flake 来源的软件包,这比直接从 nixpkgs 安装要灵活很多,最主要的
用途是安装 Nixpkgs 中还未添加或未更新的某软件的最新版本。
现在我们学习下如何安装其他 flake 来源的软件包,这比直接从 nixpkgs 安装要灵活很多,最主要的用途是安装 Nixpkgs 中还未添加或未更新的某软件的最新版本。
以 [helix](https://github.com/helix-editor/helix) 编辑器为例,这里演示下如何直接编译安装
helix 的 master 分支。
以 [helix](https://github.com/helix-editor/helix)
编辑器为例,这里演示下如何直接编译安装 helix 的 master 分支。
首先在 `flake.nix` 中添加 helix 这个 inputs 数据源:
@@ -213,38 +211,32 @@ helix 的 master 分支。
}
```
改好后再 `sudo nixos-rebuild switch` 部署,就能安装好 Helix 程序了。这次部署用时会比以往长
挺多,因为 Nix 会从源码编译整个 Helix 程序。
改好后再 `sudo nixos-rebuild switch`
部署,就能安装好 Helix 程序了。这次部署用时会比以往长挺多,因为 Nix 会从源码编译整个 Helix 程序。
部署完毕后,可直接在终端使用 `hx` 命令测试验证。
另外,如果你只是想尝试一下 Helix 的最新版本,再决定要不要真正地将它安装到系统里,有更简单
的办法,一行命令就行(但如前所述,源码编译会很费时间):
另外,如果你只是想尝试一下 Helix 的最新版本,再决定要不要真正地将它安装到系统里,有更简单的办法,一行命令就行(但如前所述,源码编译会很费时间):
```bash
nix run github:helix-editor/helix/master
```
我们会在后面的 [新一代 Nix 命令行工具的使用](../other-usage-of-flakes/the-new-cli.md) 中详
细介绍 `nix run` 的用法。
我们会在后面的 [新一代 Nix 命令行工具的使用](../other-usage-of-flakes/the-new-cli.md)
中详细介绍 `nix run` 的用法。
## 使用其他 Flakes 包提供的功能
其实这才是 Flakes 最主要的功能,一个 Flake 可以依赖其他 Flakes从而使用它们提供的功能——就
如同我们在写 TypeScript/Go/Rust 等程序时使用其他 Library 提供的功能一样。
其实这才是 Flakes 最主要的功能,一个 Flake 可以依赖其他 Flakes从而使用它们提供的功能——就如同我们在写 TypeScript/Go/Rust 等程序时使用其他 Library 提供的功能一样。
上面使用 Helix 的官方 Flake 中提供的最新版本就是一个例子,其他更多的用例会在后面提到,这里
引用几个后面会讲的例子:
上面使用 Helix 的官方 Flake 中提供的最新版本就是一个例子,其他更多的用例会在后面提到,这里引用几个后面会讲的例子:
- [Getting Started with Home Manager](./start-using-home-manager.md): 这里引入了社区的
Home-Manager 作为依赖项,从而能直接使用该 Flake 提供的功能
- [Downgrading or Upgrading Packages](./downgrade-or-upgrade-packages.md): 这里引入了不同
版本的 Nixpkgs 作为依赖项,从而能很灵活地选用不同版本的 Nixpkgs 中的包。
- [Getting Started with Home Manager](./start-using-home-manager.md): 这里引入了社区的 Home-Manager 作为依赖项,从而能直接使用该 Flake 提供的功能。
- [Downgrading or Upgrading Packages](./downgrade-or-upgrade-packages.md): 这里引入了不同版本的 Nixpkgs 作为依赖项,从而能很灵活地选用不同版本的 Nixpkgs 中的包
## 其他 Flakes 学习资料
到此为止,我们已经学习了如何使用 Flakes 来配置 NixOS 系统。如果你对 Flakes 还有更多的疑
问,或者想深入学习,请直接参考如下官方/半官方的文档。
到此为止,我们已经学习了如何使用 Flakes 来配置 NixOS 系统。如果你对 Flakes 还有更多的疑问,或者想深入学习,请直接参考如下官方/半官方的文档。
- Nix Flakes 的官方文档:
- [Nix flakes - Nix Manual](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake)

View File

@@ -1,12 +1,13 @@
# `flake.nix` 配置详解 {#flake-nix-configuration-explained}
上面我们创建了一个 `flake.nix` 文件并通过它来管理系统配置,但你对它的结构还是一头雾水,下
面我们来详细解释一下这个文件的内容。
上面我们创建了一个 `flake.nix`
文件并通过它来管理系统配置,但你对它的结构还是一头雾水,下面我们来详细解释一下这个文件的内容。
### 1. flake inputs
首先看看其中的 `inputs` 属性,它是一个 attribute set其中定义了这个 flake 的所有依赖项,
这些依赖项会在被拉取后,作为参数传递给 `outputs` 函数:
首先看看其中的 `inputs` 属性,它是一个 attribute
set其中定义了这个 flake 的所有依赖项,这些依赖项会在被拉取后,作为参数传递给 `outputs`
函数:
```nix{2-5,7}
{
@@ -21,21 +22,22 @@
}
```
`inputs` 中的每一项依赖有许多类型与定义方式,可以是另一个 flake也可以是一个普通的 Git 仓
库,又或者一个本地路径。
[Flakes 的其他玩法 - Flake 的 inputs](../other-usage-of-flakes/inputs.md) 中详细介绍了常见
的依赖项类型与定义方式。
`inputs`
中的每一项依赖有许多类型与定义方式,可以是另一个 flake也可以是一个普通的 Git 仓库,又或者一个本地路径。
[Flakes 的其他玩法 - Flake 的 inputs](../other-usage-of-flakes/inputs.md)
中详细介绍了常见的依赖项类型与定义方式。
这里我们只定义了 `nixpkgs` 这一个依赖项,使用的是 flake 中最常见的引用方式,即
`github:owner/name/reference`,这里的 `reference` 可以是分支名、commit-id 或 tag。
`nixpkgs` 在 `inputs` 中被定义后,就可以在后面的 `outputs` 函数的参数中使用此依赖项中的内
容了,我们的示例中正是这么干的。
`nixpkgs` 在 `inputs` 中被定义后,就可以在后面的 `outputs`
函数的参数中使用此依赖项中的内容了,我们的示例中正是这么干的。
### 2. flake outputs
再来看看 `outputs`,它是一个以 `inputs` 中的依赖项为参数的函数,函数的返回值是一个
attribute set这个返回的 attribute set 即为该 flake 的构建结果:
再来看看 `outputs`,它是一个以 `inputs`
中的依赖项为参数的函数,函数的返回值是一个 attribute set这个返回的 attribute
set 即为该 flake 的构建结果:
```nix{10-18}
{
@@ -58,13 +60,13 @@ attribute set这个返回的 attribute set 即为该 flake 的构建结果:
}
```
flake 有很多的用途,也可以有很多不同类型的
outputs[Flake 的 outputs](../other-usage-of-flakes/outputs.md) 一节有更详细的介绍。这里
我们只用到了 `nixosConfigurations` 这一类型的 outputs它用于配置 NixOS 系统。
flake 有很多的用途,也可以有很多不同类型的 outputs[Flake 的 outputs](../other-usage-of-flakes/outputs.md)
一节有更详细的介绍。这里我们只用到了 `nixosConfigurations`
这一类型的 outputs它用于配置 NixOS 系统。
在我们运行 `sudo nixos-rebuild switch` 命令时,它会从 `/etc/nixos/flake.nix` 的 `outputs`
函数返回值中查找 `nixosConfigurations.my-nixos` (其中的 `my-nixos` 将会是你当前系统的
hostname这一属性并使用其中的定义来配置你的 NixOS 系统。
函数返回值中查找 `nixosConfigurations.my-nixos` (其中的 `my-nixos`
将会是你当前系统的 hostname这一属性并使用其中的定义来配置你的 NixOS 系统。
实际我们也可以自定义 flake 的位置与 NixOS 配置的名称,而不是使用默认值。只需要在
`nixos-rebuild` 命令后面添加 `--flake` 参数即可,一个例子:
@@ -76,8 +78,8 @@ sudo nixos-rebuild switch --flake /path/to/your/flake#your-hostname
上述命令中的 `--flake /path/to/your/flake#your-hostname` 参数简要说明如下:
1. `/path/to/your/flake` 为目标 flake 的位置,默认会使用 `/etc/nixos/` 这个路径。
2. `#` 是一个分隔符,其后的 `your-hostname` 则是 NixOS 配置的名称。`nixos-rebuild` 默认会
以你当前系统的 hostname 为配置名称进行查找。
2. `#` 是一个分隔符,其后的 `your-hostname` 则是 NixOS 配置的名称。`nixos-rebuild`
默认会以你当前系统的 hostname 为配置名称进行查找。
你甚至能直接引用一个远程的 GitHub 仓库作为你的 flake 来源,示例如下:
@@ -94,24 +96,24 @@ sudo nixos-rebuild switch --flake github:owner/repo#your-hostname
> The special input named `self` refers to the outputs and source tree of this flake.
所以说 `self` 是当前 flake 的 `outputs` 函数的返回值,同时也是当前 flake 源码的文件夹路径
source tree
所以说 `self` 是当前 flake 的 `outputs`
函数的返回值,同时也是当前 flake 源码的文件夹路径source tree
这里我们并未使用到 `self` 这个参数,在后面一些更复杂的例子(或者你网上搜
到的一些配置)中,我们会看到 `self` 的用法。
这里我们并未使用到 `self`
这个参数,在后面一些更复杂的例子(或者你网上搜到的一些配置)中,我们会看到 `self` 的用法。
> 注意:你可能会在一些代码中看到,有人会使用 `self.outputs` 来引用当前 flake 的输出,这
> 确实是可行的,但 Nix Manual 并未对其做任何说明,属于是 flake 的内部实现细节,不建议在
> 你自己的代码中使用!
> 注意:你可能会在一些代码中看到,有人会使用 `self.outputs`
> 来引用当前 flake 的输出,这确实是可行的,但 Nix
> Manual 并未对其做任何说明,属于是 flake 的内部实现细节,不建议在你自己的代码中使用!
### 4. `nixpkgs.lib.nixosSystem` 函数的简单介绍 {#simple-introduction-to-nixpkgs-lib-nixos-system}
**一个 Flake 可以依赖其他 Flakes从而使用它们提供的功能**。
默认情况下,一个 flake 会在其每个依赖项(即 `inputs` 中的每一项)的根目录下寻找
`flake.nix` 文件并**懒惰求值**lazy evaluation它们的 `outputs` 函数,接着将这些函数返回
的 attribute sets 作为参数传递给它自身的 `outputs` 函数,这样我们就能在当前 flake 中使用它
所依赖的其他 flakes 提供的功能了。
`flake.nix` 文件并**懒惰求值**lazy evaluation它们的 `outputs`
函数,接着将这些函数返回的 attribute sets 作为参数传递给它自身的 `outputs`
函数,这样我们就能在当前 flake 中使用它所依赖的其他 flakes 提供的功能了。
更精确地说,对每个依赖项的 `outputs` 函数的求值都是懒惰lazy也就是说一个 flake 的
`outputs` 函数只有在被真正使用到的时候才会被求值,这样就能避免不必要的计算,从而提高效率。
@@ -119,8 +121,8 @@ sudo nixos-rebuild switch --flake github:owner/repo#your-hostname
上面的描述可能有点绕,我们还是结合本节中使用的 `flake.nix` 示例来看看这个过程。我们的
`flake.nix` 声明了 `inputs.nixpkgs` 这个依赖项,因此 [nixpkgs/flake.nix] 会在我们执行
`sudo nixos-rebuild switch` 这个命令时被求值。从 Nixpkgs 仓库的源码中能看到它的 flake
outputs 定义中有返回 `lib` 这个属性,我们的例子中就使用了 `lib` 属性中的 `nixosSystem`
个函数来配置我们的 NixOS 系统:
outputs 定义中有返回 `lib` 这个属性,我们的例子中就使用了 `lib` 属性中的 `nixosSystem`
个函数来配置我们的 NixOS 系统:
```nix{8-13}
{
@@ -140,21 +142,20 @@ outputs 定义中有返回 `lib` 这个属性,我们的例子中就使用了 `
}
```
`nixpkgs.lib.nixosSystem` 后面跟的 attribute set 就是该函数的参数,我们这里只设置了两个参
数:
`nixpkgs.lib.nixosSystem` 后面跟的 attribute
set 就是该函数的参数,我们这里只设置了两个参数:
1. `system`: 这个很好懂,就是系统架构参数。
2. `modules`: 它是一个 modules 的列表NixOS 的实际系统配置都定义在这些 modules 中。
`/etc/nixos/configuration.nix` 这个配置文件本身就是一个 Nixpkgs Module因此可以直接将其添
加到 `modules` 列表中使用。
`/etc/nixos/configuration.nix` 这个配置文件本身就是一个 Nixpkgs
Module因此可以直接将其添加到 `modules` 列表中使用。
新手阶段了解这些就足够了,探究 `nixpkgs.lib.nixosSystem` 函数的具体实现需要对 Nixpkgs 的模
块系统有一定的了解。读者可以在学习了
新手阶段了解这些就足够了,探究 `nixpkgs.lib.nixosSystem`
函数的具体实现需要对 Nixpkgs 的模块系统有一定的了解。读者可以在学习了
[模块化 NixOS 配置](./modularize-the-configuration.md) 一节后,再回过头来从
[nixpkgs/flake.nix] 中找到 `nixpkgs.lib.nixosSystem` 的定义,跟踪它的源码,研究其实现方
式。
[nixpkgs/flake.nix] 中找到 `nixpkgs.lib.nixosSystem`
的定义,跟踪它的源码,研究其实现方式。
[nix flake - Nix Manual]:
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-inputs
@@ -167,5 +168,3 @@ outputs 定义中有返回 `lib` 这个属性,我们的例子中就使用了 `
https://github.com/NixOS/nixpkgs/blob/nixos-25.05/lib/modules.nix#L122-L184
[nixpkgs/nixos-25.05/nixos/doc/manual/development/option-types.section.md#L268-L275]:
https://github.com/NixOS/nixpkgs/blob/nixos-25.05/nixos/doc/manual/development/option-types.section.md?plain=1#L268-L275

View File

@@ -1,10 +1,8 @@
# 使用 Flakes 来管理你的 NixOS
与 NixOS 当前默认的配置方式相比Flakes 提供了更好的可复现性,同时它清晰的包结构定义原生支
持了以其他 Git 仓库为依赖,便于代码分享,因此本书更建议使用 Flakes 来管理系统配置。
与 NixOS 当前默认的配置方式相比Flakes 提供了更好的可复现性,同时它清晰的包结构定义原生支持了以其他 Git 仓库为依赖,便于代码分享,因此本书更建议使用 Flakes 来管理系统配置。
本节我们介绍如何使用 Flakes 来管理 NixOS 系统配置,**阅读本节内容不需要提前对 Flakes 有任
何了解**。
本节我们介绍如何使用 Flakes 来管理 NixOS 系统配置,**阅读本节内容不需要提前对 Flakes 有任何了解**。
## 启用 NixOS 的 Flakes 支持 {#enable-nix-flakes}
@@ -39,8 +37,8 @@
然后运行 `sudo nixos-rebuild switch` 应用修改后,即可使用 Flakes 特性来管理系统配置。
nix 的新命令行工具还提供了一些方便的功能,比如说你现在可以使用 `nix repl` 命令打开一个 nix
交互环境,有兴趣的话,可以使用它复习测试一遍前面学过的所有 Nix 语法。
nix 的新命令行工具还提供了一些方便的功能,比如说你现在可以使用 `nix repl`
命令打开一个 nix 交互环境,有兴趣的话,可以使用它复习测试一遍前面学过的所有 Nix 语法。
## 将系统配置切换到 flake.nix {#switch-to-flake-nix}
@@ -60,8 +58,8 @@ nix flake init -t templates#full
cat flake.nix
```
我们参照该模板创建文件 `/etc/nixos/flake.nix` 并编写好配置内容,后续系统的所有修改都将全部
由 Nix Flakes 接管,示例内容如下:
我们参照该模板创建文件 `/etc/nixos/flake.nix`
并编写好配置内容,后续系统的所有修改都将全部由 Nix Flakes 接管,示例内容如下:
```nix{16}
{
@@ -89,8 +87,9 @@ cat flake.nix
这里我们定义了一个名为 `my-nixos` 的系统,它的配置文件为 `/etc/nixos/` 文件夹下的
`./configuration.nix`,也就是说我们仍然沿用了旧的配置。
现在执行 `sudo nixos-rebuild switch` 应用配置,系统应该没有任何变化,因为我们仅仅是切换到
了 Nix Flakes配置内容与之前还是一致的。
现在执行 `sudo nixos-rebuild switch`
应用配置,系统应该没有任何变化,因为我们仅仅是切换到了 Nix
Flakes配置内容与之前还是一致的。
> 如果你的系统 Hostname 不是 `my-nixos`,你需要在 `flake.nix` 中修改 `nixosConfigurations`
> 的名称,或者使用 `--flake /etc/nixos#my-nixos` 来指定配置名称。
@@ -99,30 +98,29 @@ cat flake.nix
目前我们的 flake 包含这几个文件:
- `/etc/nixos/flake.nix`: flake 的入口文件,执行 `sudo nixos-rebuild switch` 时会识别并部
署它。
- `/etc/nixos/flake.lock`: 自动生成的版本锁文件,它记录了整个 flake 所有输入的数据源、hash
值、版本号,确保系统可复现。
- `/etc/nixos/configuration.nix`: 这是我们之前的配置文件,在 `flake.nix` 中被作为模块导
入,目前所有系统配置都写在此文件中。
- `/etc/nixos/hardware-configuration.nix`: 这是系统硬件配置文件,由 NixOS 生成,描述了系统
的硬件信息
- `/etc/nixos/flake.nix`: flake 的入口文件,执行 `sudo nixos-rebuild switch`
时会识别并部署它。
- `/etc/nixos/flake.lock`: 自动生成的版本锁文件,它记录了整个 flake 所有输入的数据源、hash 值、版本号,确保系统可复现。
- `/etc/nixos/configuration.nix`: 这是我们之前的配置文件,在 `flake.nix`
中被作为模块导入,目前所有系统配置都写在此文件中。
- `/etc/nixos/hardware-configuration.nix`: 这是系统硬件配置文件,由 NixOS 生成,描述了系统的硬件信息
## 总结 {#conclusion}
本节中我们添加了一个非常简单的配置文件 `/etc/nixos/flake.nix`,它仅仅是
`/etc/nixos/configuration.nix` 的一个 thin wrapper它自身并没有提供任何新的功能也没有引
入任何破坏性的变更。
`/etc/nixos/configuration.nix` 的一个 thin
wrapper它自身并没有提供任何新的功能也没有引入任何破坏性的变更。
在本书后面的内容中,我们会学习了解 `flake.nix` 的结构与功能,并逐渐看到这样一个 wrapper 能
为我们带来哪些好处。
在本书后面的内容中,我们会学习了解 `flake.nix`
的结构与功能,并逐渐看到这样一个 wrapper 能为我们带来哪些好处。
> 注意:**本书描述的配置管理方式并非「Everything in a single file」更推荐将配置内容分门
> 别类地存放到不同的 nix 文件中**,然后在 `flake.nix` 的 `modules` 参数列表中引入这些配置
> 文件,并通过 Git 管理它们。这样做的好处是,可以更好地组织配置文件,提高配置的可维护性。
> 后面的 [模块化 NixOS 配置](./modularize-the-configuration.md) 一节将会详细介绍如何模块化
> 你的 NixOS 配置,[其他实用技巧 - 使用 Git 管理 NixOS 配置](./other-useful-tips.md) 将会
> 介绍几种使用 Git 管理 NixOS 配置的最佳实践。
> 注意:**本书描述的配置管理方式并非「Everything in a single
> file」更推荐将配置内容分门别类地存放到不同的 nix 文件中**,然后在 `flake.nix` 的
> `modules`
> 参数列表中引入这些配置文件,并通过 Git 管理它们。这样做的好处是,可以更好地组织配置文件,提高配置的可维护性。后面的
> [模块化 NixOS 配置](./modularize-the-configuration.md)
> 一节将会详细介绍如何模块化你的 NixOS 配置,[其他实用技巧 - 使用 Git 管理 NixOS 配置](./other-useful-tips.md)
> 将会介绍几种使用 Git 管理 NixOS 配置的最佳实践。
[nix flake - Nix Manual]:
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-inputs

View File

@@ -15,29 +15,28 @@ sudo nixos-rebuild switch --flake .#myhost --show-trace -L -v
## 使用 Git 管理 NixOS 配置 {#git-manage-nixos-config}
NixOS 的配置文件是纯文本,因此跟普通的 dotfiles 一样可以使用 Git 管理,这样可以方便的回滚
到历史版本,或者在多台机器上同步配置。
NixOS 的配置文件是纯文本,因此跟普通的 dotfiles 一样可以使用 Git 管理,这样可以方便的回滚到历史版本,或者在多台机器上同步配置。
> 注意:使用 Git 后,所有未被 Git 跟踪的文件都会被 Nix 忽略,如果发现 Nix 报错说某某文件
> not found或许是因为你没 `git add` 它
> 注意:使用 Git 后,所有未被 Git 跟踪的文件都会被 Nix 忽略,如果发现 Nix 报错说某某文件 not
> found或许是因为你没 `git add` 它
另外一点是Nix Flakes 配置也不一定需要放在 `/etc/nixos` 目录下,可以放在任意目录下,只要
在部署时指定正确的路径即可。
另外一点是Nix Flakes 配置也不一定需要放在 `/etc/nixos`
目录下,可以放在任意目录下,只要在部署时指定正确的路径即可。
> 我们在前面第 3 小节的代码注释中有说明过,可以通过
> `sudo nixos-rebuild switch --flake .#xxx` 的 `--flake` 参数指定 Flakes 配置的文件夹路
> 径,并通过 `#` 后面的值来指定使用的 outputs 名称。
> `sudo nixos-rebuild switch --flake .#xxx` 的 `--flake`
> 参数指定 Flakes 配置的文件夹路径,并通过 `#` 后面的值来指定使用的 outputs 名称。
比如我的使用方式是将 Nix Flakes 配置放在 `~/nixos-config` 目录下,然后在 `/etc/nixos` 目录
下创建一个软链接:
比如我的使用方式是将 Nix Flakes 配置放在 `~/nixos-config` 目录下,然后在 `/etc/nixos`
目录下创建一个软链接:
```shell
sudo mv /etc/nixos /etc/nixos.bak # 备份原来的配置
sudo ln -s ~/nixos-config/ /etc/nixos
```
然后就可以在 `~/nixos-config` 目录下使用 Git 管理配置了,配置使用普通的用户级别权限即可,
不要求 owner 为 root.
然后就可以在 `~/nixos-config`
目录下使用 Git 管理配置了,配置使用普通的用户级别权限即可,不要求 owner 为 root.
另一种方法是直接删除掉 `/etc/nixos`,并在每次部署时指定配置文件路径:
@@ -50,8 +49,7 @@ cd ~/nixos-config
sudo nixos-rebuild switch --flake .#my-nixos
```
两种方式都可以,看个人喜好。搞定之后,系统的回滚也变得非常简单,只需要切换到上一个 commit
即可:
两种方式都可以,看个人喜好。搞定之后,系统的回滚也变得非常简单,只需要切换到上一个 commit 即可:
```shell
cd ~/nixos-config
@@ -61,13 +59,11 @@ git checkout HEAD^1
sudo nixos-rebuild switch --flake .#my-nixos
```
Git 的更多操作这里就不介绍了,总之一般情况下的回滚都能直接通过 Git 完成,只在系统完全崩溃
的情况下,才需要通过重启进入 grub从上一个历史版本启动系统。
Git 的更多操作这里就不介绍了,总之一般情况下的回滚都能直接通过 Git 完成,只在系统完全崩溃的情况下,才需要通过重启进入 grub从上一个历史版本启动系统。
## 查看与清理历史数据 {#view-and-delete-history}
如前所述NixOS 的每次部署都会生成一个新的版本,所有版本都会被添加到系统启动项中,除了重启
电脑外,我们也可以通过如下命令查询当前可用的所有历史版本:
如前所述NixOS 的每次部署都会生成一个新的版本,所有版本都会被添加到系统启动项中,除了重启电脑外,我们也可以通过如下命令查询当前可用的所有历史版本:
```shell
nix profile history --profile /nix/var/nix/profiles/system

View File

@@ -1,7 +1,6 @@
# 安装使用 Home Manager
前面简单提过NixOS 自身的配置文件只能管理系统级别的配置,而用户级别的配置则需要使用
home-manager 来管理。
前面简单提过NixOS 自身的配置文件只能管理系统级别的配置,而用户级别的配置则需要使用 home-manager 来管理。
根据官方文档
[Home Manager Manual](https://nix-community.github.io/home-manager/index.xhtml),要将 home
@@ -167,8 +166,8 @@ manager 作为 NixOS 模块安装,首先需要创建 `/etc/nixos/home.nix`
}
```
添加好 `/etc/nixos/home.nix` 后,还需要在 `/etc/nixos/flake.nix` 中导入该配置,它才能生
效,可以使用如下命令,在当前文件夹中生成一个示例配置以供参考:
添加好 `/etc/nixos/home.nix` 后,还需要在 `/etc/nixos/flake.nix`
中导入该配置,它才能生效,可以使用如下命令,在当前文件夹中生成一个示例配置以供参考:
```shell
nix flake new example -t github:nix-community/home-manager#nixos
@@ -234,46 +233,45 @@ nix flake new example -t github:nix-community/home-manager#nixos
`home.nix` 中 Home Manager 的配置项有这几种查找方式:
- [Home Manager - Appendix A. Configuration Options](https://nix-community.github.io/home-manager/options.xhtml):
一份包含了所有配置项的列表,建议在其中关键字搜索
- [Home Manager Option Search](https://mipmip.github.io/home-manager-option-search/): 一
个更方便的 option 搜索工具。
- [home-manager](https://github.com/nix-community/home-manager): 有些配置项在官方文档中没
有列出,或者文档描述不够清晰,可以直接在这份 home-manager 的源码中搜索阅读对应的源码。
- [Home Manager - Appendix A. Configuration Options](https://nix-community.github.io/home-manager/options.xhtml): 一份包含了所有配置项的列表,建议在其中关键字搜索。
- [Home Manager Option Search](https://mipmip.github.io/home-manager-option-search/): 一个更方便的 option 搜索工具
- [home-manager](https://github.com/nix-community/home-manager): 有些配置项在官方文档中没有列出,或者文档描述不够清晰,可以直接在这份 home-manager 的源码中搜索阅读对应的源码。
## Home Manager vs NixOS
有许多的软件包或者软件配置, 既可以使用 NixOS Module 配置(`configuration.nix`),也可以使用
Home Manager 配置(`home.nix`), 这带来一个选择难题:**将软件包或者配置文件写在 NixOS Module
里还是 Home Manager 配置里面有何区别? 该如何决策?**
有许多的软件包或者软件配置, 既可以使用 NixOS
Module 配置(`configuration.nix`)也可以使用Home
Manager 配置(`home.nix`), 这带来一个选择难题:**将软件包或者配置文件写在 NixOS
Module 里还是 Home Manager 配置里面有何区别? 该如何决策?**
首先看看区别, NixOS Module 中安装的软件包跟配置文件都是整个系统全局的, 全局的配置通常会被
存放在 `/etc` 中, 系统全局安装的软件也在任何用户环境下都可使用。
首先看看区别, NixOS
Module 中安装的软件包跟配置文件都是整个系统全局的, 全局的配置通常会被存放在 `/etc`
中, 系统全局安装的软件也在任何用户环境下都可使用。
相对的,通过 Home Manager 安装的配置项将会被链接到对应用户的 Home 目录, 其安装的软件也仅在
对应的用户环境下可用, 切换到其他用户后这些配置跟软件就都用不了了。
相对的,通过 Home
Manager 安装的配置项将会被链接到对应用户的 Home 目录, 其安装的软件也仅在对应的用户环境下可用, 切换到其他用户后这些配置跟软件就都用不了了。
根据这种特性, 一般的推荐用法是:
- NixOS Module: 安装系统的核心组件, 以及所有用户都需要用到的其他软件包或配置
- 比如说如果你希望某个软件包能在你切换到 root 用户时仍能正常使用, 或者使某个配置能在系统
全局生效, 那就得用 NixOS Module 来安装它
- 比如说如果你希望某个软件包能在你切换到 root 用户时仍能正常使用, 或者使某个配置能在系统全局生效, 那就得用 NixOS
Module 来安装它
- Home Manager: 其他所有的配置与软件, 都建议用 Home Manager 来安装
这样做的好处是:
1. 系统层面安装的软件与后台服务常常以 root 特权用户的身份运行,尽量避免在系统层面安装不必
要的软件,可以减少系统的安全风险。
1. Home Manager 的许多配置都可以在 NixOS, macOS 以及其他 Linux 发行版上通用,尽可能选用
Home Manager 来安装软件与配置系统,可以提高配置的可移植性。
1. 如果你需要多用户,通过 Home Manager 安装的软件与配置,可以更好地隔离不同用户的环境,避
免不同用户之间的配置与软件版本冲突。
1. 系统层面安装的软件与后台服务常常以 root 特权用户的身份运行,尽量避免在系统层面安装不必要的软件,可以减少系统的安全风险。
1. Home Manager 的许多配置都可以在 NixOS,
macOS 以及其他 Linux 发行版上通用,尽可能选用 Home
Manager 来安装软件与配置系统,可以提高配置的可移植性。
1. 如果你需要多用户,通过 Home
Manager 安装的软件与配置,可以更好地隔离不同用户的环境,避免不同用户之间的配置与软件版本冲突。
## 如何以特权身份使用 Home Manager 安装的软件包?
对这个问题,首先想到的一般都是直接切换到 `root` 用户, 可切换用户后,当前用户通过
`home.nix` 安装的软件包都将不可用。让我们以 `kubectl` 为例(已通过 `home.nix` 预先安装
好),来演示一下:
`home.nix` 安装的软件包都将不可用。让我们以 `kubectl` 为例(已通过 `home.nix`
预先安装好),来演示一下:
```sh
# 1. kubectl 当前可用
@@ -302,8 +300,8 @@ Error: nu::shell::external_command
/home/ryan/nix-config> exit
```
解决方法是,使用 `sudo` 来运行命令,该命令临时授予当前用户以特权身份(`root`)运行命令的权
限:
解决方法是,使用 `sudo`
来运行命令,该命令临时授予当前用户以特权身份(`root`)运行命令的权限:
```sh
sudo kubectl

View File

@@ -22,4 +22,3 @@ sudo nixos-rebuild switch --recreate-lock-file --flake .
有时在运行 `nixos-rebuild switch` 时可能会遇到 `sha256 mismatch`
类似的错误,一般可以通过运行 `nix flake update` 来更新 `flake.lock` 解决。

View File

@@ -24,9 +24,9 @@ nix-repl> pkgs.writeShellScriptBin "hello" '' echo "hello, xxx!" ''
«derivation /nix/store/zhgar12vfhbajbchj36vbbl3mg6762s8-hello.drv»
```
上面这个 Derivation 的定义很短,就一行,但 nixpkgs 中大部分的 Derivation 的定义都要比这复
杂很多。前面我们介绍并大量使用了 `import xxx.nix` 来从其他 Nix 文件中导入 Nix 表达式,我们
可以在这里也使用这种方法来提升代码的可维护性:
上面这个 Derivation 的定义很短,就一行,但 nixpkgs 中大部分的 Derivation 的定义都要比这复杂很多。前面我们介绍并大量使用了
`import xxx.nix`
来从其他 Nix 文件中导入 Nix 表达式,我们可以在这里也使用这种方法来提升代码的可维护性:
1. 将上面这一行 Derivation 的定义存放到单独的文件 `hello.nix` 中。
1.`hello.nix` 自身的上下文中不包含 `pkgs` 这个变量,所以需要修改下其内容,将 `pkgs`
@@ -58,13 +58,13 @@ nix-repl> import ./hello.nix pkgs
中,这样做的缺点有:
1. `hello` 这个 derivation 的所有其他依赖项都只能从 `pkgs` 中获取,耦合度太高。
1. 比如说我们如果需要其他自定义依赖项,就必须修改 `pkgs` 或者修改 `hello.nix` 的内容,
而这两个都很麻烦。
1.`hello.nix` 变复杂的情况下,很难判断 `hello.nix` 到底依赖了 `pkgs` 中的哪些
Derivation很难分析 Derivation 之间的依赖关系。
1. 比如说我们如果需要其他自定义依赖项,就必须修改 `pkgs` 或者修改 `hello.nix`
的内容,而这两个都很麻烦。
1.`hello.nix` 变复杂的情况下,很难判断 `hello.nix` 到底依赖了 `pkgs`
中的哪些 Derivation很难分析 Derivation 之间的依赖关系。
`pkgs.callPackage` 作为一个参数化构建 Derivation 的工具函数,可解决上述两个问题。首先看
看源码中此函数的定义与注释
`pkgs.callPackage`
作为一个参数化构建 Derivation 的工具函数,可解决上述两个问题。首先看看源码中此函数的定义与注释
[nixpkgs/lib/customisation.nix#L101-L121](https://github.com/NixOS/nixpkgs/blob/fe138d3/lib/customisation.nix#L101-L121)
```nix
@@ -101,25 +101,25 @@ nix-repl> import ./hello.nix pkgs
# ...... 省略后面的内容 ......
```
简单的说,它的使用格式是 `pkgs.callPackage fn args`,其中 `fn` 是一个 nix 文件或者函
数,`args` 是一个 attribute set它的工作流程是
简单的说,它的使用格式是 `pkgs.callPackage fn args`,其中 `fn`
是一个 nix 文件或者函数,`args` 是一个 attribute set它的工作流程是
1. `pkgs.callPackage fn args` 会先判断 `fn` 是一个函数还是一个文件,如果是文件就先通过
`import xxx.nix` 导入其中定义的函数。
1. 第一步执行完毕得到的是一个函数,其参数通常会有 `lib`, `stdenv`, `fetchurl` 等参数,
可能还会带有一些自定义参数。
2. 之后,`pkgs.callPackage fn args` 会将 `args``pkgs` 这个 attribute set 合并。如果存
在冲突,`args` 中的参数会覆盖 `pkgs` 中的参数。
3. 再之后,`pkgs.callPackage fn args` 会从上一步得到的 attribute set 中提取出 `fn` 函数的
参数,并使用它们来执行 `fn` 函数。
1. 第一步执行完毕得到的是一个函数,其参数通常会有 `lib`, `stdenv`, `fetchurl`
等参数,可能还会带有一些自定义参数。
2. 之后,`pkgs.callPackage fn args` 会将 `args``pkgs` 这个 attribute
set 合并。如果存在冲突,`args` 中的参数会覆盖 `pkgs` 中的参数。
3. 再之后,`pkgs.callPackage fn args` 会从上一步得到的 attribute set 中提取出 `fn`
函数的参数,并使用它们来执行 `fn` 函数。
4. 函数执行结果是一个 Derivation也就是一个 Nix 包。
那可以作为 `pkgs.callPackage` 参数的 nix 文件具体长啥样呢,可以去看看我们前面在
[Nixpkgs 高级用法 - 简介](./intro.md) 中举例过的 `hello.nix` `fcitx5-rime.nix`
`vscode/with-extensions.nix` `firefox/common.nix`,它们都可以被 `pkgs.callPackage` 导入。
比如说我们自定义了一个 NixOS 内核配置 `kernel.nix`,并且将开发版名称与内核源码作为了可变更
参数:
比如说我们自定义了一个 NixOS 内核配置
`kernel.nix`,并且将开发版名称与内核源码作为了可变更参数:
```nix
{
@@ -146,8 +146,8 @@ nix-repl> import ./hello.nix pkgs
})
```
那么就可以在任意 Nixpkgs Module 中使用 `pkgs.callPackage ./hello.nix {}` 来导入并使用它,
并且替换它的任意参数。
那么就可以在任意 Nixpkgs Module 中使用 `pkgs.callPackage ./hello.nix {}`
来导入并使用它,并且替换它的任意参数。
```nix
{ lib, pkgs, pkgsKernel, kernel-src, ... }:
@@ -167,17 +167,16 @@ nix-repl> import ./hello.nix pkgs
```
就如上面所展示的,通过 `pkgs.callPackage` 我们可以给 `kernel.nix` 定义的函数传入不同的
`src``boardName`,来生成不同的内核包,这样就可以使用同一份 `kernel.nix` 来适配不同的内
核源码与不同的开发板了。
`src``boardName`,来生成不同的内核包,这样就可以使用同一份 `kernel.nix`
来适配不同的内核源码与不同的开发板了。
`pkgs.callPackage` 的优势在于:
1. Derivation 的定义被参数化,定义中的所有函数参数就是 Derivation 的所有依赖项,这样就可以
很方便的分析 Derivation 之间的依赖关系。
1. Derivation 的定义被参数化,定义中的所有函数参数就是 Derivation 的所有依赖项,这样就可以很方便的分析 Derivation 之间的依赖关系。
2. Derivation 的所有依赖项与其他自定义参数都可以很方便地被替换(通过使用
`pkgs.callPackage` 的第二个参数Derivation 定义的可复用性大大提升。
3. 在实现了前两条功能的情况下,并未增加代码的复杂度,所有 `pkgs` 中的依赖项都可以被自动注
入,不需要手动传递。
3. 在实现了前两条功能的情况下,并未增加代码的复杂度,所有 `pkgs`
中的依赖项都可以被自动注入,不需要手动传递。
因此我们总是推荐使用 `pkgs.callPackage` 来定义 Derivation。

View File

@@ -1,22 +1,18 @@
# Nixpkgs 的高级用法 {#nixpkgs-advanced-usage}
callPackage、Overriding 与 Overlays 是在使用 Nix 时偶尔会用到的技术,它们都是用来自定义
Nix 包的构建方法的。
callPackage、Overriding 与 Overlays 是在使用 Nix 时偶尔会用到的技术,它们都是用来自定义 Nix 包的构建方法的。
我们知道许多程序都有大量构建参数需要配置,不同的用户会希望使用不同的构建参数,这时候就需要
Overriding 与 Overlays 来实现。我举几个我遇到过的例子:
我们知道许多程序都有大量构建参数需要配置,不同的用户会希望使用不同的构建参数,这时候就需要 Overriding 与 Overlays 来实现。我举几个我遇到过的例子:
1. [fcitx5-rime.nix](https://github.com/NixOS/nixpkgs/blob/e4246ae1e7f78b7087dce9c9da10d28d3725025f/pkgs/tools/inputmethods/fcitx5/fcitx5-rime.nix):
fcitx5-rime 的 `rimeDataPkgs` 默认使用 `rime-data` 包,但是也可以通过 override 来自定义
该参数的值,以加载自定义的 rime 配置(比如加载小鹤音形输入法配置)。
fcitx5-rime 的 `rimeDataPkgs` 默认使用 `rime-data`
包,但是也可以通过 override 来自定义该参数的值,以加载自定义的 rime 配置(比如加载小鹤音形输入法配置)。
2. [vscode/with-extensions.nix](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/pkgs/applications/editors/vscode/with-extensions.nix):
vscode 的这个包也可以通过 override 来自定义 `vscodeExtensions` 参数的值来安装自定义插
件。
1. [nix-vscode-extensions](https://github.com/nix-community/nix-vscode-extensions): 就
是利用该参数实现的 vscode 插件管理
vscode 的这个包也可以通过 override 来自定义 `vscodeExtensions`
参数的值来安装自定义插件。
1. [nix-vscode-extensions](https://github.com/nix-community/nix-vscode-extensions): 就是利用该参数实现的 vscode 插件管理
3. [firefox/common.nix](https://github.com/NixOS/nixpkgs/blob/416ffcd08f1f16211130cd9571f74322e98ecef6/pkgs/applications/networking/browsers/firefox/common.nix):
firefox 同样有许多可自定义的参数
4. 等等
总之如果需要自定义上述这类 Nix 包的构建参数,或者实施某些比较底层的修改,我们就得用到
callPackage、Overriding 与 Overlays 这些特性。
总之如果需要自定义上述这类 Nix 包的构建参数,或者实施某些比较底层的修改,我们就得用到 callPackage、Overriding 与 Overlays 这些特性。

View File

@@ -1,19 +1,17 @@
# 多 nixpkgs 实例的妙用
我们在前面 [降级与升级软件包](../nixos-with-flakes/downgrade-or-upgrade-packages.md) 一节
中见过,怎么通过 `import nixpkgs {...}` 这样的方法实例化多个不同的 nixpkgs 实例,再通过
`specialArgs` 在所有子模块中使用这些 nixpkgs 实例。这种方法有很多的用途,常见的有:
我们在前面 [降级与升级软件包](../nixos-with-flakes/downgrade-or-upgrade-packages.md)
一节中见过,怎么通过 `import nixpkgs {...}`
这样的方法实例化多个不同的 nixpkgs 实例,再通过 `specialArgs`
在所有子模块中使用这些 nixpkgs 实例。这种方法有很多的用途,常见的有:
1. 通过实例化 commit id 不同的 nixpkgs 实例,用于安装不同版本的软件包。前面的
[降级与升级软件包](../nixos-with-flakes/downgrade-or-upgrade-packages.md) 一节中就是这
样使用的。
2. 如果希望使用 overlays但是又不想影响到默认的 nixpkgs 实例,可以通过实例化一个新的
nixpkgs 实例,然后在这个实例上使用 overlays。
- 上一节 Overlays 中提到的 `nixpkgs.overlays = [...];` 是直接修改全局的 nixpkgs 实例,
如果你的 overlays 改了比较底层的包,可能会影响到其他模块。坏处之一是会导致大量的本地
编译(因为二进制缓存失效了),二是被影响的包功能可能也会出问题。
3. 在跨系统架构的编译中,你可以通过实例化多个 nixpkgs 实例来在不同的地方分别选用 QEMU 模拟
编译与交叉编译,或者添加不同的 gcc 编译参数。
[降级与升级软件包](../nixos-with-flakes/downgrade-or-upgrade-packages.md)
一节中就是这样使用的。
2. 如果希望使用 overlays但是又不想影响到默认的 nixpkgs 实例,可以通过实例化一个新的 nixpkgs 实例,然后在这个实例上使用 overlays。
- 上一节 Overlays 中提到的 `nixpkgs.overlays = [...];`
是直接修改全局的 nixpkgs 实例,如果你的 overlays 改了比较底层的包,可能会影响到其他模块。坏处之一是会导致大量的本地编译(因为二进制缓存失效了),二是被影响的包功能可能也会出问题。
3. 在跨系统架构的编译中,你可以通过实例化多个 nixpkgs 实例来在不同的地方分别选用 QEMU 模拟编译与交叉编译,或者添加不同的 gcc 编译参数。
总之,实例化多个 nixpkgs 实例是非常有用的。
@@ -72,15 +70,14 @@
我们学习 Nix 语法时就学过:
> `import` 表达式以其他 Nix 文件的路径作为参数,返回该 Nix 文件的执行结果。 `import` 的参
> 数如果为文件夹路径,那么会返回该文件夹下的 `default.nix` 文件的执行结果。
> `import` 表达式以其他 Nix 文件的路径作为参数,返回该 Nix 文件的执行结果。 `import`
> 的参数如果为文件夹路径,那么会返回该文件夹下的 `default.nix` 文件的执行结果。
`nixpkgs` 是一个 Git 仓库,它的根目录下刚好有一个 `default.nix` 文件,那么答案就呼之欲出
了:`import nixpkgs` 就是返回
[nixpkgs/default.nix](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/default.nix) 文件
的执行结果。从这个文件开始探索,就能找到 `import nixpkgs` 的实现代码是
[pkgs/top-level/impure.nix](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/pkgs/top-level/impure.nix)
这里截取部分内容:
`nixpkgs` 是一个 Git 仓库,它的根目录下刚好有一个 `default.nix`
文件,那么答案就呼之欲出了:`import nixpkgs` 就是返回
[nixpkgs/default.nix](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/default.nix)
文件的执行结果。从这个文件开始探索,就能找到 `import nixpkgs` 的实现代码是
[pkgs/top-level/impure.nix](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/pkgs/top-level/impure.nix)这里截取部分内容:
```nix
# ... skip some lines
@@ -121,17 +118,16 @@ import ./. (builtins.removeAttrs args [ "system" ] // {
})
```
因此 `import nixpkgs {...}` 实际就是调用了上面这个函数,后面的 attribute set 就是这个参数
的参数。
因此 `import nixpkgs {...}` 实际就是调用了上面这个函数,后面的 attribute
set 就是这个参数的参数。
## 注意事项
在创建多 nixpkgs 实例的时候需要注意一些细节,这里列举一些常见的问题:
1. 根据 @fbewivpjsbsby 补充的文章
[1000 instances of nixpkgs](https://discourse.nixos.org/t/1000-instances-of-nixpkgs/17347)
在子模块或者子 flakes 中用 `import` 来定制 `nixpkgs` 不是一个好的习惯,因为每次
`import` 都会重新求值并产生一个新的 nixpkgs 实例,在配置越来越多时会导致构建时间变长、
内存占用变大。所以这里改为了在 `flake.nix` 中创建所有 nixpkgs 实例。
2. 在混合使用 QEMU 模拟编译与交叉编译时,搞得不好可能会导致许多包被重复编译多次,要注意避
免这种情况。
[1000 instances of nixpkgs](https://discourse.nixos.org/t/1000-instances-of-nixpkgs/17347)在子模块或者子 flakes 中用
`import` 来定制 `nixpkgs` 不是一个好的习惯,因为每次 `import`
都会重新求值并产生一个新的 nixpkgs 实例,在配置越来越多时会导致构建时间变长、内存占用变大。所以这里改为了在
`flake.nix` 中创建所有 nixpkgs 实例。
2. 在混合使用 QEMU 模拟编译与交叉编译时,搞得不好可能会导致许多包被重复编译多次,要注意避免这种情况。

View File

@@ -1,30 +1,28 @@
# Overlays
前面介绍的 `pkgs.xxx.override { ... }`
`pkgs.xxx.overrideAttrs (finalAttrs: previousAttrs: { ... });` 都不会修改 pkgs 实例中原有
的 Derivation而是返回一个新的 Derivation因此它们只适合作为局部参数使用。但如果你需要覆
写的 Derivation 还被其他 Nix 包所依赖,那其他 Nix 包使用的仍然会是未被修改的 Derivation.
`pkgs.xxx.overrideAttrs (finalAttrs: previousAttrs: { ... });`
都不会修改 pkgs 实例中原有的 Derivation而是返回一个新的 Derivation因此它们只适合作为局部参数使用。但如果你需要覆写的 Derivation 还被其他 Nix 包所依赖,那其他 Nix 包使用的仍然会是未被修改的 Derivation.
为了解决这个问题Nix 提供了 overlays 这个功能。简单的说Overlays 可以全局修改 pkgs 中的
Derivation.
为了解决这个问题Nix 提供了 overlays 这个功能。简单的说Overlays 可以全局修改 pkgs 中的 Derivation.
在传统的 Nix 环境中Nix 默认会自动使用 `~/.config/nixpkgs/overlays.nix`
`~/.config/nixpkgs/overlays/*.nix` 这类路径下的所有 overlays 配置。
但是在使用了 Flakes 特性后为了确保系统的可复现性Flake 不能依赖任何 Git 仓库之外的配
置,所以这种旧的配置方式就不再适用了。
但是在使用了 Flakes 特性后为了确保系统的可复现性Flake 不能依赖任何 Git 仓库之外的配置,所以这种旧的配置方式就不再适用了。
在使用 `flake.nix` 配置你的 NixOS 时Home Manager 与 NixOS 都提供了 `nixpkgs.overlays`
个 option 来引入 overlays, 相关文档:
在使用 `flake.nix` 配置你的 NixOS 时Home Manager 与 NixOS 都提供了 `nixpkgs.overlays`
个 option 来引入 overlays, 相关文档:
- [home-manager docs - `nixpkgs.overlays`](https://nix-community.github.io/home-manager/options.xhtml#opt-nixpkgs.overlays)
- [nixpkgs source code - `nixpkgs.overlays`](https://github.com/NixOS/nixpkgs/blob/30d7dd7e7f2cba9c105a6906ae2c9ed419e02f17/nixos/modules/misc/nixpkgs.nix#L169)
举个例子,如下内容就是一个加载 Overlays 的 Module它既可以用做 Home Manager Module也可
以用做 NixOS Module因为这俩定义完全是一致的
举个例子,如下内容就是一个加载 Overlays 的 Module它既可以用做 Home Manager
Module也可以用做 NixOS Module因为这俩定义完全是一致的
> 不过我使用发现Home Manager 毕竟是个外部组件,而且现在全都用的 unstable 分支,这导致
> Home Manager Module 有时候会有点小毛病,因此更建议以 NixOS Module 的形式引入 overlays
> 不过我使用发现Home
> Manager 毕竟是个外部组件,而且现在全都用的 unstable 分支,这导致 Home Manager
> Module 有时候会有点小毛病,因此更建议以 NixOS Module 的形式引入 overlays
```nix
# ./overlays/default.nix
@@ -104,13 +102,11 @@ Derivation.
## 多个使用不同 overlays 的 Nixpkgs 实例
上面介绍的 `nixpkgs.overlays = [...];` 是直接修改全局的默认 nixpkgs 实例,如果你的
overlays 改了比较底层的包,可能会影响到其他模块。坏处之一是会导致大量的本地编译(因为二进
制缓存失效了),二是被影响的包功能可能也会出问题。
上面介绍的 `nixpkgs.overlays = [...];`
是直接修改全局的默认 nixpkgs 实例,如果你的 overlays 改了比较底层的包,可能会影响到其他模块。坏处之一是会导致大量的本地编译(因为二进制缓存失效了),二是被影响的包功能可能也会出问题。
如果你只是想在某个地方使用 overlays而不想影响到全局的 nixpkgs 实例,可以通过实例化多个
nixpkgs 实例来实现。下一节 [多 nixpkgs 实例的妙用](./multiple-nixpkgs.md) 将会介绍如何做到
这一点。
如果你只是想在某个地方使用 overlays而不想影响到全局的 nixpkgs 实例,可以通过实例化多个 nixpkgs 实例来实现。下一节
[多 nixpkgs 实例的妙用](./multiple-nixpkgs.md) 将会介绍如何做到这一点。
## 参考

View File

@@ -1,7 +1,7 @@
# Overriding
简单的说,所有 nixpkgs 中的 Nix 包都可以通过 `<pkg>.override {}` 来自定义某些构建参数,它
返回一个使用了自定义参数的新 Derivation. 举个例子:
简单的说,所有 nixpkgs 中的 Nix 包都可以通过 `<pkg>.override {}`
来自定义某些构建参数,它返回一个使用了自定义参数的新 Derivation. 举个例子:
```nix
pkgs.fcitx5-rime.override {rimeDataPkgs = [
@@ -14,12 +14,10 @@ pkgs.fcitx5-rime.override {rimeDataPkgs = [
如何知道 `fcitx5-rime` 这个包有哪些参数可以覆写呢?有几种方法:
1. 直接在 GitHub 的 nixpkgs 源码中
找:[fcitx5-rime.nix](https://github.com/NixOS/nixpkgs/blob/e4246ae1e7f78b7087dce9c9da10d28d3725025f/pkgs/tools/inputmethods/fcitx5/fcitx5-rime.nix)
1. 注意要选择正确的分支,加入你用的是 nixos-unstable 分支,那就要在 nixos-unstable 分支
中找
2. 通过 `nix repl` 交互式查看:`nix repl -f '<nixpkgs>'`,然后输入 `:e pkgs.fcitx5-rime`
会通过编辑器打开这个包的源码,然后就可以看到这个包的所有参数了。
1. 直接在 GitHub 的 nixpkgs 源码中找:[fcitx5-rime.nix](https://github.com/NixOS/nixpkgs/blob/e4246ae1e7f78b7087dce9c9da10d28d3725025f/pkgs/tools/inputmethods/fcitx5/fcitx5-rime.nix)
1. 注意要选择正确的分支,加入你用的是 nixos-unstable 分支,那就要在 nixos-unstable 分支中找。
2. 通过 `nix repl` 交互式查看:`nix repl -f '<nixpkgs>'`,然后输入
`:e pkgs.fcitx5-rime`,会通过编辑器打开这个包的源码,然后就可以看到这个包的所有参数了
通过上述两种方法,都可以看到 `fcitx5-rime` 这个包拥有如下输入参数,它们都是可以通过
`override` 修改的:
@@ -43,8 +41,8 @@ stdenv.mkDerivation rec {
}
```
除了覆写参数,还可以通过 `overrideAttrs` 来覆写使用 `stdenv.mkDerivation` 构建的
Derivation 的属性。以
除了覆写参数,还可以通过 `overrideAttrs` 来覆写使用 `stdenv.mkDerivation`
构建的 Derivation 的属性。以
[pkgs.hello](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/pkgs/applications/misc/hello/default.nix)
为例,首先通过前述方法查看这个包的源码:
@@ -81,8 +79,8 @@ helloWithDebug = pkgs.hello.overrideAttrs (finalAttrs: previousAttrs: {
});
```
上面这个例子中,`helloWithDebug` 就是一个新的 Derivation它的 `doCheck` 参数被改写为 `false`,而
其他参数则沿用原来的值。
上面这个例子中,`helloWithDebug` 就是一个新的 Derivation它的 `doCheck` 参数被改写为
`false`,而其他参数则沿用原来的值。
除了包源码中自定义的参数值外,我们也可以通过 `overrideAttrs` 直接改写
`stdenv.mkDerivation` 内部的默认参数,比如:
@@ -93,8 +91,8 @@ helloWithDebug = pkgs.hello.overrideAttrs (finalAttrs: previousAttrs: {
});
```
具体的内部参数可以通过 `nix repl -f '<nixpkgs>'` 然后输入 `:e stdenv.mkDerivation` 来查看
其源码。
具体的内部参数可以通过 `nix repl -f '<nixpkgs>'` 然后输入 `:e stdenv.mkDerivation`
来查看其源码。
## 参考

View File

@@ -1,7 +1,7 @@
# Flake 的 inputs {#flake-inputs}
`flake.nix` 中的 `inputs` 是一个 attribute set用来指定当前 Flake 的依赖inputs 有很多种
类型,举例如下:
`flake.nix` 中的 `inputs` 是一个 attribute
set用来指定当前 Flake 的依赖inputs 有很多种类型,举例如下:
> 详细的例子参见官方文档 [Flakes Check - Nix Manual]
@@ -61,9 +61,9 @@
}
```
## 参考
- [Flakes Check - Nix Manual]
[Flakes Check - Nix Manual]: https://nix.dev/manual/nix/stable/command-ref/new-cli/nix3-flake-check
[Flakes Check - Nix Manual]:
https://nix.dev/manual/nix/stable/command-ref/new-cli/nix3-flake-check

View File

@@ -1,4 +1,4 @@
# Nix Flakes 的其他玩法 {#nix-flakes-usage}
到这里我们已经写了不少 Nix Flakes 配置来管理 NixOS 系统了,这里再简单介绍下 Nix Flakes 更
细节的内容,以及常用的 nix flake 命令。
到这里我们已经写了不少 Nix Flakes 配置来管理 NixOS 系统了,这里再简单介绍下 Nix
Flakes 更细节的内容,以及常用的 nix flake 命令。

View File

@@ -4,42 +4,37 @@
`options` 实际都在这两个位置定义:
- NixOS:
[nixpkgs/nixos/modules](https://github.com/NixOS/nixpkgs/tree/25.05/nixos/modules), 我们
<https://search.nixos.org/options> 中能看到的所有 NixOS options 都是在这里定义的。
[nixpkgs/nixos/modules](https://github.com/NixOS/nixpkgs/tree/25.05/nixos/modules), 我们
<https://search.nixos.org/options> 中能看到的所有 NixOS options 都是在这里定义的。
- Home Manager:
[home-manager/modules](https://github.com/nix-community/home-manager/blob/release-25.05/modules):
可在 <https://nix-community.github.io/home-manager/options.xhtml> 中找到其所有的options.
[home-manager/modules](https://github.com/nix-community/home-manager/blob/release-25.05/modules): 可在
<https://nix-community.github.io/home-manager/options.xhtml> 中找到其所有的options.
> 如果你还使用 nix-darwin那么它的配置也是类似的其模块系统的实现位于
> [nix-darwin/modules](https://github.com/LnL7/nix-darwin/tree/master/modules)
而上述 NixOS Modules 跟 Home Manager Modules 的基础,是 Nixpkgs 中实现的一套通用模块系统
[lib/modules.nix][lib/modules.nix],这套模块系统的官方文档如下(即使是对熟练使用 NixOS 的
用户而言,要看懂这玩意儿也不是件容易的事...
[lib/modules.nix][lib/modules.nix],这套模块系统的官方文档如下(即使是对熟练使用 NixOS 的用户而言,要看懂这玩意儿也不是件容易的事...
- [Module System - Nixpkgs]
因为 Nixpkgs 的模块系统文档没人写,文档中直接建议读另一份专门针对 NixOS 模块系统的编写指
南,确实写得清晰一些,但也很难说它对新手有多友好:
因为 Nixpkgs 的模块系统文档没人写,文档中直接建议读另一份专门针对 NixOS 模块系统的编写指南,确实写得清晰一些,但也很难说它对新手有多友好:
- [Writing NixOS Modules - Nixpkgs]
总之,模块系统是由 Nixpkgs 实现的,并不是 Nix 包管理器的一部分,因此它的文档也不在 Nix 包
管理器的文档中。另外 NixOS 与 Home Manager 都是基于 Nixpkgs 的模块系统实现的。
总之,模块系统是由 Nixpkgs 实现的,并不是 Nix 包管理器的一部分,因此它的文档也不在 Nix 包管理器的文档中。另外 NixOS 与 Home
Manager 都是基于 Nixpkgs 的模块系统实现的。
## 模块系统有什么用? {#what-is-module-system}
我们作为一个普通用户,使用 NixOS 与 Home Manager 基于模块系统实现的各种 options 就已经能满
足我们大部分的需求了。那么深入学习模块系统对于我们来说,还有什么好处呢?
我们作为一个普通用户,使用 NixOS 与 Home
Manager 基于模块系统实现的各种 options 就已经能满足我们大部分的需求了。那么深入学习模块系统对于我们来说,还有什么好处呢?
我们在前面介绍配置的模块化时,提到了核心点是将配置拆分为多个模块,再通过
`imports = [ ... ];` 来导入这些模块。这其实就是模块系统最基础的用法。但仅仅使用
`imports = [ ... ];`,我们只能将模块中定义的配置原封不动地导入到当前模块中,无法对其做任何
定制,灵活性很差。在配置简单的情况下,这种方式已经足够了,但如果我们的配置比较复杂,那么这
种方式就显得力不从心了。
`imports = [ ... ];`,我们只能将模块中定义的配置原封不动地导入到当前模块中,无法对其做任何定制,灵活性很差。在配置简单的情况下,这种方式已经足够了,但如果我们的配置比较复杂,那么这种方式就显得力不从心了。
这里举个例子来说明其弊端,譬如说我通过一份配置管理了 A、B、C 跟 D 共 4 台 NixOS 主机,我希
望能在尽量减少配置重复的前提下实现如下功能:
这里举个例子来说明其弊端,譬如说我通过一份配置管理了 A、B、C 跟 D 共 4 台 NixOS 主机,我希望能在尽量减少配置重复的前提下实现如下功能:
- A、B、C 跟 D 都需要启用 docker 服务,设置开机自启
- A 需要将 docker 的存储驱动改为 `btrfs`,其他不变
@@ -47,8 +42,8 @@
- C 是位于美国的服务器,无特殊要求
- D 是桌面主机,需要为 docker 设置 HTTP 代理加速下载
如果单纯使用 `imports`,那么我们可能得将配置拆分成如下几个模块,然后在每台主机上导入不同的
模块:
如果单纯使用
`imports`,那么我们可能得将配置拆分成如下几个模块,然后在每台主机上导入不同的模块:
```bash
tree
@@ -59,15 +54,15 @@
└── docker-proxy.nix # 导入了 docker-default.nix设置 HTTP 代理
```
是否感觉到这样的配置很冗余?这还是一个简单的例子,如果我们的机器更多,不同机器的配置差异更
大,那么这种配置的冗余性就会更加明显。
是否感觉到这样的配置很冗余?这还是一个简单的例子,如果我们的机器更多,不同机器的配置差异更大,那么这种配置的冗余性就会更加明显。
显然,我们需要借助其他的手段来解决这个配置冗余的问题,自定义一些我们自己的 `options` 就是
一个很不错的选择。
显然,我们需要借助其他的手段来解决这个配置冗余的问题,自定义一些我们自己的 `options`
就是一个很不错的选择。
在深入学习模块系统之前,我再强调一下,如下内容不是必须学习与使用的,有很多 NixOS 用户并未
自定义任何 `options`,只是简单地使用 `imports` 就能满足他们的需求了。如果你是新手,可以考
虑在遇到类似上面这种,`imports` 解决不了的问题时再来学习这部分内容,这是完全 OK 的。
在深入学习模块系统之前,我再强调一下,如下内容不是必须学习与使用的,有很多 NixOS 用户并未自定义任何
`options`,只是简单地使用 `imports`
就能满足他们的需求了。如果你是新手,可以考虑在遇到类似上面这种,`imports`
解决不了的问题时再来学习这部分内容,这是完全 OK 的。
## 基本结构与用法 {#basic-structure-and-usage}
@@ -91,14 +86,14 @@ Nixpkgs 中定义的模块,其基本结构如下:
}
```
其中的 `imports = [ ... ];` 我们已经很熟悉了,但另外两个部分,我们还没有接触过,这里简单介
绍下:
其中的 `imports = [ ... ];`
我们已经很熟悉了,但另外两个部分,我们还没有接触过,这里简单介绍下:
- `options = { ... };`: 它类似编程语言中的变量声明,用于声明一些可配置的选项。
- `config = { ... };`: 它类似编程语言中的变量赋值,用于为 `options` 中声明的选项赋值。
最典型的用法是:在同一 Nixpkgs 模块中,依据 `options = { ... };` 中声明的 `options` 当前的
值,在 `config = { .. };` 中为其他的 `options` 赋值,这样就实现了参数化配置的功能。
最典型的用法是:在同一 Nixpkgs 模块中,依据 `options = { ... };` 中声明的 `options`
当前的值,在 `config = { .. };` 中为其他的 `options` 赋值,这样就实现了参数化配置的功能。
直接看个例子更容易理解:
@@ -149,8 +144,7 @@ in {
上面这个模块定义了三个 `options`
- `programs.foo.enable`: 用于控制是否启用此模块
- `programs.foo.package`: 用于自定义 foo 这个包,比如说使用不同版本、设置不同编译参数等
等。
- `programs.foo.package`: 用于自定义 foo 这个包,比如说使用不同版本、设置不同编译参数等等。
- `programs.foo.extraConfig`: 用于自定义 foo 的配置文件。
然后在 `config` 中,根据 `options` 中声明的这三个变量的值,做了不同的设置:
@@ -161,8 +155,8 @@ in {
-`programs.foo.package` 添加到 `home.packages` 中,以将其安装到用户环境中。
-`programs.foo.extraConfig` 的值写入到 `~/.config/foo/foorc` 中。
这样,我们就可以在另一个 nix 文件中导入这个模块,并通过设置这里定义的 `options` 来实现对
foo 的自定义配置了,示例:
这样,我们就可以在另一个 nix 文件中导入这个模块,并通过设置这里定义的 `options`
来实现对 foo 的自定义配置了,示例:
```nix
# ./bar.nix
@@ -183,8 +177,9 @@ foo 的自定义配置了,示例:
}
```
上面这个例子中我们为 `options` 赋值的方式实际上是一种**缩写**,当一个模块中只包含定义(`config`),而没有声明(`option`,并且没有其他特殊参数)时,我们可以省略掉 `config`
包装,直接书写 `config` 部分来对其他模块中已声明的 `option` 赋值。
上面这个例子中我们为 `options`
赋值的方式实际上是一种**缩写**,当一个模块中只包含定义(`config`),而没有声明(`option`,并且没有其他特殊参数)时,我们可以省略掉
`config` 包装,直接书写 `config` 部分来对其他模块中已声明的 `option` 赋值。
## 模块系统的赋值与延迟求值 {#module-system-assignment-and-lazy-evaluation}
@@ -228,18 +223,19 @@ foo 的自定义配置了,示例:
}
```
上述配置中的示例 1、2、3 中,`config.warnings` 的值都依赖于 `config.foo` 的值,但它们的实
现方式却不同。将上述配置保存为 `flake.nix`,然后使用命令
`nix eval .#nixosConfigurations.test.config.warnings` 分别测试示例 1、2、3可以发现示例
1、3 都能正常工作,而示例 2 则会报错 `error: infinite recursion encountered`
上述配置中的示例 1、2、3 中,`config.warnings` 的值都依赖于 `config.foo`
的值,但它们的实现方式却不同。将上述配置保存为 `flake.nix`,然后使用命令
`nix eval .#nixosConfigurations.test.config.warnings`
分别测试示例 1、2、3可以发现示例 1、3 都能正常工作,而示例 2 则会报错
`error: infinite recursion encountered`
下面分别解释说明下:
1. 示例一计算流程:`config.warnings` => `config.foo` => `config`
1. 首先Nix 尝试计算 `config.warnings` 的值,但发现它依赖于 `config.foo`.
2. 接着Nix 尝试计算 `config.foo` 的值,它依赖于其外层的 `config`.
3. Nix 尝试计算 `config` 的值,`config` 中未被 `config.foo` 真正使用的内容都会被 Nix 延
迟求值,因此这里不会递归依赖 `config.warnings`
3. Nix 尝试计算 `config` 的值,`config` 中未被 `config.foo`
真正使用的内容都会被 Nix 延迟求值,因此这里不会递归依赖 `config.warnings`
4. `config.foo` 求值结束,接着 `config.warnings` 被赋值,计算结束。
2. 示例二:`config` => `config.foo` => `config`
1. 首先Nix 尝试计算 `config` 的值,但发现它依赖于 `config.foo`.
@@ -250,21 +246,21 @@ foo 的自定义配置了,示例:
其关键就在于 `lib.mkIf` 这个函数,使用它定义的 `config` 会被 Nix 延迟求值,也就是说会在
`config.foo` 求值结束后,才会真正计算 `config = lib.mkIf ...` 的值。
Nixpkgs 中的模块系统提供了一系列类似 `lib.mkIf` 的函数,用于实现参数化配置与智能的模块合
并:
Nixpkgs 中的模块系统提供了一系列类似 `lib.mkIf`
的函数,用于实现参数化配置与智能的模块合并:
1. `lib.mkIf`: 上面已经介绍过了。
1. `lib.mkOverride` / `lib.mkdDefault` / `lib.mkForce`: 在前面
[模块化 NixOS 配置](../nixos-with-flakes/modularize-the-configuration.md) 中已经介绍过
了。
[模块化 NixOS 配置](../nixos-with-flakes/modularize-the-configuration.md)
中已经介绍过了。
1. `lib.mkOrder`, `lib.mkBefore``lib.mkAfter`: 同上
1. 查看 [Option Definitions - NixOS][Option Definitions - NixOS] 了解更多与 options 赋值
definition相关的函数。
1. 查看 [Option Definitions - NixOS][Option Definitions - NixOS]
了解更多与 options 赋值definition相关的函数。
## Options 声明与类型检查 {#option-declarations-and-type-checking}
模块系统的赋值是我们最常用的功能,而如果我们需要自定义一些 `options`,还需要深入了解下
options 的声明与类型检查。
模块系统的赋值是我们最常用的功能,而如果我们需要自定义一些
`options`,还需要深入了解下 options 的声明与类型检查。
这个我觉得就还挺简单的,比赋值要简单挺多了,直接看官方文档就能懂个大概,这里就不再赘述了:
@@ -275,13 +271,12 @@ options 的声明与类型检查。
我们在
[使用 Flakes 来管理你的 NixOS](../nixos-with-flakes/nixos-with-flakes-enabled.md#pass-non-default-parameters-to-submodules)
中已经介绍了如何使用 `specialArgs``_module.args` 来传递额外的参数给其他 Modules 函数,
这里不再赘述。
中已经介绍了如何使用 `specialArgs``_module.args`
来传递额外的参数给其他 Modules 函数,这里不再赘述。
## 如何选择性地导入模块 {#selectively-import-modules}
在上面的例子中,我们已经介绍了如何通过自定义的 options 来决定是否启用某个功能,但我们的代
码实现都是在同一个 nix 文件中的,那么如果我们的模块是分散在不同的文件中的,该如何实现呢?
在上面的例子中,我们已经介绍了如何通过自定义的 options 来决定是否启用某个功能,但我们的代码实现都是在同一个 nix 文件中的,那么如果我们的模块是分散在不同的文件中的,该如何实现呢?
我们先来看看一些常见的错误用法,然后再来介绍正确的使用方式。
@@ -336,8 +331,8 @@ options 的声明与类型检查。
这是最推荐的方式。NixOS 系统中的模块都是这样实现的,在 <https://search.nixos.org/options>
中搜索 `enable` 能看到非常多的可通过 `enable` option 启用或关闭的系统模块。
具体的写法已经在前面的 [基本结构与用法](#basic-structure-and-usage) 中介绍过了,这里不再赘
述。
具体的写法已经在前面的 [基本结构与用法](#basic-structure-and-usage)
中介绍过了,这里不再赘述。
它的缺点是,所有需要条件导入的 Nix 模块都要做改造,把其中的配置声明全部移到
`config = { ...};` 代码块中,代码复杂度会增加,同时也对新手不太友好。
@@ -391,8 +386,8 @@ options 的声明与类型检查。
[ "foo" ]
```
这里需要注意的一点是,**不能在 `imports =[ ... ];` 中使用由 `_module.args` 传递的参数**
我们在前面
这里需要注意的一点是,**不能在 `imports =[ ... ];` 中使用由 `_module.args`
传递的参数**我们在前面
[传递非默认参数到模块系统中 ](../nixos-with-flakes/nixos-flake-and-module-system#pass-non-default-parameters-to-submodules)
一章中已经做过详细说明。

View File

@@ -1,19 +1,19 @@
# Flake 的 outputs {#flake-outputs}
`flake.nix` 中的 `outputs` 是一个 attribute set是整个 Flake 的构建结果,每个 Flake 都可
以有许多不同的 outputs。
`flake.nix` 中的 `outputs` 是一个 attribute
set是整个 Flake 的构建结果,每个 Flake 都可以有许多不同的 outputs。
一些特定名称的 outputs 有特殊用途,会被某些 Nix 命令识别处理,比如:
- Nix packages: 名称为 `apps.<system>.<name>`, `packages.<system>.<name>`
`legacyPackages.<system>.<name>` 的 outputs都是 Nix 包,通常都是一个个应用程序。
- 可以通过 `nix build .#name` 来构建某个 nix 包
- Nix Helper Functions: 名称为 `lib` 的 outputs 是 Flake 函数库,可以被其他 Flake 作为
inputs 导入使用。
- Nix Helper Functions: 名称为 `lib`
的 outputs 是 Flake 函数库,可以被其他 Flake 作为 inputs 导入使用。
- Nix development environments: 名称为 `devShells` 的 outputs 是 Nix 开发环境
- 可以通过 `nix develop` 命令来使用该 Output 创建开发环境
- NixOS configurations: 名称为 `nixosConfigurations.<hostname>` 的 outputs是 NixOS 的系
统配置。
- NixOS configurations: 名称为 `nixosConfigurations.<hostname>`
的 outputs是 NixOS 的系统配置。
- `nixos-rebuild switch .#<hostname>` 可以使用该 Output 来部署 NixOS 系统
- Nix templates: 名称为 `templates` 的 outputs 是 flake 模板
- 可以通过执行命令 `nix flake init --template <reference>` 使用模板初始化一个 Flake 包
@@ -75,5 +75,3 @@ NixOS Wiki 中给出的使用案例:
};
}
```

View File

@@ -1,9 +1,9 @@
# 新一代 Nix 命令行工具的使用 {#flake-commands-usage}
在启用了 `nix-command` & `flakes` 功能后,我们就可以使用 Nix 提供的新一代 Nix 命令行工具
[New Nix Commands][New Nix Commands] 了,这里主要介绍 `nix shell``nix run` 两个命令,
其他重要的命令(如 `nix shell` `nix build`)将在「在 NixOS 上进行开发工作」一章中再详细介
绍。
[New Nix Commands][New Nix Commands] 了,这里主要介绍 `nix shell``nix run`
两个命令,其他重要的命令(如 `nix shell`
`nix build`)将在「在 NixOS 上进行开发工作」一章中再详细介绍。
## `nix shell`
@@ -38,8 +38,8 @@ Hello, world!
## `nix run`
`nix run` 则是创建一个含有指定 Nix 包的环境,并在该环境中直接运行该 Nix 包(临时运行该程
序,不将它安装到系统环境中):
`nix run`
则是创建一个含有指定 Nix 包的环境,并在该环境中直接运行该 Nix 包(临时运行该程序,不将它安装到系统环境中):
```shell
# hello 不存在
@@ -80,8 +80,7 @@ echo "Hello Nix" | nix run "github:NixOS/nixpkgs/nixos-unstable#ponysay"
## `nix run` 与 `nix shell` 的常见用途
那显然就是用来跑些临时命令,比如说我在新 NixOS 主机上恢复环境,但是还没有装 Git我可以直
接用如下命令临时使用 Git 克隆我的配置仓库:
那显然就是用来跑些临时命令,比如说我在新 NixOS 主机上恢复环境,但是还没有装 Git我可以直接用如下命令临时使用 Git 克隆我的配置仓库:
```bash
nix run nixpkgs#git clone git@github.com:ryan4yin/nix-config.git

View File

@@ -5,59 +5,37 @@
NixOS 是个非常特殊的 Linux 发行版,它构建在 Nix 包管理器之上,设计哲学也跟传统的 Ubuntu,
CentOS, Arch Linux 等发行版大相径庭。
NixOS 相比其他发行版最大的优势,是它的可复现能力(即在多台机器上复现出一致的系统环境的能
力)以及声明式配置。
NixOS 相比其他发行版最大的优势,是它的可复现能力(即在多台机器上复现出一致的系统环境的能力)以及声明式配置。
NixOS 很强大,但它的强大也带来了系统复杂度的提升,提高了使用门槛。一方面你在其他 Linux 发
行版上积累很多经验很难在 NixOS 上复用,另一方面 NixOS 又一直因为官方文档与社区文档的散乱陈
旧而饱受诟病。这些问题都困扰着许多 NixOS 新手。
NixOS 很强大,但它的强大也带来了系统复杂度的提升,提高了使用门槛。一方面你在其他 Linux 发行版上积累很多经验很难在 NixOS 上复用,另一方面 NixOS 又一直因为官方文档与社区文档的散乱陈旧而饱受诟病。这些问题都困扰着许多 NixOS 新手。
再说到 Nix 包管理器的实验特性 Flakes它借鉴了 npm/cargo 等包管理器的设计思路,使用
flake.nix 记录所有外部依赖项,使用 flake.lock 锁定所有依赖的版本,极大地增强了 Nix 包管理
器及 NixOS 的可复现能力跟配置可组合能力。
再说到 Nix 包管理器的实验特性 Flakes它借鉴了 npm/cargo 等包管理器的设计思路,使用 flake.nix 记录所有外部依赖项,使用 flake.lock 锁定所有依赖的版本,极大地增强了 Nix 包管理器及 NixOS 的可复现能力跟配置可组合能力。
因为 Flakes 的好处很大,社区非常喜欢它。据官方调查,目前 GitHub 新建的 nix 仓库超过半数都
使用了 Flakes传统的 Nix 配置方式已经不再是主流。
因为 Flakes 的好处很大,社区非常喜欢它。据官方调查,目前 GitHub 新建的 nix 仓库超过半数都使用了 Flakes传统的 Nix 配置方式已经不再是主流。
但是另一方面 Flakes 作为一个实验性能力存在不确定性,官方文档为了保持稳定性,几乎未包含任何
Flakes 相关的内容。这使许多 Nix/NixOS 用户感到相当困惑——他们看到大家都在用 Flakes也想学
习下它,但却发现无处学起,只能到处拼凑零散的资料、翻 Nixpkgs 源码,或者找前辈高人请教。
但是另一方面 Flakes 作为一个实验性能力存在不确定性,官方文档为了保持稳定性,几乎未包含任何 Flakes 相关的内容。这使许多 Nix/NixOS 用户感到相当困惑——他们看到大家都在用 Flakes也想学习下它但却发现无处学起只能到处拼凑零散的资料、翻 Nixpkgs 源码,或者找前辈高人请教。
## 本书的由来
本书最初源于我入坑 NixOS 时的一份零散学习笔记。
我在今年2023 4 月份入坑 NixOS 时就深深地爱上了它的设计哲学,同时在朋友的推荐下我了解到
了 Nix 的 Flakes 实验特性。在对比了 Flakes 与传统的 NixOS 配置方式后,我意识到只有带上了
Flakes 的 NixOS 才符合我对它的期待。于是我完全忽略掉了传统的 Nix 配置方式,在入门阶段就直
接学习使用 Flakes 来配置我的 NixOS 系统。
我在今年2023 4 月份入坑 NixOS 时就深深地爱上了它的设计哲学,同时在朋友的推荐下我了解到了 Nix 的 Flakes 实验特性。在对比了 Flakes 与传统的 NixOS 配置方式后,我意识到只有带上了 Flakes 的 NixOS 才符合我对它的期待。于是我完全忽略掉了传统的 Nix 配置方式,在入门阶段就直接学习使用 Flakes 来配置我的 NixOS 系统。
在学习的过程中,我发现适合新手的 Flakes 文档几乎没有,大量的文档都是传统的 Nix 配置方法,
我需要从 NixOS Wiki、Zero to Nix、Nixpkgs Manual、Nixpkgs 源码等各种资料中提取出我需要的信
息,同时还要忽略掉所有非 Flakes 的内容。这个学习过程非常曲折痛苦。为了避免后面再次踩坑,我
在学习过程中记录了大量的零散笔记。
在学习的过程中,我发现适合新手的 Flakes 文档几乎没有,大量的文档都是传统的 Nix 配置方法,我需要从 NixOS
Wiki、Zero to Nix、Nixpkgs
Manual、Nixpkgs 源码等各种资料中提取出我需要的信息,同时还要忽略掉所有非 Flakes 的内容。这个学习过程非常曲折痛苦。为了避免后面再次踩坑,我在学习过程中记录了大量的零散笔记。
在拥有了一定使用经验后,今年 5 月初的时候我将自己的主力 PC 切换到了 NixOS然后将这份写了
大半个月的 NixOS 新手笔记整理润色后发布到了我的博客[^1] ,并分享到了 NixOS 中文社区。中文
社区的朋友们表示写得很棒,在他们的建议下,我又将这篇文章翻译成了英文并分享到了 Reddit
到了非常强烈的正反馈[^2]。
在拥有了一定使用经验后,今年 5 月初的时候我将自己的主力 PC 切换到了 NixOS然后将这份写了大半个月的 NixOS 新手笔记整理润色后发布到了我的博客[^1]
,并分享到了 NixOS 中文社区。中文社区的朋友们表示写得很棒,在他们的建议下,我又将这篇文章翻译成了英文并分享到了 Reddit收到了非常强烈的正反馈[^2]。
这份笔记分享出来后好评不断,这让我备感振奋,继续完善它的热情也高涨。在我的持续更新下这份笔
记的内容不断增多,逐渐扩充到了 2 万多字,有读者反馈阅读体验不太好,于是在他的建议下[^3]
我将文章内容迁移到了一个 GitHub 仓库,搭建了一个专门的文档站点,方便大家阅读与贡献。同时也
调整了内容的表述,去掉了一些过于个人化的内容,使其更贴近一本新手指南的风格,而不是一份随性
而为的个人笔记。
这份笔记分享出来后好评不断,这让我备感振奋,继续完善它的热情也高涨。在我的持续更新下这份笔记的内容不断增多,逐渐扩充到了 2 万多字,有读者反馈阅读体验不太好,于是在他的建议下[^3],我将文章内容迁移到了一个 GitHub 仓库,搭建了一个专门的文档站点,方便大家阅读与贡献。同时也调整了内容的表述,去掉了一些过于个人化的内容,使其更贴近一本新手指南的风格,而不是一份随性而为的个人笔记。
至此,一本中英双语的开源书籍诞生了,我给它取名叫 <NixOS & Flakes Book>中文名叫《NixOS 与
Flakes 新手指南》。
至此,一本中英双语的开源书籍诞生了,我给它取名叫 <NixOS & Flakes
Book>中文名叫《NixOS 与 Flakes 新手指南》。
这本开源书籍的内容是我在使用 NixOS 的过程中,以及与读者的沟通中一步步优化的,读者的好评带
来的成就感是我更新的最大动力,一些读者的反馈也对它的「进化」产生了很大的帮助。我最初只是想
分享一下自己的 NixOS 折腾心得,内容也写得比较随意,没想到最后却成了一本开源书籍,国外的阅
读量甚至是国内的两倍,而且还得到了许多 stars ,这真是完全没预料到的。
这本开源书籍的内容是我在使用 NixOS 的过程中,以及与读者的沟通中一步步优化的,读者的好评带来的成就感是我更新的最大动力,一些读者的反馈也对它的「进化」产生了很大的帮助。我最初只是想分享一下自己的 NixOS 折腾心得,内容也写得比较随意,没想到最后却成了一本开源书籍,国外的阅读量甚至是国内的两倍,而且还得到了许多 stars ,这真是完全没预料到的。
感谢所有对本书做出过贡献、提出过建议的朋友们,感谢所有读者的支持与鼓励,没有你们,这本书的
内容可能会一直停留在我个人的博客上,也不会有今天的样子。
感谢所有对本书做出过贡献、提出过建议的朋友们,感谢所有读者的支持与鼓励,没有你们,这本书的内容可能会一直停留在我个人的博客上,也不会有今天的样子。
## 捐赠
@@ -71,26 +49,21 @@ Flakes 新手指南》。
## 反馈与讨论
我不是什么 NixOS 专家到目前为止2024-02我只使用了不到 9 个月的 NixOS所以书中肯定存
在一些误解或者不当的例子。如果你发现了任何错误或者有任何问题/建议,可以直接通过开 issue 或
者加入 [GitHub Discussions](https://github.com/ryan4yin/nixos-and-flakes-book/discussions)
我不是什么 NixOS 专家到目前为止2024-02我只使用了不到 9 个月的 NixOS所以书中肯定存在一些误解或者不当的例子。如果你发现了任何错误或者有任何问题/建议,可以直接通过开 issue 或者加入
[GitHub Discussions](https://github.com/ryan4yin/nixos-and-flakes-book/discussions)
讨论,我很乐意根据大家的反馈持续优化本书的内容。
我写这本小书的原因只是因为没有人为当时还是新手的我做这件事,如上所述,社区的文档太乱了,所
以我选择自己动手,丰衣足食。即使我知道我可能会犯错,但这总比什么都不做要好得多。
我写这本小书的原因只是因为没有人为当时还是新手的我做这件事,如上所述,社区的文档太乱了,所以我选择自己动手,丰衣足食。即使我知道我可能会犯错,但这总比什么都不做要好得多。
我希望这本书能帮助更多的人,让他们能够体验到 NixOS 的乐趣。希望你们喜欢它!
## 本书的特点
1. 以 NixOS 与 Flakes 为核心进行讲解,摈弃了传统的 Nix 配置方式
2. 新手友好,内容尽可能地从拥有一定 Linux 使用经验与编程经验的 NixOS 初学者角度出发进行讲
2. 新手友好,内容尽可能地从拥有一定 Linux 使用经验与编程经验的 NixOS 初学者角度出发进行讲
3. step-by-step渐进式地学习
4. 本书的大部分章节的末尾都给出了其中内容的参考链接,方便读者进一步深入学习,也方便读者对
内容的可信度进行评估
5. 内容连贯,组织良好,比较成体系。读者既可以渐进式地阅读本书,也可以快速定位自己需要的信
4. 本书的大部分章节的末尾都给出了其中内容的参考链接,方便读者进一步深入学习,也方便读者对内容的可信度进行评估
5. 内容连贯,组织良好,比较成体系。读者既可以渐进式地阅读本书,也可以快速定位自己需要的信息
## 本书的历史反馈与相关讨论
@@ -108,6 +81,7 @@ Flakes 新手指南》。
- [[2023-06-24] NixOS 与 Flakes | 一份非官方的新手指南 - 0xffff 社区](https://0xffff.one/d/1547-nixos-yu-flakes-yi-fen-fei-guan)
[^1]: [NixOS 与 Nix Flakes 新手入门](https://thiscute.world/posts/nixos-and-flake-basics/)
[^2]:
[NixOS & Nix Flakes - A Guide for Beginners - Reddit](https://www.reddit.com/r/NixOS/comments/13dxw9d/nixos_nix_flakes_a_guide_for_beginners/)

View File

@@ -1,25 +1,23 @@
# Nix 语言入门
Nix 语言是 Nix 包管理器的基础,要想玩得转 NixOS 与 Nix Flakes享受到它们带来的诸多好处
就必须学会这门语言。
Nix 语言是 Nix 包管理器的基础,要想玩得转 NixOS 与 Nix
Flakes享受到它们带来的诸多好处就必须学会这门语言。
Nix 是一门比较简单的函数式语言,在已有一定编程基础的情况下,过一遍这些语法用时应该在 2 个
小时以内。
Nix 是一门比较简单的函数式语言,在已有一定编程基础的情况下,过一遍这些语法用时应该在 2 个小时以内。
NixOS-CN 社区已经有了一份不错的 Nix 语言教程,我不打算重复造轮子,请直接阅
读[**Nix 语言概览 - NixOS-CN**](https://nixos-cn.org/tutorials/lang) 来快速入门。
NixOS-CN 社区已经有了一份不错的 Nix 语言教程,我不打算重复造轮子,请直接阅读[**Nix 语言概览 - NixOS-CN**](https://nixos-cn.org/tutorials/lang)
来快速入门。
先把语法过一遍,有个大概的印象就行,后面需要用到时再边用边复习语法知识。
## 补充说明
1. 如果你英文尚可,我建议直接阅读官方的入门教程
[Nix language basics - nix.dev](https://nix.dev/tutorials/nix-language) 了解 Nix 语言的
基础语法。
2. 另外需要注意的是NixOS-CN 的语言教程跟 nix.dev 都未介绍完整的 Nix 语法,仅适合新手快速
入门。**如果你遇到任何自己未接触过的语法,请通过官方文档 [Nix Language - Nix Reference
Manual] 查阅 Nix 语言的完整语法**
3. <https://noogle.dev/> 是社区的一个 Nix 函数库搜索器,可以帮助你快速查找你需要的函数以及
它们的用法,非常实用。
[Nix language basics - nix.dev](https://nix.dev/tutorials/nix-language)
了解 Nix 语言的基础语法。
2. 另外需要注意的是NixOS-CN 的语言教程跟 nix.dev 都未介绍完整的 Nix 语法,仅适合新手快速入门。**如果你遇到任何自己未接触过的语法,请通过官方文档
[Nix Language - Nix Reference Manual] 查阅 Nix 语言的完整语法**
3. <https://noogle.dev/>
是社区的一个 Nix 函数库搜索器,可以帮助你快速查找你需要的函数以及它们的用法,非常实用。
[Nix Language - Nix Reference Manual]: https://nixos.org/manual/nix/stable/language/

View File

@@ -15,71 +15,80 @@ const sidebar: {
* Also map shell/console → bash. If no lang, keep plain ``` only.
*/
function normalizeFenceOpeners(md: string): string {
return md.split(/(```[\s\S]*?```)/g).map(block => {
if (!block.startsWith("```")) return block
return md
.split(/(```[\s\S]*?```)/g)
.map((block) => {
if (!block.startsWith("```")) return block
const lines = block.split("\n")
const opener = lines[0]
const m = opener.match(/^```([^\n]*)$/)
if (!m) return block
const lines = block.split("\n")
const opener = lines[0]
const m = opener.match(/^```([^\n]*)$/)
if (!m) return block
let info = m[1].trim()
let info = m[1].trim()
// Attribute form: ```{.nix ...} → extract first class as lang
if (info.startsWith("{")) {
const mm = info.match(/\.([a-zA-Z0-9_-]+)/)
const lang = mm ? mm[1] : ""
const mapped = (lang === "shell" || lang === "console") ? "bash" : lang
lines[0] = "```" + (mapped || "")
// Attribute form: ```{.nix ...} → extract first class as lang
if (info.startsWith("{")) {
const mm = info.match(/\.([a-zA-Z0-9_-]+)/)
const lang = mm ? mm[1] : ""
const mapped = lang === "shell" || lang === "console" ? "bash" : lang
lines[0] = "```" + (mapped || "")
return lines.join("\n")
}
// Info-string form: ```lang{...} or ```lang
const mm = info.match(/^([a-zA-Z0-9_-]+)(\{[^}]*\})?$/) // ignore tail
if (!mm) {
// unknown → leave as-is
lines[0] = "```" + info
return lines.join("\n")
}
let lang = mm[1]
if (lang === "shell" || lang === "console") lang = "bash"
lines[0] = "```" + (lang || "")
return lines.join("\n")
}
// Info-string form: ```lang{...} or ```lang
const mm = info.match(/^([a-zA-Z0-9_-]+)(\{[^}]*\})?$/) // ignore tail
if (!mm) {
// unknown → leave as-is
lines[0] = "```" + info
return lines.join("\n")
}
let lang = mm[1]
if (lang === "shell" || lang === "console") lang = "bash"
lines[0] = "```" + (lang || "")
return lines.join("\n")
}).join("")
})
.join("")
}
/** Add left-gutter line numbers as literal text (e.g., " 1 | …") inside fenced blocks. */
function addLineNumbersToFences(md: string): string {
return md.split(/(```[\s\S]*?```)/g).map(block => {
if (!block.startsWith("```")) return block
return md
.split(/(```[\s\S]*?```)/g)
.map((block) => {
if (!block.startsWith("```")) return block
const lines = block.split("\n")
// find closing fence
let closeIdx = lines.length - 1
while (closeIdx > 0 && !lines[closeIdx].startsWith("```")) closeIdx--
const lines = block.split("\n")
// find closing fence
let closeIdx = lines.length - 1
while (closeIdx > 0 && !lines[closeIdx].startsWith("```")) closeIdx--
const opener = lines[0]
const body = lines.slice(1, closeIdx)
const width = Math.max(1, String(body.length).length)
const opener = lines[0]
const body = lines.slice(1, closeIdx)
const width = Math.max(1, String(body.length).length)
const numbered = body.map((l, i) => `${String(i + 1).padStart(width, " ")} | ${l}`)
const tail = lines.slice(closeIdx) // includes closing fence
return [opener, ...numbered, ...tail].join("\n")
}).join("")
const numbered = body.map((l, i) => `${String(i + 1).padStart(width, " ")} | ${l}`)
const tail = lines.slice(closeIdx) // includes closing fence
return [opener, ...numbered, ...tail].join("\n")
})
.join("")
}
/** Apply XHTML + path fixes only outside fenced code blocks. */
function sanitizeOutsideCode(md: string): string {
return md.split(/(```[\s\S]*?```)/g).map(part => {
if (part.startsWith("```")) return part
return part
.replace(/<br\s*>/g, "<br />")
.replace(/<img([^>]*?)(?<!\/)>/g, "<img$1 />")
.replace(/!\[([^\]]*)\]\(\/([^)]*)\)/g, "![$1]($2)") // MD images /foo → foo
.replace(/src="\/([^"]+)"/g, 'src="$1"') // HTML <img src="/foo"> → "foo"
}).join("")
return md
.split(/(```[\s\S]*?```)/g)
.map((part) => {
if (part.startsWith("```")) return part
return part
.replace(/<br\s*>/g, "<br />")
.replace(/<img([^>]*?)(?<!\/)>/g, "<img$1 />")
.replace(/!\[([^\]]*)\]\(\/([^)]*)\)/g, "![$1]($2)") // MD images /foo → foo
.replace(/src="\/([^"]+)"/g, 'src="$1"') // HTML <img src="/foo"> → "foo"
})
.join("")
}
// -------- setup .temp --------
@@ -106,7 +115,6 @@ console.log("Files to include:", fileList)
// --- Copy and patch Markdown files into .temp ---
for (const relFile of fileList) {
const srcPath = path.join("docs", relFile)
const dstPath = path.join(tempDir, relFile)
@@ -134,9 +142,8 @@ pre { line-height: 1.2 !important; margin: 0 !important; } /* tighten & re
pre code { display: block; padding: 0; margin: 0; }
pre, code { font-variant-ligatures: none; } /* avoid odd ligature spacing */
pre > code.sourceCode { white-space: pre; } /* dont pre-wrap lines */
`;
fs.writeFileSync(path.join(tempDir, "epub-fixes.css"), css);
`
fs.writeFileSync(path.join(tempDir, "epub-fixes.css"), css)
// --- Run Pandoc ---
const outputFileName = "../nixos-and-flakes-book.epub"