Replace the stale Bun-only project-scaffold spec with a Nix-aware packaging spec, and record the flake-parts + bun2nix + pinned-Bun decision as ADR 0007.
7.3 KiB
Problem Statement
The repository was scaffolded as a Bun workspaces monorepo before the developer adopted Nix.
There is no declarative, reproducible way to build, run, or install the kitchen CLI, and no reproducible development environment.
A contributor must install the right Bun version and tooling by hand, and the "no Node.js runtime on target machines" promise is trusted rather than enforced.
The developer wants to install the CLI declaratively — as a flake input in a system or home configuration — and to enter a fully specified dev shell with a single command.
Solution
Package the whole repository as a Nix flake, structured with flake-parts, exposing the kitchen CLI as an installable package plus a development shell, a runnable app, and a set of checks.
Bun dependencies are vendored reproducibly with bun2nix so the sandboxed build needs no network.
Bun itself is pinned so the version used to develop is the version used to compile.
The flake is the single declarative entry point: build it, run it, install it, or develop in it.
User Stories
- As the developer, I want to install the
kitchenCLI declaratively via a flake input, so that it lands in my system or home configuration reproducibly without manual build steps. - As the developer, I want
nix buildto produce the compiledkitchenbinary, so that the artifact is built the same way on any machine. - As the developer, I want
nix runto execute the CLI without a prior install, so that I can try it ad hoc. - As a contributor, I want
nix developto drop me into a shell with the exact Bun and tooling the project expects, so that I do not have to install or match versions by hand. - As a contributor using direnv, I want the dev shell to load automatically on entering the directory, so that the environment is present without a manual command.
- As the developer, I want
nix flake checkto run the test suites and the linter, so that a single command verifies the project reproducibly and can gate CI later. - As the developer, I want the compiled binary to embed its dependencies, so that no Node.js or Bun runtime is required on the target machine.
- As a maintainer, I want dependency vendoring to stay in sync with the lockfile automatically, so that adding or removing a dependency does not require a separate manual regeneration step I might forget.
- As a maintainer, I want the Bun version pinned and shared between the dev shell and the build, so that a Bun release that breaks sandboxed compilation cannot silently diverge dev from build.
Implementation Decisions
- Flake structure: flake-parts.
Per-system outputs (package, dev shell, app, checks) are declared through flake-parts'
perSystemmodule, and system-independent configuration sits at the top level. This keeps per-system and top-level outputs separate and lets tooling integrations compose as modules. - Outputs.
The flake exposes a default package building the
kitchenbinary, a default app pointing at that binary, a default dev shell, and a checks set aggregating tests and lint.nix build,nix run,nix develop, andnix flake checkare the supported entry points. - Dependency vendoring: bun2nix. Bun workspace dependencies are vendored via bun2nix, which reads the lockfile and produces a generated Nix expression the build consumes. A postinstall hook regenerates that expression on any dependency change, keeping it in sync with the lockfile without a manual step. bun2nix is added as a flake input.
- Binary build via Bun's native compiler. The package derivation compiles the CLI to a self-contained native binary that embeds its dependencies, so the runtime closure on the target has no Node.js or Bun. The vendored dependencies are consumed at build time only.
- Bun pinned. A single pinned Bun version is shared by the dev shell and the build derivation. The pin avoids Bun releases known to break native compilation inside the Nix sandbox, where specific point releases have produced empty binaries.
- Systems:
x86_64-linuxonly for now, declared in the flake-parts systems list. Additional targets are a one-line addition when a real target appears; vendoring and compilation are per-platform, so each added system carries its own build. - nixpkgs tracks
nixpkgs-unstable, to follow a recent Bun. - Lint as a check: treefmt-nix.
Biome runs as a
nix flake checkthrough the treefmt-nix flake-parts module, so lint verification is reproducible and shares one configuration with the existing biome setup. - direnv auto-load.
A direnv configuration using
use flakeloads the dev shell automatically for direnv users. - The monorepo it packages is unchanged.
The existing Bun workspaces layout (
@kitchen-md/coreand@kitchen-md/bin) and thekitchenbinary target are the inputs to the flake; the flake wraps them without restructuring the packages.
Testing Decisions
- The flake's verification surface is its outputs, exercised as commands rather than unit tests.
The highest seam is
nix flake check: it composes the package build, the Bun test suites, and the biome lint into one reproducible gate. - Build seam.
nix buildmust produce a runnablekitchenbinary; a minimal check invokes the built binary (for example--help) and asserts a zero exit, mirroring the existing smoke-test approach in@kitchen-md/binbut against the Nix artifact. - Dev-shell seam. Entering the dev shell must expose the pinned Bun and biome; this is verified by the presence and versions of those tools in the shell.
- Application behaviour is not re-tested here.
The parser and CLI behaviour are covered by their own specs' unit, integration, and smoke tiers, which run under the Bun test runner and are invoked by
nix flake check. This spec's tests concern packaging and environment reproducibility only. - Prior art: the existing smoke tests in
@kitchen-md/bininvoke the CLI as a black box; the build-seam check reuses that black-box style against the Nix-built binary.
Out of Scope
- The parser and CLI implementation and their behaviour, covered by the core-parser and cli-view specs.
- Publishing either package to a registry, and the future Obsidian plugin's own repository and packaging.
- CI/CD wiring;
nix flake checkis designed to be CI-callable, but the CI configuration itself is separate. - Multi-system and cross-compilation beyond
x86_64-linux, and any binary cache or substituter setup. - A NixOS or home-manager module wrapping the package; the package output is directly consumable without one.
- git-hooks integration; treefmt-nix covers the lint check, and pre-commit hooks can be added later as another flake-parts module.
Further Notes
- Because Bun's native compiler bundles dependencies into the binary, the vendoring problem is confined to build time; nothing about the dependency closure reaches consumers of the installed CLI.
- The Bun-version pin is a correctness constraint, not just hygiene: native compilation has produced empty binaries in the sandbox on specific Bun point releases, so the dev shell and build must never drift apart on Bun version.
- The decision to package with a flake, vendor via bun2nix, and pin Bun is recorded in ADR 0007.
- This spec replaces the removed project-scaffold spec's role for anything Nix-related; the underlying Bun monorepo layout that spec documented is retained in the codebase.