Featured image of post 基于Nix的下一代个人开发环境使用指南

基于Nix的下一代个人开发环境使用指南

背景

我在上一篇文章构建下一代个人开发环境提到的解决方案,在推广给组内同事使用过程中,大家用得挺愉快,也提了一些改进意见。主要是易用性上,比如要修改的配置较分散,比如能否一键安装。当然可以,趁周末通通安排上。顺便将相关示例代码整理开源到Github

快速上手

如果你还没试用过,欢迎参考README中说明,只需执行以下命令,根据提示一路下去即可:

1
sh <(curl -L https://raw.githubusercontent.com/kevin1sMe/dev-with-nix/main/install)

脚本主要做了如下事情:

  1. 检查 Nix 是否已安装: 如果已安装,则跳过安装步骤;如果未安装,将自动进行安装。
  2. 配置 Nix: 开启 Nix 的 Flakes 功能。
  3. 克隆项目仓库: 克隆 dev-with-nix 仓库到本地。

之后请参考config-tmpl.toml来建立个人配置~/.dev-with-nix.toml 并执行

1
nix run nixpkgs#home-manager -- switch --flake .#dev --impure

即可开始配置个人环境。初次会下载比较多的软件包(用时视你网络而定),请起身去喝杯水吧~~

如果上面默认的配置已经够了,就可以到此为止。但如果不满足你的需求,接下来聊聊如何一步步调整到你所需。

如何自定义你的修改?

我建议你fork下本仓库,然后做一些调整。以下将详细讲述如何调整为你的私有配置。

如何安装软件

本套方案基于home-manager来管理软件包,你可以看到home-manager/home.nix中的home.packages:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
home.packages = with pkgs; [ 
    python3
    git
    openssh
    curl
    htop
    lsof
    neofetch
    ...
];

如果不需要指定版本,则直接增加对应软件名即可,当前所支持的软件列表可以从NixOS Search Packages,主流的软件应该都有。

如何安装指定版本的软件

大多数时候home-manager会设置使用和nixpkgs相同的源,这块定义见flake.nix:

1
2
3
4
5
    # home-manager,用于管理用户配置
    home-manager = {
      url = "github:nix-community/home-manager";
      inputs.nixpkgs.follows = "nixpkgs";
    };

那么要安装指定版本的软件,比如我们在用k8s时,对kubectl的版本会有要求,通过上面方式安装的版本可能较高非你所希望的,比如我们想要安装v1.23.4版本,这需要从nixpkgs的历史中找到对应版本的commit,如下示例展示了如何配置它:

首先,在flake.nix中的inputs部分指定对应版本的nixpkgs:

1
2
    # 给指定版本的kubectl使用的nixpkgs
    nixpkgs-for-kubectl.url = "github:NixOS/nixpkgs/e7e54ace729a1e88177c0121d05f35352b05aed8";

然后将这个输入传递给outputs:

1
outputs = { self, nixpkgs, nixpkgs-for-kubectl,  home-manager, nvimdots, ... } @inputs: 

最后,将它一路传给home-manager,然后将它加入到home.packages列表中即可,详情请参考仓库中的做法。

如何安装不在仓库中的软件

这个分两部分,有一些提供了下载链接的软件,我们可以写个flake来下载,有些软件可能是私有的需要构建的,我们要让它自动构建安装到系统中。具体如下:

基于下载链接安装软件

在这里有个示例home-manager/apps/k9s.nix,展示了怎样基于URL安装:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{ stdenv, fetchurl, lib }:

# 这是一个示例:解释了如何下载一个二进制文件并安装到 $out/bin 目录下
# 安装指定版本的软件
stdenv.mkDerivation rec {
  pname = "k9s";
  version = "0.27.4";

  src = fetchurl {
    url = "https://github.com/derailed/k9s/releases/download/v${version}/k9s_Linux_amd64.tar.gz";
    sha256 = "sha256-5QeDHr1fm4wDgPISZp81LG40y3YMkWtJi6uui+g8Q5I=";
  };

  setSourceRoot = "sourceRoot=$(mktemp -d)";
  sourceRoot = ".";

  phases = [ "unpackPhase" "installPhase" ];

  unpackPhase = ''
    tar -xvf $src -C $sourceRoot
    ls -al $sourceRoot
  '';

  installPhase = ''
    ls -la $sourceRoot
    mkdir -p $out/bin
    cp -a k9s $out/bin/
  '';

  meta = with lib; {
    description = "Kubernetes CLI To Manage Your Clusters In Style!";
    homepage = "https://github.com/derailed/k9s";
    license = licenses.asl20;
    platforms = platforms.linux;
  };
}

可能遇上的一个问题是上面sha256不知道填什么,可以先设置为空,然后第一次构建会报错,同时会提示远端的sha值是多少,复制过去即可。

基于仓库自动构建软件包

nix提供了很多构建软件的库,C/C++、Go、Rust等一应俱全,我在home-manager/apps/go-example.nix展示了如何构建一个Go程序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{ lib, buildGoModule, fetchFromGitHub }:

# 这是一个示例,基于某个仓库编译构建,生成相关的二进制文件
buildGoModule rec {
  pname = "go-dnsmasq";
  version = "unstable-2022-07-19"; # 添加实际的提交日期

  src = fetchFromGitHub {
    owner = "kevin1sMe";
    repo = pname;
    rev = "6505b037c29de3b4cc6e1ddfab570cefd360c395"; # 添加你想要打包的版本的commit hash
    sha256 = "sha256-3b4K0kx10SJ9OAv6cEK52PBSRJC/bUuULKdufBFPapA="; # 添加正确的SHA-256哈希
  };

  vendorSha256 = "sha256-0DqS/scc4MD+KVs9lGC6t0hHNCaYLf56k0W1bk5cE9s="; # 添加正确的SHA-256哈希

  subPackages = [ "cmd/dnsmasq" ]; # 打包的子目录路径

  meta = with lib; {
    description = "A golang dnsmasq application"; # 添加合适的描述
    homepage = "https://github.com/kevin1sMe/go-dnsmasq";
    license = licenses.mit; # 确保许可证匹配实际项目许可证
    platforms = platforms.unix;
    maintainers = with maintainers; [ kevinlin ]; # 将其替换为你的名字
  };
}

同样的,上面的sha值不知道怎么写就填空,等报错再来覆盖上。不过网上也有开源工具直接帮你计算获得这个值,我感觉…好像也没太大必要:)

如何定义程序的配置

home-manager给我们提供了不少的程序配置能力,可以在这里看到,比如我们想配置zsh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{userConfig}:
{
  programs.zsh = {
    enable = true;
    enableCompletion = true;
    enableAutosuggestions = true;
    syntaxHighlighting.enable = true;

    oh-my-zsh = {
      enable = true;
      plugins = [ "vi-mode" "z" "colored-man-pages" "sudo" "last-working-dir" ];
      theme = "dst";
    };
    initExtra = ''
      bindkey '^f' autosuggest-accept
      neofetch
    '';

    # 设置一些alias
    shellAliases = {
        k = "kubectl";
        b = "byobu";

    };
  };

  programs.fzf = {
    enable = true;
    enableZshIntegration = true;
  };
}

定义了部分配置让zsh更好用,并且使用了oh-my-zsh的配置方案,这过程你居然都不用关心怎么安装下载它,直接声明使用它的哪些插件即可。或许这就是声明式开发环境的另一个魅力所在吧:)

如何定义个人配置

可能你还有一些配置,比如.gitconfig的配置,甚至涉及个人敏感信息,这里提供一个配置思路。你可以看到在home-manager/files/gitconfig中的内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 省略...

[push]
  default = simple

[log]
  date = local

# User信息是在构建后自动被替换的,你不必修改这里
[user]
  name = REPLACE_NAME_AUTO
  email = REPLACE_EMAIL_AUTO

# ...

我们提交到仓库中的配置不想带上敏感信息,可以通过外部配置去渲染它。比如在~/.dev-with-nix.toml中(模版见config-tmpl.toml)定义:

1
2
3
[gitconfig]
  name = "kevin"
  email = "abc@gmail.com"

然后只要写一个简单的nix表达式即可:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{userConfig, ...}:
# 这是一个示例,如何设置一些用户配置
let 
    # 设置git配置
    gitConfigContent = builtins.readFile ../files/gitconfig;
in
{
	#name = REPLACE_NAME_AUTO
	#email = REPLACE_EMAIL_AUTO
    # 将内容中的name/email替换为用户输入的值
    home.file.".gitconfig".text =  builtins.replaceStrings ["REPLACE_NAME_AUTO" "REPLACE_EMAIL_AUTO"] [userConfig.gitconfig.name userConfig.gitconfig.email] gitConfigContent;
}

这里参数userConfig是用户配置,即~/.dev-with-nix.toml,它将自动解析为nix中可引用的对象。其它的可以明文保存的文件,参考仓库中对于[neofetch]home-manager/files/neofetch的做法。

如何设置环境变量

有时我们会有些变量希望可以从外部传递,并且经常有些敏感信息,当然不能放在仓库中明文写,所以同样的,当前我们可以继续写到私有文件~/.dev-with-nix.toml中,然后将它追加到home.sessionVariables中(这是home-manager的能力)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{ userConfig, ... }:
let 
    envs = builtins.concatStringsSep " + " (builtins.attrNames userConfig.env);
    appendEnvs = builtins.trace "user append envs: *** ${envs} ***" userConfig.env;
in
{
  home.sessionVariables = {
    # 默认编辑器
    EDITOR = "nvim";
} // appendEnvs;
}

之后我们只要在配置文件中类似如下定义键值对即可:

1
2
3
4
# 在这里添加一些希望设定的环境变量
[env]
WELCOME_TO_USE_NIX = "greet from kevin"
TEST="abc"

后记

相信通过上文,你已经学会了如何打造自己私人化开发环境了。请记得将它保存到版本控制仓库中,未来随时随地可重现你的环境。这个方案当前对于如何保存个人配置,有朋友建议将私有化的个人配置也保存在另一个仓库中,然后将它以inputs传递给falke,我感觉也挺有道理,全面的声明式,你说呢?

本文仅限我当前对Nix的了解,若有错漏欢迎指出哈!

本项目仓库的README的中英文是GPT给我写的,感谢它!配的表情真棒:)

最后,欢迎试用,有问题请留言或在Github提Issue

-EOF