Some weeks ago, xref:{nix-flakes-post}[I've made a post describing how to combine traditional dotfiles and NixOS config with Nix flakes].
That seems all well and good but it comes with a few problems.
* Changes from the dotfiles have to be pushed first to the remote repo.
* Then, the dotfile flake input have to be updated.
After pushing changes from the dotfiles, you have to update it as depicted from xref:{nix-flakes-post}#workflows-and-its-caveats[Workflows and its caveats].
While managing with flakes is less tedious than managing it with fetchers, you're essentially just reducing the source of updates from two to one.
While it is reproducible, it's a tedious process especially if you need immediate changes from your dotfiles.
One of the responses I got from the aforementioned post is link:https://www.reddit.com/r/NixOS/comments/11kme1n/comment/jb80qgy[an alternative solution for managing dotfiles].
I'm more likely to use home-manager's link:https://github.com/nix-community/home-manager/blob/bb4b25b302dbf0f527f190461b080b5262871756/modules/files.nix#L64[mkOutOfStoreSymlink] if I find myself in situations where I need non-generational or mutable file content.
I have a trivial personal HM module that git-clones things to a desired path as part of the activation script if they're absent and then manage them out of band from then on.
== A better way to manage traditional dotfiles in home-manager
As hinted from the quoted statement, github:nix-community/home-manager[`mkOutOfStoreSymlink`, rev={home-manager-rev}, path=modules/files.nix] is a home-manager function that accepts a path string and returns a derivation.
This derivation contains a builder script that links the given path to a store path.
Using it should be simple.
The following listing simply links my dotfiles located on `/home/foo-dogsquared/dotfiles` and creates a path on the Nix store.
This pretty much allows us to interact with various options from home-manager that normally accepts a link:https://nixos.org/manual/nix/stable/glossary.html#gloss-store-path[store path].
In my case, I mainly use it for linking various files with `home.file.<name>.source`, `xdg.configFile.<name>.source`, and so forth.
To give some more context, here's an example usage of the function with my use case.
[#lst:dotfiles-mkoutofstoresymlink]
.`home.nix`, this time with `mkOutOfStoreSymlink` instead of flakes
[source, nix]
----
include::./assets/mkoutofstoresymlink.nix[]
----
[#sidebar:what-is-dotfiles-yet-again]
.What is `dotfiles` yet again?
****
Just to continue the tradition from the last post, `dotfiles` is now a derivation from `mkOutOfStoreSymlink`.
The very same type as xref:{nix-flakes-post}#sidebar:what-is-dotfiles-this-time[to what `fetchFromGitHub` returns].
Since it is a derivation, it will evaluate to the output path if coerced into a string which should be a store path that is symlinked to the dotfiles.
This is why the code works still unchanged for the most part.
****
Compared to the approach of making the dotfiles as a flake input, this reduces the reproducibility of our home-manager configuration a little bit.
Instead of fully including the dotfiles, we only assume we have the dotfiles at the given location.
However, this does remove the xref:{nix-flakes-post}#workflow-and-its-caveats[workflow of managing the flake input and its caveats] altogether.
You don't have to do `nix flake update` or anything else in your NixOS config and manage them separately.
We're compromising reproducibility with this but it is worth it considering I want the changes immediately.
We could generate the entire script by iterating each of the file from `home.mutableFile.<name>` and mapping the methods from it.
Here's one way to let Nix generate our shell script featuring link:https://nixos.org/manual/nixpkgs/unstable/#trivial-builder-writeText[`writeShellScript` builder].
For those who want a complete version of the module directly without applying all of the above patches, you can see it link:./assets/fetch-mutable-files.nix[with this link].
====
With the module being complete for the most part, we just have to include it to our home-manager configuration...
The given code from this post is just one minimal starting point for this module.
In my NixOS configuration, github:foo-dogsquared/nixos-config[my version of the module, rev=c5bf67553c84efb1e0d3f1859f2d98736d66f616, path=modules/home-manager/files/mutable-files.nix] has expanded with the ability to declare archived files which is already extracted in the filesystem.
link:https://github.com/foo-dogsquared/nixos-config/blob/e04d31afeb9b3c1f1fb341ff068062d32def480f/users/home-manager/foo-dogsquared/default.nix#L335-L369[I use it for fetching several things] including Doom Emacs (even automatically installing it).
Starting from the version of the module featured here, there are room for improvement.
You could implement the following suggestions as an exercise.
* Add a type for fetching archived files.
The archive should already be extracted into the path.
Additionally, you could add an option for extracting a single file or directory.
* Each resource to be fetched may require different tweaks.
For example, you may want to shallow clone link:https://github.com/doomemacs/doomemacs/[Doom Emacs] since the repo history is too large.
You might want to add an option (e.g., `home.mutableFile.<name>.extraArgs`) to set extra arguments to each file.
* Add the option to allow changing the package to be used for the shell script.
This would also require restructuring (and possibly renaming) of the module though.
* Add an attribute that links to a store path (e.g., `home.mutableFile.<name>.outPath`) for each of the given URL.
You could also add an attribute (e.g., `home.mutableFile.<name>.dontLinkToStore`) that either opts-in/-out of including the file in the store directory.
Take note this value should be automatically applied and shouldn't be set by the user.
****
[#mutable-files-in-nixos]
== Mutable files in NixOS
So far, we only manage them mutable files in home-manager.
I cannot find an equivalent in NixOS but it should be pretty trivial to recreate it especially that the things that made `mkOutOfStoreSymlink` possible is readily available in nixpkgs.
All we have to do is to recreate them.
For this case, we'll use the link:https://nixos.org/manual/nixpkgs/unstable/#trivial-builder-runCommandLocal[`runCommandLocal` builder] typically used for cheap commands.
This also what `mkOutOfStoreSymlink` uses in the source code.
If you want to create a declarative interface similar to <<adding-a-declarative-interface-for-fetching-mutable-files, the featured module>> for NixOS, you can create your implementation based from that.
However, there's more moving parts that you have to worry about since we're using NixOS where the scope includes the whole system unlike with home-manager where it focuses on the home environment.
Here are some things you may need to pay attention to.
- Since we're using system-level services with systemd, the fetched files will be owned the user of the fetching service (which is root by default).
You may want to add an option for the group and owner for each files.
- Reject (or at least discourage) relative paths since it will be confusing to use.
It's best if the module encourages the user to use absolute paths instead.
- Making sure the fetching service does not modify the generated files from NixOS.
Even though this is already handled by the previous module, it is something to keep in mind now that we're modifying the whole operating system.