Reputation: 1437
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:
Create an expression for Bass patching the Makefile to install the files under $out.
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
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