Skip to content

About

An opinionated dendritic base flake

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

17 Commits

Folders and files

Repository files navigation

dendritic-base

A flake-parts module carrying the parts of a dendritic Rust project flake that are the same in every project, so they can be bumped in one place instead of nine files per repository.

Why

A project flake in this style ends up with rust-overlay.nix, treefmt.nix, hooks.nix, devshell.nix, cargo-readme.nix, systems.nix, packages.nix, _package.nix and overlay.nix. Across projects those files are near-identical, but they are copies, so every change to the shared parts is a commit per repository, and the copies fall out of step: a project keeps a workaround for an upstream bug that has since been fixed, or a comment describing a mechanism that was refactored away.

Here they are one input and a handful of options. Bumping the input bumps rust-overlay, treefmt-nix, devshell, hk-nix and cargo-readme together.

Usage

{
  description = "...";

  inputs = {
    nixpkgs.url = "https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz";

    flake-parts.url = "github:hercules-ci/flake-parts";
    flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";

    import-tree.url = "github:vic/import-tree";

    dendritic-base = {
      url = "github:nix-tools/dendritic-base";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.flake-parts.follows = "flake-parts";
      inputs.import-tree.follows = "import-tree";
    };
  };

  outputs =
    { flake-parts, import-tree, ... }@inputs:
    flake-parts.lib.mkFlake { inherit inputs; } (import-tree ./nix);
}

nix/base.nix:

{ inputs, ... }:
{
  imports = [ inputs.dendritic-base.flakeModules.default ];

  dendritic-base = {
    root = ../.;
    rust.readme.input = "src/main.rs";
  };
}

pkgs comes from the consumer's nixpkgs with the base's rust-overlay applied, so there is one nixpkgs in the lock. Everything else -- rust-overlay, treefmt-nix, devshell, hk-nix, cargo-readme -- comes from the base.

root is the one option every project sets. It has no default because a default would have to be self, and a top-level flake output whose presence depends on self is an infinite recursion: producing the output set means knowing its keys, and whether the project is Rust decides whether there is an overlays output at all. A path written in the consumer's own file also cannot accidentally name the base's store path.

What gets derived

For a Cargo workspace, from Cargo.toml alone:

  • packages.<crate> for every member that produces a binary, plus packages.default
  • checks.<crate> for each of those, so a crate nothing depends on still has to build
  • apps.default and overlays.default
  • versions and descriptions, with [workspace.package] inheritance resolved
  • the cargo readme invocation, including how many ../ it takes to reach the template from the crate directory

Ambiguity is an error rather than a guess: with more than one binary crate and none named after the repository, evaluation stops and asks for dendritic-base.rust.defaultCrate.

Customizing without options

A project's own nix/*.nix files are still flake-parts modules, and they merge with the base's config. So most additions need no option at all:

# nix/demo.nix
{
  perSystem =
    { pkgs, ... }:
    {
      devshells.default.packages = [ pkgs.vhs pkgs.gifsicle ];
    };
}

That works for devshells.default.packages and .env, treefmt.programs.*, treefmt.settings.global.excludes, new keys under packages / checks / apps, and flake.overlays.<name> for any name other than default.

Three things do need to go through an option, because defining them twice either conflicts or silently drops one definition:

  • flake.overlays.default, which is a unique option -- so remove a project's own nix/overlay.nix when adopting the base
  • hk-nix.settings, which merges shallowly -- add hook steps via dendritic-base.hooks.steps instead
  • _module.args.pkgs, which is a single definition -- add overlays via dendritic-base.extraOverlays instead, and they apply after the base's own

Versioning

VERSION holds a semver. The dev shell notices when a newer one is published and prints a line saying so; nix run .#bump-base shows what changed between the pinned version and the latest, then updates the input. The check never blocks shell entry and does not run in CI.

Opt out with dendritic-base.updateCheck.enable = false, or per-shell with DENDRITIC_BASE_NO_UPDATE_CHECK=1.

About

An opinionated dendritic base flake

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages