How to run derivations from home manager

I have a simple derivation that I want to run via home-manager that looks like:

{ stdenv, lib, pkgs, fetchFromGitHub }:

with lib;

let
in
  stdenv.mkDerivation rec {
    name = "nvchad";

    src = fetchFromGitHub {
      owner = "nvchad";
      repo = "nvchad";
      rev  = "c7a4d4e3376b1684c1dd117d53e94480b2d7c9fe";
      sha256 = "14xskps1h07w2fwj78aw7sd6la0c8w2jwf5cwzsg67ikzr69iqmi";
    };

    installPhase = ''
      cp -r $src/* ~/.config/nvim
    '';
}

I have home.nix which runs my home manager stuff via nixos:

{ config, lib, pkgs, ... }:

with lib;

let
  home-manager = builtins.fetchTarball
    "https://github.com/nix-community/home-manager/archive/release-21.11.tar.gz";
in {
  imports = [ (import "${home-manager}/nixos") ];

  pkgs.callPackage ./nvchad.nix { };
  ...
}

However when I run this I get the following error:

error: syntax error, unexpected PATH, expecting ‘.’ or ‘=’, at /home/vagrant/nixos/home.nix:12:20
(use ‘–show-trace’ to show detailed location information)
make: *** [Makefile:8: pc] Error 1

Does anybody know how I can install a derivation from home manager?

1 Like

Close, you fail to understand the difference between a module and a derivation here :slight_smile:

Though note just calling an expression like you did is generally not possible in functional programming languages. You need at least an assignment.

The simple answer is:

{ config, lib, pkgs, ... }:

let
  home-manager = builtins.fetchTarball
    "https://github.com/nix-community/home-manager/archive/release-21.11.tar.gz";
in {
  imports = [ (import "${home-manager}/nixos") ];

  home.packages = [
    (pkgs.callPackage ./nvchad.nix { })
  ];
  ...
}

Basically, modules are where the configuration options are set. In here you can only use the settings defined here: Appendix A. Configuration Options.

Those options can however refer to derivations that will be installed alongside whichever files they create. home.packages is where all derivations you want to install as packages for your user should go.

If you’re interested how modues work, and how they differ from derivations, it’s explained in more detail on the wiki.

I also just noticed this. That won’t put those files in your ~/.config/nvim, it will put them in /nix/store/<long-sha>-nvchad/.config/nvim, and neovim won’t be able to read them.

It’s also incorrect, you need to put your files in $out/.config/nvim for that to work. But I don’t suggest writing a derivation for this at all.

Instead, you will need to use xdg.configFile, something like this:


{ config, lib, pkgs, ... }:

let
  home-manager = builtins.fetchTarball
    "https://github.com/nix-community/home-manager/archive/release-21.11.tar.gz";
  nvchad = pkgs.fetchFromGitHub {
    owner = "nvchad";
    repo = "nvchad";
    rev  = "c7a4d4e3376b1684c1dd117d53e94480b2d7c9fe";
    sha256 = "14xskps1h07w2fwj78aw7sd6la0c8w2jwf5cwzsg67ikzr69iqmi";
  };
in {
  imports = [ (import "${home-manager}/nixos") ];

  xdg.configFile.nvim = {
    source = nvchad;
    recursive = true;
  };
  ...
}

Note I’ve removed your derivation entirely; fetchFromGitHub already is a super simple derivation that just puts all the files from github in a directory.

We then use that derivation and symlink it to ~/.config/nvim using xdg.configFile. At least I think that will work, I haven’t tried it.


Other minor notes:

You must define something between these, or skip them entirely, nix doesn’t allow this syntax.

I recommend not doing this, especially in this case - you’re not actually using any lib functions :slight_smile: If you do use any, it’s clearer just to call them using lib.function.

Only use this when necessary, which is when you want to use something you assigned to an attribute as a variable in the same set. I.e., if you want to do something like:

stdenv.mkDerivation rec {
    name = "nvchad";

    src = fetchFromGitHub {
      owner = name;
    };
}
2 Likes

TLATER already commented on this a bit, but I wanted to elaborate.

The purpose of a derivation is to create nix store objects (often only one nix store object), reproducibly. Because of this goal, derivations run inside a sandbox that severely restricts what they can do. Basically they can only read store objects that they depend on (directly or indirectly), and can only write to:

  • the output store object(s)
  • a temporary directory (the PWD of the build process) which is deleted as soon as the derivation finishes running.

There are exceptions which get some external interaction capability in exchange for other restrictions, but they’re not relevant at the moment. (Except to say the fetchFromGitHub is one of them. It needs network access, clearly.)

This ensures the derivation functions as a definition of a store object. It doesn’t depend on any external factors, and can’t affect anything other than the ouput store object. You don’t exactly “run” it, so much as “realise” the definition. This is part of why nix is referred to as a “purely functional” package manager.

If you want to manage files outside the nix store with nix, you need some kind of run-time system that does so according to instructions from inside the nix store. Home-manager has a system like this already in place, which TLATER’s suggestion is using through the xdg.configFile option (which is implemented using the home.file option, which in turn allows you to put arbitrary files in place anywhere in your home directory).

1 Like

Thanks for such a detailed reply.

Yes I originally started with xdg.configFile."nvim".source = pkgs.fetchFromGitHub ..., however the full install requirement is to:

  1. clone the repos and
  2. place my configs inside the cloned repo directory

Cloning the repo worked well however when I try and put the configs into the repository it complains “file conflict for file .config/nvim” - even with trace level logging. I suspect this is because at the end of the day home manager is trying to keep the cloned directory free of changes?

I originally thought I would create a derivation that would clone the repo then copy the contents of the directory into my xdg config directory and then afterwards copy the configs in place - a hack i guess but the best way I could think of.

However you have rightly pointed out that my derivation isn’t going to work because I won’t be able to copy the src into my xdg config directory.

So back to the drawing board. How would you approach this?

IE if I do

File conflict for file ‘.config/nvim’ when trying to run the home.file function:

xdg.configFile."nvim".source = pkgs.fetchFromGitHub {
  owner = "nvchad";
  repo = "nvchad";
  rev = "84cc8ec750ba73ea19f2fa615d6682f8ee6785df";
  sha256 = "0nd31f45000rx8m90sw4g1r2j88mnigl3xi2vyz1qminrls918kq";
};

home.file.".config/nvim/jamestest".text = ''
  hello
'';

or

Error installing file ‘.config/nvim/jamestest’ outside $HOME when trying to run the xdg.configFile function

xdg.configFile."nvim".source = pkgs.fetchFromGitHub {
  owner = "nvchad";
  repo = "nvchad";
  rev = "84cc8ec750ba73ea19f2fa615d6682f8ee6785df";
  sha256 = "0nd31f45000rx8m90sw4g1r2j88mnigl3xi2vyz1qminrls918kq";
};

xdg.configFile."nvim/jamestest".text = ''
  hello
'';

TLATER’s suggestion differs from yours in that he set recursive = true; on the nvim directory. That makes home-manager recreate the entire filestructure in your home directory, rather than just symlinking the whole directory into place. That should allow coexistence with other changes within the directory.

1 Like

It’s not just trying to do that, it’s fundamentally impossible to modify the directories this way. They’re symlinks into the nix store, which is read-only, so you cannot write files into them (but @tejing is right, using recursive will let you do that to an extent, I just think modifying the derivation is less of a hack).

Ah, actually that’s a pretty good way to do it, you just need to think about it a bit differently. Instead of modifying what’s in your xdg directory, modify what is in the derivation (i.e., what’s in the cloned repository).

We’d, instead of using the fetched repo as our source, use our derivation as a source:

{ config, pkgs, ... }:

let
  home-manager = builtins.fetchTarball
    "https://github.com/nix-community/home-manager/archive/release-21.11.tar.gz";
  nvchad = pkgs.callPackage ./nvchad.nix { };
in {
  imports = [ (import "${home-manager}/nixos") ];

  xdg.configFile.nvim = {
    source = nvchad;
    recursive = true; # This is optional
  };
  ...
}

And then use patchPhase to modify our output:

{ stdenv, fetchFromGitHub }:

stdenv.mkDerivation {
  name = "nvchad";

  src = fetchFromGitHub {
    owner = "nvchad";
    repo = "nvchad";
    rev  = "c7a4d4e3376b1684c1dd117d53e94480b2d7c9fe";
    sha256 = "14xskps1h07w2fwj78aw7sd6la0c8w2jwf5cwzsg67ikzr69iqmi";
  };

  patchPhase = ''
    echo 'hello' > jamestest
  '';

  installPhase = ''
    cp -r . $out
  '';
}
2 Likes

Thanks TLATER this pointed me in the right direction and got it completely working now.

Thanks for everyones help

1 Like

Hello @jamesla ,

May I ask how did you finally get this to work? I’m trying to build from what is discussed here, but I’m having issues getting NvChad to load/see my custom configs.