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", text: "Dev Environments on NixOS",
items: [ items: [
{ {
text: "nix develop & pkgs.mkShell", text: "nix shell, nix develop & pkgs.runCommand",
link: "/development/intro.md", link: "/development/intro.md",
}, },
{ {
@ -455,7 +455,7 @@ function themeConfigChinese() {
text: "在 NixOS 上进行开发工作", text: "在 NixOS 上进行开发工作",
items: [ items: [
{ {
text: "nix develop 与 pkgs.mkShell", text: "nix shell, nix develop & pkgs.runCommand",
link: "/zh/development/intro.md", 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. 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 ## 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`. 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). 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 ## 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. 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 ## 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`: 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
hello: command not found hello: command not found
# Enter an environment with the 'hello' package # Enter an environment with the 'hello' and `cowsay` package
nix shell nixpkgs#hello nix shell nixpkgs#hello nixpkgs#cowsay
# hello is now available # hello is now available
hello hello
Hello, world! Hello, world!
# ponysay is also available
cowsay "Hello, world!"
_______
< hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
``` ```
## `nix run` ## `nix run`

View File

@ -10,8 +10,44 @@
在本章中我们先学习一下 Nix Flakes 开发环境的实现原理,后面的章节再按使用场景介绍一些更具体的内容。 在本章中我们先学习一下 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. 在 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 ## 在开发环境中使用 zsh/fish 等其他 shell
@ -171,6 +206,66 @@ stdenv.mkDerivation ({
使用上面的 `flake.nix` 配置,`nix develop` 将进入一个 nodejs 18 的开发环境,同时使用 `nushell` 作为交互式 shell. 使用上面的 `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 包的构建环境
现在再来看看 `nix develop`,先读下 `nix develop --help` 输出的帮助文档: 现在再来看看 `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) - [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) - [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/) - [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 [New Nix Commands]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix.html

View File

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