feat: nix shell, nix develop & pkgs.runCommand

This commit is contained in:
Ryan Yin 2023-12-13 09:52:01 +08:00
parent fb2fe1224a
commit c098dfa2b0
5 changed files with 224 additions and 10 deletions

View File

@ -265,7 +265,7 @@ function themeConfigEnglish() {
text: "Dev Environments on NixOS",
items: [
{
text: "nix develop & pkgs.mkShell",
text: "nix shell, nix develop & pkgs.runCommand",
link: "/development/intro.md",
},
{
@ -455,7 +455,7 @@ function themeConfigChinese() {
text: "在 NixOS 上进行开发工作",
items: [
{
text: "nix develop 与 pkgs.mkShell",
text: "nix shell, nix develop & pkgs.runCommand",
link: "/zh/development/intro.md",
},
{

View File

@ -8,8 +8,42 @@ You should NOT install the development environment of each language in the globa
In the following sections, we'll introduce how the development environment works in NixOS.
## Createing a Custom Shell Environment with `nix shell`
The simplest way to create a development environment is to use `nix shell`. `nix shell` will create a shell environment with the specified Nix package installed.
Here's an example:
```shell
# hello is not available
hello
hello: command not found
# Enter an environment with the 'hello' and `cowsay` package
nix shell nixpkgs#hello nixpkgs#cowsay
# hello is now available
hello
Hello, world!
# ponysay is also available
cowsay "Hello, world!"
_______
< hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
`nix shell` is very useful when you just want to try out some packages or quickly create a clean environment.
## Creating a Development Environment
`nix shell` is simple and easy to use, but it's not very flexible, for a more complex development environment, we need to use `pkgs.mkShell` and `nix develop`.
We can create a development environment using `pkgs.mkShell { ... }` and open an interactive Bash shell of this development environment using `nix develop`.
To see how `pkgs.mkShell` works, let's take a look at [its source code](https://github.com/NixOS/nixpkgs/blob/nixos-23.05/pkgs/build-support/mkshell/default.nix).
@ -112,7 +146,7 @@ Here is a `flake.nix` that defines a development environment with Node.js 18 ins
}
```
Create an empty folder, save the above configuration as `flake.nix`, and then execute `nix develop` (or more precisely, you can use `nix develop .#default`), you will find that you have entered a nodejs 18 development environment, you can use `node` `npm` `pnpm` `yarn` and other commands. And when you just entered, `shellHook` was also executed, outputting the current version of nodejs.
Create an empty folder, save the above configuration as `flake.nix`, and then execute `nix develop` (or more precisely, you can use `nix develop .#default`), the current version of nodejs will be outputed, and now you can use `node` `pnpm` `yarn` seamlessly.
## Using zsh/fish/... instead of bash
@ -168,6 +202,66 @@ Here is an example:
With the above configuration, `nix develop` will enter the REPL environment of nushell.
## Creating a Development Environment with `pkgs.runCommand`
The derivation created by `pkgs.mkShell` cannot be used directly, but must be accessed via `nix develop`.
It is actually possible to create a shell wrapper containing the required packages via `pkgs.stdenv.mkDerivation`, which can then be run directly into the environment by executing the wrapper.
Using `mkDerivation` directly is a bit cumbersome, and Nixpkgs provides some simpler functions to help us create such wrappers, such as `pkgs.runCommand`.
Example:
```nix
{
description = "A Nix-flake-based Node.js development environment";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
};
outputs = { self , nixpkgs ,... }: let
# system should match the system you are running on
# system = "x86_64-linux";
system = "x86_64-darwin";
in {
packages."${system}".dev = let
pkgs = import nixpkgs {
inherit system;
overlays = [
(self: super: rec {
nodejs = super.nodejs_20;
pnpm = super.nodePackages.pnpm;
})
];
};
packages = with pkgs; [
nodejs
pnpm
nushell
];
in pkgs.runCommand "dev-shell" {
# Dependencies that should exist in the runtime environment
buildInputs = packages;
# Dependencies that should only exist in the build environment
nativeBuildInputs = [ pkgs.makeWrapper ];
} ''
mkdir -p $out/bin/
ln -s ${pkgs.nushell}/bin/nu $out/bin/dev-shell
wrapProgram $out/bin/dev-shell --prefix PATH : ${pkgs.lib.makeBinPath packages}
'';
};
}
```
Then execute `nix run .#dev`, you will enter a nushell session, where you can use the `node` `pnpm` command normally, and the node version is 20.
Related source code:
- [pkgs/build-support/trivial-builders/default.nix - runCommand](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/pkgs/build-support/trivial-builders/default.nix#L21-L49)
- [pkgs/build-support/build-support/setup-hooks/make-wrapper.sh](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/pkgs/build-support/setup-hooks/make-wrapper.sh)
## Enter the build environment of any Nix package
Now let's take a look at `nix develop`, first read the help document output by `nix develop --help`:

View File

@ -13,12 +13,23 @@ The `nix shell` command allows you to enter an environment with the specified Ni
hello
hello: command not found
# Enter an environment with the 'hello' package
nix shell nixpkgs#hello
# Enter an environment with the 'hello' and `cowsay` package
nix shell nixpkgs#hello nixpkgs#cowsay
# hello is now available
hello
Hello, world!
# ponysay is also available
cowsay "Hello, world!"
_______
< hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
## `nix run`

View File

@ -10,8 +10,44 @@
在本章中我们先学习一下 Nix Flakes 开发环境的实现原理,后面的章节再按使用场景介绍一些更具体的内容。
## 通过 `nix shell` 创建开发环境
在 NixOS 上,最简单的创建开发环境的方法是使用 `nix shell`,它会创建一个含有指定 Nix 包的 shell 环境。
示例:
```shell
# hello 不存在
hello
hello: command not found
# 进入到一个含有 hello 与 cowsay 的 shell 环境
# 可以指定多个包,用空格分隔
nix shell nixpkgs#hello nixpkgs#cowsay
# hello 可以用了
hello
Hello, world!
# cowsay 也可以用了
cowsay "Hello, world!"
_______
< hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
`nix shell` 非常适合用于临时试用一些软件包或者快速创建一个干净的环境。
## 创建与使用开发环境
`nix shell` 用起来非常简单,但它并不够灵活,对于更复杂的开发环境管理,我们需要使用 `pkgs.mkShell``nix develop`
在 Nix Flakes 中,我们可以通过 `pkgs.mkShell { ... }` 来定义一个项目环境,通过 `nix develop` 来打开一个该开发环境的交互式 Bash Shell.
为了更好的使用上述两个功能,我们先来看看它们的原理。
@ -116,8 +152,7 @@ stdenv.mkDerivation ({
}
```
建个空文件夹,将上面的配置保存为 `flake.nix`,然后执行 `nix develop`(或者更精确点,可以用 `nix develop .#default`),你会发现你已经进入了一个 nodejs 18 的开发环境,可以使用 `node` `npm` `pnpm` `yarn` 等命令了。而且刚进入时,`shellHook` 也被执行了,输出了当前 nodejs 的版本。
建个空文件夹,将上面的配置保存为 `flake.nix`,然后执行 `nix develop`(或者更精确点,可以用 `nix develop .#default`),首先会打印出当前 nodejs 的版本,之后 `node` `pnpm` `yarn` 等命令就都能正常使用了。
## 在开发环境中使用 zsh/fish 等其他 shell
@ -171,6 +206,66 @@ stdenv.mkDerivation ({
使用上面的 `flake.nix` 配置,`nix develop` 将进入一个 nodejs 18 的开发环境,同时使用 `nushell` 作为交互式 shell.
## 通过 `pkgs.runCommand` 创建开发环境
`pkgs.mkShell` 创建的 derivation 不能直接使用,必须通过 `nix develop` 进入到该环境中。
实际上我们也可以通过 `pkgs.stdenv.mkDerivation` 来创建一个包含所需软件包的 shell wrapper, 这样就能直接通过执行运行该 wrapper 来进入到该环境中。
直接使用 `mkDerivation` 略显繁琐Nixpkgs 提供了一些更简单的函数来帮助我们创建这类 wrapper比如 `pkgs.runCommand`.
示例:
```nix
{
description = "A Nix-flake-based Node.js development environment";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
};
outputs = { self , nixpkgs ,... }: let
# system should match the system you are running on
# system = "x86_64-linux";
system = "x86_64-darwin";
in {
packages."${system}".dev = let
pkgs = import nixpkgs {
inherit system;
overlays = [
(self: super: rec {
nodejs = super.nodejs_20;
pnpm = super.nodePackages.pnpm;
})
];
};
packages = with pkgs; [
nodejs
pnpm
nushell
];
in pkgs.runCommand "dev-shell" {
# Dependencies that should exist in the runtime environment
buildInputs = packages;
# Dependencies that should only exist in the build environment
nativeBuildInputs = [ pkgs.makeWrapper ];
} ''
mkdir -p $out/bin/
ln -s ${pkgs.nushell}/bin/nu $out/bin/dev-shell
wrapProgram $out/bin/dev-shell --prefix PATH : ${pkgs.lib.makeBinPath packages}
'';
};
}
```
然后执行 `nix run .#dev`,就能进入一个 nushell session可以在其中正常使用 `node` `pnpm` 命令,且 node 版本为 20.
相关源代码:
- [pkgs/build-support/trivial-builders/default.nix - runCommand](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/pkgs/build-support/trivial-builders/default.nix#L21-L49)
- [pkgs/build-support/build-support/setup-hooks/make-wrapper.sh](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/pkgs/build-support/setup-hooks/make-wrapper.sh)
## 进入任何 Nix 包的构建环境
现在再来看看 `nix develop`,先读下 `nix develop --help` 输出的帮助文档:
@ -346,6 +441,6 @@ nix build "nixpkgs#ponysay"
- [pkgs.mkShell - nixpkgs manual](https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell)
- [A minimal nix-shell](https://fzakaria.com/2021/08/02/a-minimal-nix-shell.html)
- [One too many shell, Clearing up with nix' shells nix shell and nix-shell - Yannik Sander](https://blog.ysndr.de/posts/guides/2021-12-01-nix-shells/)
- [Shell Scripts - NixOS Wiki](https://nixos.wiki/wiki/Shell_Scripts)
[New Nix Commands]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix.html

View File

@ -12,14 +12,28 @@
hello
hello: command not found
# 进入到一个含有 hello 的 shell 环境
nix shell nixpkgs#hello
# 进入到一个含有 hello 与 cowsay 的 shell 环境
# 可以指定多个包,用空格分隔
nix shell nixpkgs#hello nixpkgs#cowsay
# hello 可以用了
hello
Hello, world!
# cowsay 也可以用了
cowsay "Hello, world!"
_______
< hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
`nix shell` 非常适合用于临时试用一些软件包或者快速创建一个干净的环境。
## `nix run`
`nix run` 则是创建一个含有指定 Nix 包的环境,并在该环境中直接运行该 Nix 包(临时运行该程序,不将它安装到系统环境中):