nix-shell介绍 @

今天又学了一点新东西,nix-shell 是 Nix 包管理器提供的一个强大的开发环境工具,它可以让你在不影响系统环境的情况下,使用特定版本的工具和依赖。

最开始我以为nix-shell是类似沙箱一样的东西,结果并不是,它只是创建一个新的shell,然后将你需要的软件及依赖注入到shell中,但实际上并没有将其真正安装到你的系统上。用完退出此shell,并不会影响你现有的操作环境,对于系统洁癖症患者真的太好了!

先简单介绍下基础用法

# 使用旧的语法 (nix 2.3 之前)
nix-shell -p python3

# 使用新的语法 (nix 2.4+ 推荐)
nix shell nixpkgs#python3

# 进入一个包含多个包的环境
nix shell nixpkgs#python3 nixpkgs#go nixpkgs#nodejs

# 指定特定的 Nixpkgs 频道
nix shell github:NixOS/nixpkgs#python310

# 在 shell 中执行命令
nix shell nixpkgs#python3 -c python --version

举个简单的例子,我作为golang后端开发者,以前在公司编译老项目,需要使用go1.18去编译,但我自己系统的go版本早就升级到了1.26,最早我是使用go install来管理多个go版本,意味着在我的系统中同时存在多个go版本的二进制,且都会被添加到系统path。

# 安装某个版本的go
go install -v golang.org/dl/go1.20.7@latest
go1.20.7 download    # Go放在~/sdk/go1.20.7目录下

# download完成后, 会在go/bin下多一个带版本的go二进制,可以这样使用
go1.20.7 build main.go

可以看出上述用法其实也是很麻烦,而现在使用nixos系统,就简单多了,我可以很方便的使用nix shell来创建一个对应版本的go环境。

# 指定github:NixOS/nixpkgs/nixos-22.11的作用是只有nixos-22.11版本的源才存在go1.18
nix shell github:NixOS/nixpkgs/nixos-22.11#go_1_18

进入环境后,可以直接使用go1.18来编译项目,完成后exit不会对现有系统环境产生任何影响。

flake配置优化 @

此前的flake目录文件太混乱,没有任何目录结构,因此最近优化了下。

├── flake.nix              # Flake 入口
├── flake.lock             # 依赖锁定文件
├── hosts/
│   └── default/
│       ├── default.nix    # 系统配置
│       └── hardware.nix   # 硬件配置
└── home/
    ├── default.nix        # 用户配置入口(packages + imports)
    ├── git.nix            # git 配置
    ├── zsh.nix            # zsh + p10k 配置
    ├── nvchad.nix         # nvchad 配置
    └── p10k.zsh           # p10k 主题文件

具体则是将之前的git、zsh、nvchad相关配置拆分为多个文件来维护,然后再default.nix中import。然后在flake.nix中引用default.nix即可,后续会介绍import。

nix相关语法的粗浅理解 @

Nix 是一个纯函数式的包管理器,理解它的核心概念对使用 NixOS 至关重要。目前我还没有专门看过nix语法相关的东西,但也学习到一些概念。

下面介绍下flake.nix入口文件的核心概念,input和output。

input

简单来说,inputs主要包含你要从外面下载、依赖的东西,例如nixpkgs、home-manager、github等等,而outputs则是指你用这些依赖构建出来的结果,例如系统、home 配置、软件包等等

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    claude-code.url = "github:sadjow/claude-code-nix";

    home-manager = {
      url = "github:nix-community/home-manager";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    nix4nvchad = {
      url = "github:nix-community/nix4nvchad";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

例如上例的input配置,说明我需要依赖从github下载的nixpkgs、claude-code、home-manager、nix4nvchad模块。运行时,Nix 会把它们全部下载到 /nix/store,并生成 flake.lock 锁定版本。

output

output的基础格式可以用一个很简单的语法来描述。

outputs = { 我要用哪些inputs }: {
  我要输出什么东西
};

一般来说,在input中出现的模块,都是需要在outputs中引用的,否则下载下来干嘛呢。。而输出的内容基本就是你的用户环境,比如你的用户配置、软件包等等

下面介绍一份output的配置内容

outputs = { self, nixpkgs, home-manager, nix4nvchad, claude-code, ... }@inputs:
  let
    system = "x86_64-linux";
    pkgs = import nixpkgs {
      inherit system;
      config = {
        allowUnfree = true;
      };
      overlays = [
        claude-code.overlays.default
      ];
    };
  in
  {
    nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {

      inherit system;
      inherit pkgs;

      modules = [
        ./hosts/default/hardware.nix
        ./hosts/default/default.nix

        home-manager.nixosModules.home-manager {
          home-manager.useGlobalPkgs = true;
          home-manager.useUserPackages = true;

          home-manager.sharedModules = [
            inputs.nix4nvchad.homeManagerModules.default
          ];

          home-manager.users.lznauy = import ./home/default.nix;
        }
      ];

      specialArgs = { inherit inputs; };
    };
  };

第一行的output就是一个函数定义,而{ self, nixpkgs, ... }@inputs 则是函数对应的参数,@inputs 表示把所有依赖打包成一个集合,叫inputs。

第二行的let ... in 是 Nix 中用于定义局部变量的语法,这里定义了 system、pkgs 等局部变量。

下面的{ ... } 是函数的主体,这里定义了 nixosConfigurations.nixos 这个输出,作用是输出一个叫 “nixos” 的 NixOS 系统配置。

inherit system; inherit pkgs; 的意思则是把外面定义的 system、pkgs 变量,传给 nixosSystem 当参数,所以下面的modules和specialArgs也是参数。你可能会疑惑,你怎么知道这几个是参数呢?由于目前没有对应的文档,只能从nixpkgs的flake.nix 源码中获得下列信息。

Create a NixOS system configuration.

Example:

    lib.nixosSystem {
        modules = [ ./configuration.nix ];
    }

Inputs:

    - `modules` (list of paths or inline modules): The NixOS modules to include in the system configuration.
    - `specialArgs` (attribute set): Extra arguments to pass to all modules, that are available in `imports` but can not be extended or overridden by the `modules`.
    - `modulesLocation` (path): A default location for modules that aren't passed by path, used for error messages.

 Legacy inputs:

    - `system`: Legacy alias for `nixpkgs.hostPlatform`, but this is already set in the generated `hardware-configuration.nix`, included by `configuration.nix`.
    - `pkgs`: Legacy alias for `nixpkgs.pkgs`; use `nixpkgs.pkgs` and `nixosModules.readOnlyPkgs` instead.

这段源码注释告诉我们,它的参数有modules、specialArgs、modulesLocation, 而system和pkgs是被废弃的字段, 未来可能彻底移除,所以更现代的方式是如下写法,而pkgs和system则不需要传递了。

outputs = { self, nixpkgs, home-manager, nix4nvchad, claude-code, ... }@inputs:
  {
    nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {

      modules = [
        # 匿名函数 { ... }: { } 的意思是 参数 { ... },返回 { }
        ({ ... }: {
          nixpkgs.hostPlatform = "x86_64-linux";
          nixpkgs.config.allowUnfree = true;
          nixpkgs.overlays = [
            claude-code.overlays.default
          ];
        })

        # 路径
        ./hosts/default/hardware.nix
        ./hosts/default/default.nix

        # 函数调用,形式为func {...},其中{...}是传递给函数的参数
        home-manager.nixosModules.home-manager {
          home-manager.useGlobalPkgs = true;
          home-manager.useUserPackages = true;

          home-manager.sharedModules = [
            inputs.nix4nvchad.homeManagerModules.default
          ];

          home-manager.users.lznauy = import ./home/default.nix;
        }
      ];

      specialArgs = { inherit inputs; };
    };
  };

import 语句 @

import 用于引入外部 Nix 文件,借助import,我们可以将之前的git、zsh、nvchad相关配置拆分为多个文件来维护,然后再default.nix中import。

# default.nix
imports = [
    ./git.nix
    ./zsh.nix
    ./nvchad.nix
];

mcp-nixos介绍 @

mcp-nixos 是专门为 NixOS 提供的 MCP 服务,由于nixos相关文档很少,很多东西需要看源码,之前也尝试问过AI,但它经常给我瞎编一些内容,压根就是nix不存在的语法或者配置,因此使用该mcp能让AI更加聪明点。

安装只需要在home.packages中添加mcp-nixos即可 (如果你使用home-manager的话)

home.packages = with pkgs; [
    mcp-nixos
];

然后就是配置你的mcp,例如我的opencode,只需要在当前项目目录添加opencode.json,然后填写下述信息即可使用该mcp。

{
  "$schema": "https://opencode.ai/config.schema.json",
  "mcp": {
    "nixos": {
      "type": "local",
      "command": ["mcp-nixos"],
      "enabled": true
    }
  }
}

内容总结 @

主要了解下nix shell的使用,还有flake.nix的概念,以及一些简单的语法,并且介绍了下mcp-nixos使用,同时优化下之前的配置结构。