diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..9338933 --- /dev/null +++ b/README.adoc @@ -0,0 +1,440 @@ += nix-wrapper-manager-fds +:toc: + + +wrapper-manager-fds is foodogsquared's reimagining of https://github.com/viperML/wrapper-manager/[wrapper-manager]. + +[CAUTION] +==== +wrapper-manager-fds is considered unstable and the author is still freely experimenting with various aspects of the project including the module options, library set API, and ease of use for third-partyuse. +Expect constant breakages here and put on a hard hat before exploring this part of the town. +==== + +As a recap, the original aim of wrapper-manager is to pave a way of configuring Nix environments through wrappers. +There has been https://discourse.nixos.org/t/declarative-wrappers/1775[interest on] https://github.com/NixOS/rfcs/pull/75[this feature] footnote:[I mean, a part of the nixpkgs package set has dedicated wrappers for some packages such as GIMP, Inkscape, and Blender.] and I sometimes configure programs through wrappers instead of the typical module system. +It has its use cases such as quickly creating variants of the same program for specific reasons (for example, creating specialized versions of yt-dlp for downloading audio, video, and more). +But why not create something more than that? +And that's how wrapper-manager-fds came to be. + +[#installation] +== Installation + +[#installation-channels] +=== Channels + +You can install wrapper-manager-fds with https://zero-to-nix.com/concepts/channels[Nix channels]. + +[source, shell] +---- +nix-channel --add https://github.com/foo-dogsquared/wrapper-manager-fds/archive/master.tar.gz wrapper-manager-fds +nix-channel --update +---- + +Then in your environment configuration, you'll have to import it. +For more details, see <>. + +[#installation-pinning-tool] +=== Pinning tool + +A recommended (non-flakes) way to install Nix dependencies is to use a pinning tool. +There are a handful of pinning tool out there but in this case, we'll use https://github.com/andir/npins[npins] as our tool of choice. +Assuming you have already initialized npins, you can simply add wrapper-manager-fds to your Nix project with the following command. + +[source, shell] +---- +npins add github foo-dogsquared wrapper-manager-fds +---- + +Similar to channels installation, you'll have to import the <> object. + +[#installation-manual-pinning] +=== Manual pinning + +Though not recommended, you could manually pin the Nix library yourself like in the following code. + +[source, nix] +---- +{ pkgs, ... }: + +let + wrapper-manager-fds = builtins.fetchTarball "https://github.com/foo-dogsquared/wrapper-manager-fds/archive/master.tar.gz"; + wrapperManager = import wrapper-manager-fds { }; + wrapperManagerLib = import wrapperManagerLib.lib { inherit pkgs; } +in +wrapperManagerLib.env.build { } +---- + +[#installation-flakes] +=== Flakes + +This project also has a https://zero-to-nix.com/concepts/flakes[flake object]. +In your flake definition, just import it as one of the inputs. +Unlike the other methods, the flake output is the user entrypoint so no need to import it or anything. + +[source, nix] +---- +{ + inputs.wrapper-manager-fds.url = "github:foo-dogsquared/wrapper-manager-fds"; + + outputs = { nixpkgs, ... }@inputs: { + nixosConfigurations.desktop = nixpkgs.lib.nixosSystem { + modules = [ + ./hosts/desktop.nix + inputs.wrapper-manager-fds.nixosModules.default + ]; + }; + }; +} +---- + +[#user-entrypoint] +=== User entrypoint + +Most of the things you need from the project are all retrieveable from the entrypoint of this project (`default.nix`) which is an attribute set closely structured from a typical Nix flake that you see. +For example, if you want the NixOS module for wrapper-manager, you could refer to `nixosModules.default`. +Here's the breakdown of the attrset entries in the entrypoint. + +* `lib` is an attribute set containing the main functions: `eval` and `build`. +(More details to use them are in <>.) + +* `nixosModules` contains a set of NixOS modules for wrapper-manager-fds integration. +You could get the main module with the `default` attribute. + +* `homeModules` are for home-manager modules and similarly structured to `nixosModules` attribute. + +* `wrapperManagerLib` contains a path intended to be imported within your Nix code. +It simply contains the library set that can be used outside of the wrapper-manager-fds module environment. + + +[#getting-started] +== Getting started + +Now that you have wrapper-manager-fds on the go, let's have a breakdown of what it is, exactly. +The project itself is made of different parts which you can use for different purposes. + +[#the-module-environment] +=== The module environment + +One part of the project is the module environment. +Just like https://github.com/nix-community/home-manager[home-manager] and https://github.com/nix-community/disko[disko] and https://github.com/viperML/wrapper-manager[the original source], wrapper-manager-fds comes with its own module environment. +Instead of a home environment from home-manager or an installation script from disko, wrapper-manager-fds module environment evaluates to a derivation containing a wrapper script. footnote:[While the original source also evaluates similar to that, it typically involves a set of wrappers inside of the same configuration environment rather than a single wrapper.] +This can be thought of as a declarative layer over `makeWrapper` build hook from nixpkgs. + +Here's a very simple example of a wrapper for Neofetch. + +[source, nix] +---- +{ lib, pkgs, ... }: + +{ + wrappers.neofetch = { + arg0 = lib.getExe' pkgs.neofetch "neofetch"; + appendArgs = [ + "--ascii_distro" "guix" + "--title_fqdn" "off" + "--os_arch" "off" + ]; + }; +} +---- + +Or if you want fastfetch... + +[source, nix] +---- +{ lib, pkgs, ... }: + +{ + wrappers.fastfetch = { + arg0 = lib.getExe' pkgs.fastfetch "fastfetch"; + appendArgs = [ "--logo" "Guix" ]; + env.NO_COLOR = "1"; + }; +} +---- + +Or even both in the same configuration (which you can do). + +[source, nix] +---- +{ + imports = [ + ./fastfetch.nix + ./neofetch.nix + ]; +} +---- + +You could even create https://specifications.freedesktop.org/desktop-entry-spec/latest/[XDG desktop entry] files useful for the application to be launched through an application launcher/menu. +For example, you could create an executable and a desktop entry to launch a custom Firefox profile in your home-manager configuration. + +[source, nix] +---- +{ config, lib, pkgs, ... }: + +{ + programs.firefox.profiles.custom-profile = { + # Put some profile-specific settings here. + }; + + wrapper-manager.packages.browsers = { + wrappers.firefox-custom-profile = { + arg0 = lib.getExe' config.programs.firefox.package "firefox"; + prependArgs = [ + "-P" "custom-profile" + ]; + xdg.desktopEntry = { + enable = true; + settings = { + desktopName = "Firefox (custom-profile)"; + startupNotify = true; + startupWMClass = "firefox"; + icon = "firefox"; + mimeTypes = [ + "text/html" + "application/xhtml+xml" + "application/vnd.mozilla.xul+xml" + "x-scheme-handler/http" + "x-scheme-handler/https" + ]; + }; + }; + }; + }; +} +---- + +[#as-a-library] +=== As a library + +wrapper-manager also comes with a library set which you can use to evaluate and build wrapper-manager packages yourself. +This is found in the `wrapperManagerLib` attribute from the user entrypoint where it needs an attribute set containing a nixpkgs instance in `pkgs`. + +[#src:example-lib-build] +.An example of importing wrapper-manager library +[source, nix] +---- +{ pkgs }: + +let + wrapper-manager = import (builtins.fetchgit { }) { }; + + wmLib = import wrapper-manager.wrapperManagerLib { inherit pkgs; }; +in +wmLib.env.build { + inherit pkgs; + modules = [ ./fastfetch.nix ]; + specialArgs.yourMomName = "Joe Mama"; +} +---- + +Here's a quick rundown of what you can do with the library. + +* Evaluate a wrapper-manager module with `env.eval` where it accepts an attrset similar to the <> containing a list of additional modules, the nixpkgs instance to be used, and `specialArgs` to be passed on to the `lib.evalModules` from nixpkgs. + +* Build a wrapper through `env.build` returning a derivation of the wrapper. +It accepts the same arguments as `env.eval`. + +There is also `lib` attribute if all you want to do is to build and/or evaluate a wrapper-manager configuration. +It only contains the function from `env` subset which contains `build` and `eval`. + +[#as-a-composable-module] +=== As a composable module + +The most user-friendly way of using wrapper-manager would be as a composable nixpkgs module of an existing environment. +wrapper-manager provides a Nix module specifically for NixOS and home-manager environment. footnote:[Any other environments are basically unsupported and if you like to use it outside of NixOS and home-manager, you're on your own.] +You can import them through the `{nixos,home}Modules.default` from the user entrypoint of the project. + +Most of the things set up here are implemented to make declaring wrappers ergonomic with the environment. +For a start, wrapper-manager-fds sets up a module namespace in `wrapper-manager`. +Here's a quick breakdown of the features that the module has. + +* Passes the wrapper-manager library through `wrapperManagerLib` module argument. +This is nice if you want to only use wrapper-manager to quickly create wrappers inside of the configuration without using the wrapper-manager NixOS/home-manager integration module. + +* You could declare wrappers through `wrapper-manager.packages.` where each of the attribute value is expected to be a wrapper-manager configuration to be added in its respective wider-scope environment. + +* You could include other modules through `wrapper-manager.sharedModules`. +This is useful for extending wrapper-manager inside of the configuration environment. + +Here's an example of adding wrappers through wrapper-manager inside of a home-manager configuration. +The following configuration will create a wrapped package for yt-dlp with an additional wrapper script named `yt-dlp-audio` and `yt-dlp-video`. + +[source, nix] +---- +{ config, lib, pkgs, ... }: + +{ + home.packages = with pkgs; [ + flowtime + blanket + ]; + + wrapper-manager.packages = { + music-setup = { + basePackages = [ pkgs.yt-dlp ]; + wrappers.yt-dlp-audio = { + arg0 = lib.getExe' pkgs.yt-dlp "yt-dlp"; + prependArgs = [ + "--config-location" ./config/yt-dlp/audio.conf + ]; + }; + wrappers.yt-dlp-video = { + arg0 = lib.getExe' pkgs.yt-dlp "yt-dlp"; + prependArgs = [ + "--config-location" ./config/yt-dlp/video.conf + ]; + }; + }; + }; +} +---- + + +[#development] +== Development + +If you want to hack this hack, you'll need either Nix with flakes enabled (`experimental-features = nix-command flakes` in `nix.conf`) or not. +Either way, this should be enough to cater both flake- and non-flake users. + +This project supports the current stable and unstable version of NixOS. +Specifically, we're looking out for the nixpkgs instance both of these versions has. +As an implementation detail, we pin these branches through https://github.com/andir/npins[npins] which both flakes- and non-flake-based setups uses. +Just be familiar with it and you'll be fine for the most part. footnote:[Most likely, you don't even need to interact with it for the most part since handling update cadence is handled automatically through the remote CI.] + +Setting up the development environment should be easy enough. + +* For flake users, you can just reproduce the development environment with `nix develop`. +* For non-flake users, you can do the same with `nix-develop`. + +[#development-library-set-and-modules] +=== Library set and modules + +This Nix project has a test infrastructure set up at link:./tests[./tests] covering the library set nad the wrapper-manager module environment. +For its library set, it makes use of the nixpkgs library and a JSON schema to validate if it passes the whole tests. +To make use of it, you can run it with the following commands. + +* For flake users, you can run `nix flake check`. +* For non-flake users, you can do the same with `nix-build tests/ -A libTestPkg` or `nix build -f tests/ libTestPkg`. + +The derivation output should be successfully built if all of the tests in the suite passes. +Otherwise, it should fail and you'll have to see the build log containing all of the tests that failed. + +On another note, there is a quicker way of checking the test suite with `nix eval -f tests lib` (or `nix-instantiate --eval --strict tests/ -A lib`) where it contains the raw test data which is useful if you don't want to essentially build a new derivation each time. +It is also quicker to eyeball results in this way especially if you're always working with the tests anyways. + +[#development-website] +=== Website + +This project also has a website set up with https://gohugo.io/[Hugo]. +The files that you need to see are in link:./docs[./docs] directory. + +* For flake users, you can build the website with `nix build .#website`. +* For non-flake users, you can do the same with `nix-build docs/`. + +There is also a dedicated development environment placed in `docs/shell.nix` but this should be a part of the primary development environment already. +You can enter it with `nix develop .#website` or `nix-shell docs/`. + +Just take note that the website also requires the NixOS options which comes in a JSON file. +This should be already taken care of in the package definition of the website but otherwise it is something that you'll have to be aware of. + +The more important task to developing this part of the project is continuously getting feedback from it. +You can do so but + + +[#goals-and-non-goals] +== Goals and non-goals + +As a Nix project, wrapper-manager-fds aims for the following goals. + +* Create an ecosystem of creating them wrappers, mainly through its library set and the module environment. + +* Make creating wrappers ergonomic for its users. +Not necessarily user-friendly but it should easy enough to get started while allowing some flexibility, yeah? + +* Make a nice environment for creating custom wrappers which is already quite possible thanks to the heavy lifting of the nixpkgs module system. + + +[#faq] +== Frequently asked questions (FAQ) + +[qanda] +Is this compatible with the original wrapper-manager?:: +Nope. +It is a reimagining with a completely different way of using it so it won't be fully compatible with it from the start. + +Why reimplement this anyways?:: +For funsies and also because there are some things I find not so great with using the project. +https://github.com/viperML/wrapper-manager/tree/307eb5c38c8b5102c39617a59b63929efac7b1a7[As of this writing], using wrapper-manager to simply create wrappers anywhere is a pain. + +Why not just incorporate the wanted changes into the original implementation?:: +While it could be done, there will be some unwanted major changes into the project which would cause inconvenience to its users anyways so it isn't a good idea. + +Can't you just create a wrapper with `pkgs.makeWrapper` and such from nixpkgs?:: +Yeah, you can. +There's nobody stopping you from doing so and surely there's no hitman preparing to assissinate right behind you as you about to deny wrapper-manager-fds and smugly type `make` in `makeWrapper`. +In fact, wrapper-manager uses `makeWrapper` as the main ingredient. +Just think of wrapper-manager as a declarative version of that among the bajillion ways of making wrappers in the Nix ecosystem. ++ +As an additional point, there are still use cases for it even with a simple `pkgs.writeShellScriptBin`. +In fact, if you have a situation like say having to create a one-off wrapper script to be added in a NixOS system, you can simply do the following: ++ +[source, nix] +---- +let + ytdlpAudio = pkgs.writeScriptBin "yt-dlp-audio" '' + ${pkgs.yt-dlp}/bin/yt-dlp --config-location "${../../config/yt-dlp/audio.conf}" $@ + ''; +in +{ + environment.systemPackages = [ ytdlpAudio ]; +} +---- ++ +BAM! +No need for wrapper-manager! + +Why use the module system?:: +Because screw you, that's why!!! +Am I stupid and lazy for basically using a battle-hardened configuration system library such as nixpkgs module system? footnote:[The answer is yes to both!] ++ +Seriously though, the main reason is pretty simple: it is quite established and a battle-hardened part in the Nix ecosystem. +It has gone through the test of time and the numerous 339 users of the entire Nix ecosystem are quite adamant in the declarative aspect of the Nix thingy. +So... why not use it. + +Any problems (and impending explosions) when using this project?:: +As far as I can tell, not much (especially explosions) but there are a few caveats you need to know. +Just know this is something the author is trying to resolve. ++ +-- +* wrapper-manager-fds is not great at handling double wrappers. +It just naively wraps a package and goes on its merry way. + +* wrapper-manager-fds doesn't handle any replacement for the related files very well. +This is especially noticeable in large desktop-adjacent packages such as Inkscape, Firefox, and Blender with a bunch of plugins and whatnot where they have their own wrappers. +This means you cannot set `programs.NAME.package` or something similar with it. +-- + + +[#acknowledgements] +== Acknowledgements + +I found a bunch of things for inspiration (READ: to steal ideas from). +Here's a list of resources I've found. + +* The original source of the reimagining, of course: https://github.com/viperML/wrapper-manager[wrapper-manager]. + +* https://github.com/NixOS/rfcs/pull/75[Nix RFC 75] which also comes https://github.com/NixOS/nixpkgs/pull/85103[with its implementation and discussion around what works and whatnot]. + +* https://discourse.nixos.org/t/pre-rfc-module-system-for-wrappers-in-nixpkgs/42281[This NixOS Discourse post loudly thinking about the same idea.] + + +[#copyright] +== Copyright + +This project is licensed under MIT License (SPDX identifier: https://spdx.org/licenses/MIT.html[`MIT`]). +Just see link:./LICENSE[`./LICENSE`] for full text and details and whatnot. + +The documentation (except for the code examples), on the other hand, is licensed under https://www.gnu.org/licenses/fdl-1.3.txt[GNU Free Documentation License] v1.3 only with no "Invariants" section (SPDX identifier: https://spdx.org/licenses/GFDL-1.3-no-invariants-only[`GFDL-1.3-no-invariants-only`]) +You can see either the link or link:./docs/LICENSE[`./docs/LICENSE`] for more info. +The code examples, similar to the project codebase, are licensed under MIT with the same conditions apply and all that jazz.