b73
b73

Reputation: 1437

massaging packages that install files outside of nix-store

I'm using nix package-manager on macOS (Sierra).

My intention is to write a nix expression that will install the existing fish nix package along with the Bass fish plugin.

There are no existing expressions in nixpkgs for Bass, but the git repo contains a Makefile. This Makefile attempts to copy files to the $HOME dir. This is a problem as installing files outside of the nix-store is clearly not desirable and $HOME is not set when I build my package.

I can recognise why it's not desirable for nix packages to install files outside of the nix-store - in functional programming terms it's akin to a side-effect. But I'm also not clear on how to solve my problem:

By default Fish requires plugins such as Bass to be installed under $HOME/.config/fish/. Fish does provide a means to customise the config path by specifying the environment variable XDG_CONFIG_HOME. So I was thinking of doing something like this:

  1. Create an expression for Bass patching the Makefile to install the files under $out.

  2. Create an expression that installs fish and uses Bass as a build input. Use wrapProgram to set XDG_CONFIG_HOME pointing to the Bass install path in the nix-store.

Does this sound like the right approach? Are there alternative/better ways of solving this?

Thanks

Upvotes: 1

Views: 1194

Answers (1)

b73
b73

Reputation: 1437

This is the solution that I have gone with:

Expression for bass: nix_local/pkgs/fish_plugins/bass/default.nix

{stdenv, fetchFromGitHub}:

let
  version = "0.0.1";

in
stdenv.mkDerivation rec {
  name = "bass-${version}";

  src = fetchFromGitHub {
    owner = "edc";
    repo = "bass";
    rev = "1fbf1b66f52026644818016015b8fa9e0f639364";
    sha256 = "12bp8zipjbikasx20yz29ci3hikw0ksqlbxbvi2xgi4g6rmj7pxp";
  };

  patchPhase = ''
    substituteInPlace Makefile --replace \
    "~/.config/fish" \
    $out/.config/fish
  '';
}

Expression for fish_with_config: nix_local/pkgs/fish_with_config/default.nix

{stdenv, fish, bass, makeWrapper}:

let
  version = "0.0.1";

in
stdenv.mkDerivation rec {
  name = "fish-with-config-${version}";

  src = ./.;

  buildInputs = [fish bass makeWrapper];

  installPhase = ''
    mkdir -p $out/.config/fish/functions

    cp -r $src/.config/* $out/.config

    cp -r ${bass}/.config/fish/functions/* \
    $out/.config/fish/functions/

    mkdir -p $out/bin
    ln -s ${fish}/bin/fish $out/bin/fish

    wrapProgram $out/bin/fish --set XDG_CONFIG_HOME "$out/.config"
  '';
}

The Fish program is wrapped in order for it's config to be stored in the nix-store. This enables us to symlink the functions from Bass and also copy any additional config files from the local $src dir. Additional plugins could be symlinked in the same way.

The local src dir for the derivation contains the following files:

pkgs/fish_with_config
├── .config
│   └── fish
│       ├── fishd.8c8590486f8c
│       └── functions
└── default.nix

The .config/fish/fishd.8c8590486f8c file is a "universal variable file" which Fish requires in order to operate. In a standard Fish installation this file is stored under ~/config/fish/ and is created the first time you enter interactive mode. The contents of this file would typically change over time as users interact with Fish settings.

The fish_with_config derivation stores the Fish config in the nix-store, which means it can't be modified at a latter date (not writable). This means all the config settings need to be done upfront as any attempts by the user to modify the settings will result in permission errors - this is obviously a little inconvenient, but not a show stopper for me.

It's probably worth noting that the universal variable file may change with different releases of Fish and as such if I was to build fish_with_config with a newer version of Fish I would first determine it's default content by running fish in a nix-shell and inspecting the auto generated file under ~/config/fish/.

In summary the above works nicely, I have access to bass and any additional user defined functions I choose to "bake in" (pkgs/fish_with_config/.config/fish/functions).

If you see anything that could be improved or handled more idiomatically let me know.

Upvotes: 2

Related Questions