numtide/blueprint

repository·main·Indexed 19 days ago

https://github.com/numtide/blueprint

An opinionated library that maps a standard folder structure to Nix flake outputs to reduce boilerplate and modularize Nix projects. It automatically exposes directories such as `packages/`, `devshells/`, `hosts/`, and `modules/` as flake attributes. Blueprint provides built-in support for NixOS, nix-darwin, home-manager, system-manager, and the devshell project, including support for TOML-based devshell configurations.

Tokens
6.4K
Snippets
28
Records
36
Agent score
66%

What's inside blueprint

  1. Supported features and integrations

    main

    blueprint provides built-in support for several Nix ecosystem components and workflows:

    • Environments: devshell.nix for developer environments.
    • Configurations: NixOS, nix-darwin, home-manager, and system-manager.
    • Testing & Tooling: nix-unit, default RFC166 nix formatter (nix fmt), and automatic flake checks for packages, devshells, and NixOS configurations.
    • Systems: Supports overridable systems via nix-systems.
  2. Understand the Blueprint folder structure

    main

    Blueprint uses a specific directory layout to organize Nix configurations. It is recommended to use a prefix (e.g., nix/) to hold these folders.

    High-level directory overview

    • flake.nix: The default flake entrypoint.
    • formatter.nix: Defines the default code formatter.
    • devshell.nix: Defines the default development shell.
    • package.nix: Defines the default package.
    • checks/: Contains flake checks.
    • devshells/: Contains multiple devshell configurations.
    • hosts/: Contains machine-specific configurations.
    • lib/: Contains reusable Nix functions.
    • modules/: Contains NixOS and other modules.
    • packages/: Contains package definitions.
    • templates/: Contains flake templates.
  3. Manage packages in the `packages/` directory

    main

    The packages/ folder is the central location for all project packages.

    Structure:

    • packages/<pname>/.nix or packages/<pname>/default.nix: Defines a package named <pname>.
    • package.nix (top-level): For single-package repositories, this maps to the default package.
    • formatter.nix: Defines the default formatter.

    Flake Outputs:

    • packages.<system>.<pname>: The package itself.
    • checks.<system>.pkgs-<pname>: The package used for nix flake check.
    • checks.<system>.pkgs-<pname>-<tname>: The package plus all its passthru.tests.
    • perSystem.self.<pname>: Used to consume a package from the same flake within a host/module.

    Exposing packages as an overlay: To ensure packages are built against a consumer's nixpkgs instance (to share dependencies), use the mkPackagesFor function provided by blueprint.

    outputs = inputs: 
      let 
        bp = inputs.blueprint { inherit inputs; }; 
      in 
      bp // {
        overlays.default = final: _prev: {
          myproject = bp.mkPackagesFor final;
        };
      };
  4. Arguments passed to per-system files

    main

    Certain files in Blueprint are instantiated multiple times, once for each configured system. These files receive the following arguments:

    • inputs: A map of the flake inputs.
    • flake: A shorthand for inputs.self (the flake itself).
    • system: The current system attribute (e.g., x86_64-linux).
    • perSystem: A map containing the packages of all inputs, filtered per system.
      • Example: perSystem.nixos-anywhere.default is a shorthand for inputs.nixos-anywhere.packages.<system>.default.
    • pkgs: An instance of nixpkgs.
  5. Configure the devshell.nix file

    main

    Blueprint allows you to define your development environment in a standalone devshell.nix file. This file uses pkgs.mkShell to manage dependencies, environment variables, and shell hooks. This separation allows you to easily copy your development environment configuration between different projects without modifying the main flake.nix.

    { pkgs }: 
    pkgs.mkShell {
      # Add build dependencies
      packages = [ 
        pkgs.python3 
        pkgs.python3Packages.numpy
      ];
    
      # Add environment variables
      env = { 
        MY_VAR = "value";
      };
    
      # Load custom bash code
      shellHook = ''
        export PS1="(python numpy) $PS1"
      '';
    }
  6. How blueprint maps folder structures to flake outputs

    main

    blueprint follows an opinionated 1:1 mapping between a standard folder structure and Nix flake outputs. This allows you to modularize your flake by placing logic into specific directories, which blueprint then automatically exposes as flake attributes.

    Common mappings include:

    PathFlake Output Attribute
    devshells/devShells.*
    hosts/nixosConfiguration.* and darwinConfigurations.*
    modules/nixosModules.* and darwinModules.*
    packages/packages.*

    This structure helps reduce boilerplate in your flake.nix and allows you to share modules across different projects easily.

  7. Define flake checks in the `checks/` directory

    main

    The checks/ directory is used to define tests that run when nix flake check is invoked. Blueprint also automatically populates checks from your packages and hosts attributes.

    Structure:

    • checks/<pname>.nix or checks/<pname>/default.nix

    Inputs: Modules in this directory receive the standard per-system arguments plus the pname attribute.

    Flake Outputs:

    • checks.<system>.<pname>: Contains the specific check/package.
  8. Organize Nix modules in the `modules/` directory

    main

    Blueprint automatically maps subdirectories within modules/ to specific flake outputs based on their folder names. This allows you to categorize your Nix modules (e.g., for NixOS, Home Manager, or Darwin) and access them via structured outputs.

    Mapping Rules:

    • modules/darwin/<name> $\rightarrow$ darwinModules.<name>
    • modules/home/<name> $\rightarrow$ homeModules.<name>
    • modules/nixos/<name> $\rightarrow$ nixosModules.<name>
    • Any other type <type>/<name> $\rightarrow$ modules.<type>.<name>

    Module Arguments: If a module is wrapped in a function, it can accept flake and inputs as arguments. This enables modules to reference the flake where they are defined, even if they are consumed by a different flake.

  9. Quickstart: Initialize a new blueprint project

    main

    To start a new project using blueprint, ensure you have Nix installed or are using NixOS, then initialize a new flake using the blueprint template:

    1. Create and enter a new directory:
      mkdir my-project && cd my-project
    2. Initialize the flake with the blueprint template:
      nix flake init -t github:numtide/blueprint
    mkdir my-project && cd my-project
    nix flake init -t github:numtide/blueprint