Skip to content
LogoLogo

flake-parts Integration

purr works as a flake-parts module, giving you access to all purr features alongside other flake-parts modules.

Setup

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-parts.url = "github:hercules-ci/flake-parts";
    purr.url = "github:nixcafe/purr";
    my-extra-modules.url = "github:user/my-extra-modules";
  };
 
  outputs = inputs:
    inputs.flake-parts.lib.mkFlake { inherit inputs; } {
      imports = [ inputs.purr.flakeModules.default ];
      systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ];
      purr = {
        enable = true;
        src = ./.;
        namespace = "cattery";
        extraModules.nixos = [
          inputs.my-extra-modules.nixosModules.default
        ];
      };
    };
}

Options

All options are under the purr.* namespace.

Core Options

OptionTypeDefaultDescription
purr.enableboolfalseEnable purr
purr.srcpathrequiredProject root directory
purr.namespacenullOr strnullModule option namespace
purr.libDirnullOr strnullauto-detects lib/
purr.flattenLibboolfalseFlatten lib subdirectories into root
purr.modulesDirstr"modules"Module directory name under src
purr.moduleTypesattrsSee belowSubdirectory mapping per output
purr.extraModulesattrs{}{ nixos = [...]; darwin = [...]; home = [...]; }
purr.extraArgsattrs{}Custom key-value pairs injected into all auto-discovered module args (packages, shells, checks, apps, templates, system specialArgs, home extraSpecialArgs). Purr's own keys override extraArgs on conflict
purr.bundleModulesboolfalseBundle all modules into a default module
purr.bundleExtraModulesbooltrueInclude extra modules in the default bundle

Default moduleTypes:

{
  nixos = ["nixos" "shared"];
  darwin = ["darwin" "shared"];
  home = ["home"];
}

Directory Options

OptionTypeDefaultDescription
purr.checksDirnullOr strnullauto-detects checks/
purr.shellsDirnullOr strnullauto-detects shells/ then devShells/
purr.overlaysDirnullOr strnullauto-detects overlays/
purr.packagesDirnullOr strnullauto-detects packages/
purr.packagesByNameboolfalseAlso discover packages via by-name/ convention (coexists with regular discovery)
purr.legacyPackagesDirnullOr strnullauto-detects legacyPackages/
purr.legacyPackagesByNameboolfalseAlso discover legacy packages via by-name/ convention (coexists with regular discovery)
purr.appsDirnullOr strnullauto-detects apps/
purr.templatesDirnullOr strnullauto-detects templates/
purr.templatesRecursiveboolfalseWhether to scan templates/ recursively
purr.formatterDirnullOr strnullauto-detects formatters/ then formatter/. The default.nix must return a derivation
purr.systemsDirnullOr strnullauto-detects systems/ then hosts/
purr.homesDirnullOr strnullauto-detects homes/

Other Options

OptionTypeDefaultDescription
purr.nixpkgsConfigattrs{}nixpkgs config (allowUnfree, etc.)
purr.inputsFornullOr fnidentityTransform { inputs, ... } -> inputs' (or a plain attrset) that filters/replaces the raw flake inputs into the effective inputs purr builds from internally. Modules always keep the raw inputs. See Per-host inputs
purr.autoInjectbooltrueAuto-inject networking.hostName, home.username, etc.
purr.outputsBuilderfn(_: {})Additional per-system outputs. Called for each system with { pkgs, system, lib, inputs, namespace } plus all extraArgs keys; the returned attrset is deep-merged into the perSystem flake outputs
purr.hostsattrs{}Per-host config: purr.hosts.<name>.meta = { images = [...]; deployable = true; roles.nixpkgs = "..."; ... }, deep-merged over the host's meta.nix. See Host Meta

hydraJobs Options

All under purr.hydraJobs.*. See the hydraJobs page for details.

OptionTypeDefaultDescription
purr.hydraJobs.enableboolfalseEnable the hydraJobs flake output
purr.hydraJobs.asstr"hydraJobs"Name of the flake output carrying the jobs
purr.hydraJobs.dirnullOr strnullCustom jobs directory, auto-detects hydraJobs/
purr.hydraJobs.systemsnullOr [str]nullFilter which systems get CI jobs
purr.hydraJobs.includenullOr [str]nullMirror outputs into hydraJobs (null = auto-detect all)
purr.hydraJobs.extraattrs{}Extra jobs merged last, highest priority

Custom Module Directories

You can customize which subdirectories are scanned for each module type:

purr.moduleTypes = {
  nixos = ["nixos" "shared" "container" "nixos-musl"];
};

Extra Modules from Other Flakes

Inject external modules alongside auto-discovered ones:

purr.extraModules = {
  nixos = [
    inputs.cattery-modules.nixosModules.default
    inputs.disko.nixosModules.default
  ];
};

Default Module Bundle

A default module that imports all discovered sub-modules is always generated (for nixosModules, darwinModules, and homeModules) — unless you define your own default module, in which case the auto-generated bundle is skipped. Users can import with:

{ imports = [ inputs.myflake.nixosModules.default ]; }

bundleModules (default false) controls whether your extraModules are folded into this bundle. Set bundleModules = true to include them; combine with bundleExtraModules = false to include only the auto-discovered modules.

purr.bundleModules = true;        # include extraModules in the default bundle
purr.bundleExtraModules = false;  # (optional) only discovered modules, no extras

Formatter & Legacy Packages

Auto-discovery works the same as mkFlake — drop a formatters/default.nix (returns a derivation) or legacyPackages/<name>/default.nix and the outputs appear:

# formatters/default.nix
{ pkgs, ... }: pkgs.nixfmt-rfc-style
 
# legacyPackages/hello/default.nix
{ pkgs, ... }: pkgs.hello

To also scan the by-name/ convention for legacy packages, enable legacyPackagesByName:

purr.legacyPackagesByName = true;   # also scan legacyPackages/by-name/

Outputs Builder

purr.outputsBuilder mirrors mkFlake's outputsBuilder — add any custom per-system output. It receives { pkgs, system, lib, inputs, namespace } plus all extraArgs keys, and its result is deep-merged with purr's auto-discovered outputs:

purr.outputsBuilder = { pkgs, system, lib, namespace, ... }: {
  packages.fmt = pkgs.nixfmt-rfc-style;
};

Hydra CI (hydraJobs)

Generate a hydraJobs output for Hydra that mirrors your system/home configs and adds custom jobs from a hydraJobs/ directory:

purr.hydraJobs = {
  enable = true;
  systems = ["x86_64-linux"];
  include = [ "nixosConfigurations" "homeConfigurations" ];
};

In flake-parts mode, hydraJobs mirrors the config outputs (nixosConfigurations, darwinConfigurations, homeConfigurations), images, directory jobs, and extra. Per-system outputs (checks, packages, devShells, legacyPackages, formatter) are only auto-mirrored in mkFlake mode — in flake-parts mode, add them via purr.hydraJobs.extra if needed.

See the hydraJobs page for the full reference.

Namespace Lib Output

When a lib/ directory exists and namespace is set, purr exports the namespace lib as a flake lib.<namespace> output — in both mkFlake and flake-parts modes:

# lib/default.nix
{ lib, inputs, namespace }:
{
  utils = import ./utils.nix { inherit lib inputs namespace; };
}
nix eval .#lib.myproject.utils

Extra Args

Pass custom values to every auto-discovered module:

purr.extraArgs = {
  deploymentTarget = "production";
};

Modules receive extraArgs values as function parameters:

# modules/nixos/server/default.nix
{ deploymentTarget, config, lib, ... }:
{
  services.nginx.virtualHosts."myapp" = lib.mkIf (deploymentTarget == "production") {
    # ...
  };
}

All auto-discovered modules receive extraArgs keys — packages, shells, checks, apps, templates, system specialArgs, home extraSpecialArgs, and outputsBuilder. Purr's own keys (inputs, pkgs, namespace, lib, etc.) always override extraArgs in case of naming conflicts.