Nix-wrapper-modules: like home manager for directly wrapping packages

https://birdeehub.github.io/nix-wrapper-modules/

For anyone who likes to wrap their programs with configuration directly so they can use them outside of home manager and nixos.

Do you like being able to override that config in a sensible way later in, for example, a dev shell?

Do you like not losing access to .override and .overrideAttrs while still being able to wrap something in an overrideable way?

What if I told you it uses the module system, but returns a package?

8 Likes

I like this! I think this is the first project I see that matches the closest to what I’ve been envisioning & incrementally doing in my own dotfiles these past few years :grinning:

I like how you did the flags, I don’t have a good thing for that in my impl yet. I only have an incomplete draft for setting flags and declaratively doing env var overrides…

My impl has a few additional nice features that you might be interested in:

  • (basic vision) Most of my programs are not configured through Nix, but Nix is used to package the config and make it available in different ways with its dependencies.

  • Packaged configs can seemlessly be either with editable or fully-packaged config files (by only flipping a switch) to avoid having to rebuild the package after each change in a non-Nix config file when I don’t want to.

  • Multiple outputs, so a given config can be used standalone or integrated in a home-manager setup (with editable files) but also to be able to export generated config dirs or other intermediate derivations

  • Referenced packages and additional dependencies of a given config can be overridden individually (e.g. from outside of its first evaluation).

  • Each config has an ID (must be defined, can be overriden of course) to identify state, to allow multiple installed varients of an (e.g. editable) config in a home-manager setup.

  • My own way to re-wrap N times (that I just call ‘extend’) can access the ‘previous’ config, to extend (not just replace) any config options.

My dotfiles only have defined a few config module based on this system for zsh, tmux & neovim, but I’m planning to do a lot more like git, mpv, karabiner-element… Someday… :sweat_smile:

I’ll probably take some of your ideas though, looks like you had a few more iterations than me on the option names! (and since you’re based on the existing wrappers projects)

I’m not sure how compatible my vision is to yours, but maybe we can collaborate :thinking:


Few links if you want to take a look:

2 Likes

Glad youre excited!

Multiple outputs, so a given config can be used standalone or integrated in a home-manager setup (with editable files) but also to be able to export generated config dirs or other intermediate derivations

So, this just outputs a derivation. So, by default you can use it anywhere. Just install it wherever it asks for packages. As far as intermediate options, or being able to toggle things, you can ALWAYS call .wrap on it and get a new package! You could simply define an option, and then you can .wrap { theopt = true; } when you want it.

As far as outputting extra items, you can add things to passthru, which get upvalued, its basically just like putting stuff into the set you return, except the set is also a package.

In my tmux config, I output a nixos module via passthru for setting utempter on nixos machines

You get the WHOLE module system PER package. So, anything you can do in a module, and to a module option, you can do to this. lib.mkDefault, etc…

Also, everything assigned in config is technically accessible from outside via any evaluation of the module, through the .config attribute, which is also accessible via passthru.configuration or just .configuration on the final package.

Packaged configs can seemlessly be either with editable or fully-packaged config files (by only flipping a switch) to avoid having to rebuild the package after each change in a non-Nix config file when I don’t want to.

I don’t specify if anything has to be in the store other than the input package that you plan to wrap. If you want to use a runtime location in the flags or variables set in your wrapper, that works just fine.

You could define a toggle option in your own config of the module, which if you toggle it swaps between 2 different locations of your choosing, one could be in the store, the other just a regular path.

As far as exporting the config file or directory as a package, that would be down to individual wrapper modules deciding to export that or not, but they can!

1 Like

Could you rewrite your post and Readme without presupposing that your audience is familiar with the YouTube video and the other repo? I’m interested but having a hard time understanding because your explanation keeps referring to a YouTube video and another repo that I don’t know about.

This is a reasonable point.

I could put the explanation of that stuff afterwards?

The video is a good introduction to the topic still though, and that is a link to it so you can watch it if you want, as most of it is about the concept of wrapping packages and explaining what problems this project addresses. It would be weird though, to link a video recommending a different repo at the end, without telling you also that this repo is better than that one and why, despite surface level similarities.

But currently below that is an explanation of useage, and then the rest of the docs is dedicated to the options available to you, the builtin options or “core options” and then the rest, such as those offered by importing wlib.modules.default. They are just modules. They work like modules do. The ones that do not set the package option, you can import and use for making ones which do. They are listed separately in the docs.

I would like to state now that better docs are indeed on the roadmap but also currently it is still a priority to explain how this project is meaningfully different from the mentioned repo, as that one got a lot of attention recently from said video, and they seem, at first, quite similar until you realize how much that other one cannot do. And then it would be rude to do that, without mentioning that I was originally intending to contribute the changes. I wanted to split those things up into different sections from the main introduction, but I couldnt figure out how to do that in a nice way at that moment and also wanted to announce the repo, so that was how the readme ended up.

So, yes, I can, soon, but in the meantime, the docs there are still a quite good explanation.

I might break that intro section up into a separate page and have a getting started and what is this repo sections or something. But for now, it will likely be this way for a week or so until I can get to rewriting that. One of the things I am currently working on is improving docgen, and I will definitely address the readme again at least when I do that, if not before.

I’m just looking for a simple explanation of why I would want to use this and what problem it solves without having to watch a YouTube video or read another repo. Usually that’s the first thing people include in a Readme file.

FWIW, I saved the YouTube video to watch later, but in principle I don’t think developers should completely outsource the work of explaining their projects like this.

2 Likes

Point taken. Although I don’t think “completely” is the right descriptor for how much was outsourced, I wrote a lot of option descriptions, and I did make sure that the usage was explained, and I made it actually have a website as opposed to no website, but I did outsource the description of “why should I use a wrapper script generated with nix to configure my program and why is doing it your way with modules nicer than using pkgs.symlinkJoin, pkgs.makeWrapper, and/or pkgs.writeShellScriptBin myself”

There is definitely more work to do.

But for those who already use wrapper scripts, that part would not need to be explained, of course. I suppose I was overly confident in how familiar people would be with the concept, as it is something very useful and very common on nix.

@dragon_logic, How did I do? Better? Worse? There will be more improvements and changes over time of course but I would like to know if this is better or worse. I am not as good at writing english as I am at writing code.

I ended up improving docgen a little bit and deciding it was enough of an improvement to push so I made some updates to the readme as well.

Also, you should prefer to view the website, not the readme directly, as it has a LOT more info. Although, the home page of the website is also the readme.