# repo-lib `repo-lib` is a pure-first Nix flake library for repo-level developer workflows: - `mkRepo` for `devShells`, `checks`, `formatter`, and optional `packages.release` - structured tool banners driven from package-backed tool specs - structured release steps (`writeFile`, `replace`, `versionMetaSet`, `versionMetaUnset`) - a Bun-only Moonrepo + TypeScript + Varlock template in [`template/`](/Users/eric/Projects/repo-lib/template) Audit and replacement review: [`docs/reviews/2026-03-21-repo-lib-audit.md`](/Users/eric/Projects/repo-lib/docs/reviews/2026-03-21-repo-lib-audit.md) ## Prerequisites - [Nix](https://nixos.org/download/) with flakes enabled - [`direnv`](https://direnv.net/) (recommended) ## Use the template ```bash nix flake new myapp -t 'git+https://git.dgren.dev/eric/nix-flake-lib?ref=refs/tags/v3.5.1#default' --refresh ``` The generated repo includes: - a `repo-lib`-managed Nix flake - Bun as the only JS runtime and package manager - Moonrepo root tasks - shared TypeScript configs adapted from `../moon` - Varlock with a committed `.env.schema` - empty `apps/` and `packages/` directories for new projects ## Use the library Add this flake input: ```nix inputs.repo-lib.url = "git+https://git.dgren.dev/eric/nix-flake-lib?ref=refs/tags/v3.5.1"; inputs.repo-lib.inputs.nixpkgs.follows = "nixpkgs"; ``` Build your repo outputs from `mkRepo`: ```nix outputs = { self, nixpkgs, repo-lib, ... }: repo-lib.lib.mkRepo { inherit self nixpkgs; src = ./.; config = { checks.tests = { command = "echo 'No tests defined yet.'"; stage = "pre-push"; passFilenames = false; }; release = { steps = [ ]; }; }; perSystem = { pkgs, system, ... }: { tools = [ (repo-lib.lib.tools.fromCommand { name = "Nix"; version.args = [ "--version" ]; command = "nix"; }) ]; shell.packages = [ self.packages.${system}.release ]; }; }; ``` `mkRepo` generates: - `devShells.${system}.default` - `checks.${system}.hook-check` - `checks.${system}.lefthook-check` - `formatter.${system}` - `packages.${system}.release` when `config.release != null` - merged `packages` and `apps` from `perSystem` Checks are installed through `lefthook`, with `pre-commit` and `pre-push` commands configured to run in parallel. repo-lib also sets Lefthook `output = [ "failure" "summary" ]` by default. For advanced Lefthook features, use raw `config.lefthook` or `perSystem.lefthook`. Those attrsets are merged after generated checks, so you can augment a generated command with fields that the simple `checks` abstraction does not carry, such as `stage_fixed`: ```nix config.lefthook.pre-push.commands.tests.stage_fixed = true; ``` ## Tool banners Tools are declared once. Package-backed tools are added to the shell automatically, and both package-backed and command-backed tools are rendered in the startup banner. ```nix (repo-lib.lib.tools.fromPackage { name = "Go"; package = pkgs.go; version.args = [ "version" ]; banner.color = "CYAN"; }) ``` Required tools fail shell startup if their version probe fails. This keeps banner output honest instead of silently hiding misconfiguration. When a tool should come from the host environment instead of `nixpkgs`, use `fromCommand`: ```nix (repo-lib.lib.tools.fromCommand { name = "Nix"; command = "nix"; version.args = [ "--version" ]; }) ``` ## Purity model The default path is pure: declare tools and packages in Nix, then let `mkRepo` assemble the shell. Impure bootstrap work is still possible, but it must be explicit: ```nix config.shell = { bootstrap = '' export GOBIN="$PWD/.tools/bin" export PATH="$GOBIN:$PATH" ''; allowImpureBootstrap = true; }; ``` ## Release steps Structured release steps are preferred over raw `sed` snippets: ```nix config.release = { steps = [ { writeFile = { path = "src/version.ts"; text = '' export const APP_VERSION = "$FULL_VERSION" as const; ''; }; } { replace = { path = "README.md"; regex = ''^(version = ")[^"]*(")$''; replacement = ''\1$FULL_VERSION\2''; }; } { versionMetaSet = { key = "desktop_binary_version_max"; value = "$FULL_VERSION"; }; } ]; }; ``` The generated `release` command still supports: ```bash release release select release --dry-run patch release patch release patch --commit release patch --commit --tag release patch --commit --tag --push release beta release minor beta release stable release set 1.2.3 ``` By default, `release` updates repo files, runs structured release steps, executes `postVersion`, and runs `nix fmt`, but it does not commit, tag, or push unless you opt in with flags. - `--commit` stages all changes and creates `chore(release): ` - `--tag` creates the Git tag after commit - `--push` pushes the current branch and, when tagging is enabled, pushes tags too - `--dry-run` resolves and prints the plan without mutating the repo When `release` runs with no args in an interactive terminal, it opens a Bubble Tea picker so you can preview the exact command, flags, and resolved next version before executing it. Use `release select` to force that picker explicitly. ## Low-level APIs `mkRelease` remains available for repos that want lower-level control over release automation. ## Common command ```bash nix fmt ```