/patchwork

Selective declaration of mutable files

Primary LanguageNixMIT LicenseMIT


Patchwork

Selective declaration of mutable files.

Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Contributing
  5. License

About The Project

The current methods to declaratively manage arbitrary files in NixOS and home-manager generally follow one of two approaches: The home.file approach, where the file is generated, placed on the /nix/store, and symbolically linked into place; and the systemd.tmpfiles approach, where a file is generated or copied, placed on the /nix/store, and copied into place. However, I was not satisfied with the result of either approach when configuring applications that store dynamic information in config files.

Here's why:

  • Symbolic links to the /nix/store prevent these applications from storing important operating data in their config files.
  • When replacing the file with systemd tmpfiles, config is managed only on activation, meaning changes do not take effect until nixos-rebuild switch or home-manager switch are ran.
  • When copying the file with systemd tmpfiles, config is initialized only once, not managed continually, leading to config that diverges from the declared state.

Patchwork aims to address some of these annoyances by providing a new config declaration method for applications that require only some of their config to be declared. Patchwork should be used only when other methods such as home.file and systemd.tmpfiles do not provide the desired result.

Patchwork accomplishes partial file declaration using RegEx patches to ensure fixed config. These RegEx(s) are applied on activation similar to systmd.tmpfiles, but also optionally provides a service to monitor declared files and re-apply patches on modification.

Caution

Patches specified in Patchwork must be idempotent! This means that applying the patch once is functionally identical to applying the patch multiple times. Patches that are not idempotent WILL cause corruption of your config files.

Getting Started

With Flakes

Using NixOS

Click to Expand
{
  inputs.patchwork.url = "github:quantumcoded/patchwork";

  outputs = { self, nixpkgs, patchwork }: {
    # change `yourhostname` to your actual hostname
    nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        patchwork.nixosModules.default

        # or for home-manager in NixOS
        {
          home-manager.sharedModules = [
            patchwork.homeModules.default
          ];
        }
      ];
    };
  };
}

Using home-manager standalone

Click to Expand
{
  inputs.patchwork.url = "github:quantumcoded/patchwork";

  outputs = { self, nixpkgs, patchwork }: {
    # change `yourusername` and `yourhostname` to your actual username and hostname
    homeConfigurations."yourusername@yourhostname" = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        patchwork.homeModules.default
      ];
    };
  };
}

Without Flakes

Using NixOS

Click to Expand Add the following to your NixOS configuration:
{
  imports = [ "${builtins.fetchTarball "https://github.com/quantumcoded/patchwork/archive/main.tar.gz"}/nixos.nix" ];
}

or with pinning:

{
  imports = let
    # replace this with an actual commit id or tag
    commit = "";
  in [
    "${builtins.fetchTarball {
      url = "https://github.com/quantumcoded/patchwork/archive/${commit}.tar.gz";
      # update hash from nix build output
      sha256 = "";
    }}/nixos.nix"
  ];
}

Using home-manager standalone

Click to Expand Add the following to your home-manager configuration:
{
  imports = [ "${builtins.fetchTarball "https://github.com/quantumcoded/patchwork/archive/main.tar.gz"}/home.nix" ];
}

or with pinning:

{
  imports = let
    # replace this with an actual commit id or tag
    commit = "";
  in [
    "${builtins.fetchTarball {
      url = "https://github.com/quantumcoded/patchwork/archive/${commit}.tar.gz";
      # update hash from nix build output
      sha256 = "";
    }}/home.nix"
  ];
}

(back to top)

Usage

Imagine a simple music player application with the following config file:

[Cache]
LastVolume=10
LastSong="My Song"

[Settings]
MusicDirectory=/home/user/music

Trying to manage this config file using home.file will prevent the caching of songs and volume, and trying to mange the file with systmd.tmpfiles will allow the user to change the music directory, creating impurity.

This is where Patchwork comes in.

Patchwork can declare a patch for the MusicDirectory line like so:

{
  services.patchwork = {
    enable = true;

    # use the watcher service (default: false)
    # without this, patches are only run on activation
    watchForModify = true;

    # backup config files (default: true)
    backup = true;

    # the file extension to use for backups (default: "bak")
    backupExtension = "bak";

    # an attribute set of file and patches to apply
    # in NixOS the path must be absolute, in home-manager the path is relative to `~`
    # the value can be a string for a single patch or a list of strings for multiple
    # if no patches are specified, Patchwork is effectively disabled
    patches = {
      ".config/music-player/config.conf" = "s/MusicDirectory=.*/MusicDirectory=\/home\/user\/music/";
    };
  };
}

In the event of changes to the MusicDirectory, whether initiated by the music player or through other means, Patchwork ensures that the MusicDirectory maintains its declared state, while still permitting the program to utilize its cache settings.

(back to top)

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

(back to top)

License

Distributed under the MIT License. See LICENSE for more information.

(back to top)