pkgs.runCommand not being run in Home-Manager module

I’m attempting to make a wallust integration into home manager that will allow users to have automatic color configuration similar to Stylix using wallust as a backend to generate schemes. I’m new to creating any sort of nix package or new home-manager module and I’ve only been using NixOS for about two months so please be patient and I’d love some help learning more about creating modules and packages and getting better in my Nix skills as a whole.

module.nix

 schemeJSON = builtins.trace "Generating..." 
    pkgs.runCommand "scheme.json" { 
      HOME = "./";
    } ''
      echo IS THIS WORKING?
      ${wallust}/bin/wallust run ${cfg.path}
      mkdir $out
      cp -v .cache/wallust/*.json $out/scheme.json
    '';

What I’m expecting to have happen is that the above pkgs.runCommand will be run and generate a colorscheme json file during home-manager switch --flake . From what I can tell with changing things around and adding different expressions inside the let in expression nothing is being run. However when messing around with my home.nix I can see that the walnix options in the following expression are being set and I can get it to error if I misspell or add something not in the options set in module.nix.

I’ve included the entirety of my flake.nix and module.nix below. I’d love any suggestions on this problem and any good resources on learning how to write my own home-manager modules besides just reading other packages modules.

flake.nix

{
  description = "Walnix flake";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
    base16.url = "github:SenchoPens/base16.nix";

    # Thank you matthew for example
    wallust.url = "git+https://codeberg.org/explosion-mental/wallust.git";
  };

  outputs = { self, nixpkgs, base16, wallust, ... }@inputs : 
  {
    homeManagerModules = {
      walnix = (import ./home-manager/module.nix) {
        inherit base16 wallust;
      };
      default = self.homeManagerModules.walnix;
    };
  };
}

module.nix

{ wallust, base16, ... }: {
  pkgs, 
  lib, 
  config,
  ...
}:
with lib; let 
  cfg = config.walnix;
  schemeJSON = builtins.trace "Generating..." 
    pkgs.runCommand "scheme.json" { 
      HOME = "./";
    } ''
      echo IS THIS WORKING?
      ${wallust}/bin/wallust run ${cfg.path}
      mkdir $out
      cp -v .cache/wallust/*.json $out/scheme.json
    '';
in {
  options.walnix = {

    enable = mkEnableOption {
      description = "Home manager Wallust integration";
      default = false;
    };

    path = mkOption {
      type = types.path;
    };

    scheme = mkOption {
      type = types.oneOf [
        types.path
        types.attrs
        types.lines
      ];
      default = builtins.fromJSON schemeJSON;
    };

    backend = mkOption {
      type = types.nullOr types.enum [
        "full"
        "resized"
        "wal"
        "thumb"
        "fastresize"
      ];
      description = ''
        Use this option to dictate which backend to wallust will use to generate a pallette.

        Default is selected by Wallust
      '';
    };
  };
}

Nix does lazy evaluation. From what you’ve shared, you’ve defined some options, but nix will never touch these (besides simple syntax checks) unless you actually evaluate the code, which in a home-manager setting means you need to use those options in a config block somewhere. I don’t know where your home-manager config currently lives or how you intend to depend on this, so I can’t give much more advice.

You’ll also find that you’re creating a directory called scheme.json, which contains a file called scheme.json, and then you tell nix to turn that directory into a JSON file. Besides IFD being bad practice for build dependency resolution reasons, a directory isn’t a file that can be read :wink: But we can figure those parts out once you’ve started actually evaluating anything.

1 Like

Ah thank you. I had assumed it was something to do with the lazy evaluation but couldn’t figure out when it was being used! What is IFD and what would you suggest as an alternative to reading in the file as nix attrset (once it’s no longer a directory lol)?

IFD is short for import-from-derivation. In a nutshell, anytime you make nix read files from the nix store. To do this, nix needs to schedule builds in multiple steps; first doing a build or something to produce the derivation to read from, re-evaluating after reading, and then scheduling whatever came out of that.

This increases evaluation complexity dramatically and forces parallelization to be only partial. It’s best avoided, and I believe prohibited in nixpkgs precisely to avoid nixpkgs eval from becoming too slow to be done at the scale needed.

The typical solution is just to commit the file-to-be-generated alongside the repository so you don’t have to do this; pretty common for lockfiles and such. In this case, though, I would avoid running the default settings through nix eval at all.

Depending on how wallust works, you could either:

  • Not specify the default values and just rely on wallust substituting default values for missing config.
  • Make a wrapping shell script that generates the default json file at runtime if it doesn’t exist yet before exec-ing wallust.
    • If merging user values into these defaults is important, you can use jq to do that.

If you’re doing this because you want the default attrset in there for documentation purposes; don’t bother, just add a link to the file in the upstream repo/documentation to the description.

Obviously IFD isn’t prohibited (yet), so you can of course still do what you’re doing now if those options don’t seem appealing, but I really do recommend using as little IFD as possible, even just one can slow builds significantly, it’s a bad habit to get into.

1 Like

Don't allow IFD in flakes by default by edolstra · Pull Request #5253 · NixOS/nix · GitHub seems to indicate some flake-based commands actually disable IFD. And to note, you can disable IFD overall on your own systems.

EDIT: though I wouldn’t recommend disabling IFD if you need to use haskell, since that ecosystem heavily relies on it.

1 Like