The base commit
This commit is contained in:
commit
8e7af06409
22
.editorconfig
Normal file
22
.editorconfig
Normal file
@ -0,0 +1,22 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{rs,py}]
|
||||
max_line_length = 100
|
||||
|
||||
[*.md]
|
||||
max_line_length = 150
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.sh]
|
||||
indent_size = 2
|
||||
|
||||
[*.{yaml,yml,json}]
|
||||
indent_size = 2
|
||||
38
.gitattributes
vendored
Normal file
38
.gitattributes
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
*.go text
|
||||
*.mod text
|
||||
*.sum text
|
||||
*.md text
|
||||
*.rst text
|
||||
*.json text
|
||||
*.yaml text
|
||||
*.yml text
|
||||
*.toml text
|
||||
*.sh text eol=lf
|
||||
*.html text
|
||||
*.css text
|
||||
*.js text
|
||||
*.svg text
|
||||
*.xml text
|
||||
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||
*.ico filter=lfs diff=lfs merge=lfs -text
|
||||
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
||||
*.mov filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
*.tar filter=lfs diff=lfs merge=lfs -text
|
||||
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
*.woff filter=lfs diff=lfs merge=lfs -text
|
||||
*.woff2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.exe filter=lfs diff=lfs merge=lfs -text
|
||||
*.dll filter=lfs diff=lfs merge=lfs -text
|
||||
*.so filter=lfs diff=lfs merge=lfs -text
|
||||
*.out filter=lfs diff=lfs merge=lfs -text
|
||||
*.a filter=lfs diff=lfs merge=lfs -text
|
||||
*.o filter=lfs diff=lfs merge=lfs -text
|
||||
85
.gitignore
vendored
Normal file
85
.gitignore
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
# Python specific
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# Virtual environments
|
||||
.env/
|
||||
env/
|
||||
.venv/
|
||||
venv/
|
||||
|
||||
# Packaging and distribution files
|
||||
.Python
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# Dependency directories
|
||||
develop-eggs/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
.installed.cfg
|
||||
|
||||
# Test and coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# IDE specific files and directories
|
||||
.idea/
|
||||
*.iml
|
||||
.vscode/
|
||||
|
||||
# Jupyter Notebook files
|
||||
.ipynb_checkpoints
|
||||
|
||||
# Temporary files created by editors and the system and folders to ignore
|
||||
*.swp
|
||||
*~
|
||||
*.bak
|
||||
*.tmp
|
||||
temp/
|
||||
tmp/
|
||||
|
||||
# Database files (SQLite, DuckDB, etc.)
|
||||
*.duckdb
|
||||
*.db
|
||||
*.wal
|
||||
*.sqlite
|
||||
|
||||
# Dependency lock files (uncomment to ignore)
|
||||
poetry.lock
|
||||
|
||||
# Rust specific
|
||||
/target/
|
||||
.cargo-ok
|
||||
cobertura.xml
|
||||
tarpaulin-report.html
|
||||
|
||||
# Comment out the next line if you want to checkin your lock file for Cargo
|
||||
Cargo.lock
|
||||
|
||||
# Misc
|
||||
*.proptest-regressions
|
||||
.DS_Store
|
||||
.benchmarks
|
||||
.env
|
||||
.claude/
|
||||
.codex
|
||||
44
.pre-commit-config.yaml
Normal file
44
.pre-commit-config.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
default_stages: [ pre-commit ]
|
||||
fail_fast: false
|
||||
exclude: '^\.idea/'
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
args: [ --markdown-linebreak-ext=md ]
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
- id: check-merge-conflict
|
||||
- id: check-added-large-files
|
||||
- id: detect-private-key
|
||||
- id: check-yaml
|
||||
- id: check-toml
|
||||
- id: check-json
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: nix-fmt-check
|
||||
name: Check Nix Formatting
|
||||
entry: make fmt-check
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: \.nix$
|
||||
stages: [ pre-commit ]
|
||||
|
||||
- id: nix-lint
|
||||
name: Lint Nix Files
|
||||
entry: make lint
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: \.nix$
|
||||
stages: [ pre-commit ]
|
||||
|
||||
- id: nix-flake-check
|
||||
name: Run Nix Flake Checks
|
||||
entry: make check
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: \.(nix|lock)$
|
||||
stages: [ pre-push ]
|
||||
33
01-devshell/flake.nix
Normal file
33
01-devshell/flake.nix
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
description = "A minimal dev shell: your first flake";
|
||||
|
||||
# Inputs: other flakes this one depends on.
|
||||
# `nixpkgs` is the main package set. `follows` isn't used here, but you'll
|
||||
# see it a lot when composing flakes (e.g. `inputs.foo.inputs.nixpkgs.follows = "nixpkgs";`).
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
};
|
||||
|
||||
# Outputs: a function that receives all inputs and returns an attrset.
|
||||
# The attrset's structure is what `nix` CLI commands look for (e.g. `devShells.<system>.default`).
|
||||
outputs = { self, nixpkgs, ... }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in {
|
||||
# `nix develop` picks this up by default.
|
||||
devShells.${system}.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
cowsay
|
||||
jq
|
||||
ripgrep
|
||||
];
|
||||
|
||||
# Runs once when you enter the shell.
|
||||
shellHook = ''
|
||||
echo "welcome to your first nix dev shell"
|
||||
cowsay "hello, flakes"
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
170
AGENTS.md
Normal file
170
AGENTS.md
Normal file
@ -0,0 +1,170 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to coding agents collaborating on this repository.
|
||||
|
||||
## Mission
|
||||
|
||||
`nix-playground` is a personal learning playground for Nix and flakes.
|
||||
The goal is not production software but clear, runnable, progressively more advanced examples plus prose notes that explain them.
|
||||
|
||||
Priorities, in order:
|
||||
|
||||
1. Correctness: examples must actually evaluate and build.
|
||||
2. Clarity: each example teaches one concept; names, comments, and directory structure should make that concept obvious.
|
||||
3. Minimality: prefer the shortest flake or expression that demonstrates the idea.
|
||||
4. Accuracy of notes: prose under `notes/` must not describe behavior the examples do not demonstrate.
|
||||
5. Reproducibility: every flake commits its `flake.lock`; nothing depends on ambient state.
|
||||
|
||||
## Core Rules
|
||||
|
||||
- Use English for code, comments, and prose.
|
||||
- Keep each numbered example self-contained: its own `flake.nix`, own `flake.lock`, no cross-example imports.
|
||||
- Prefer small, focused changes over broad rewrites across examples.
|
||||
- Add comments only when they clarify non-obvious Nix behavior (laziness, `rec`, string vs. path, `with` scoping, etc.).
|
||||
- Do not describe Nix features in notes or comments as if they were implemented in an example unless the example actually uses them.
|
||||
- When an example grows beyond one concept, split it into a new numbered directory rather than expanding the existing one.
|
||||
|
||||
Quick examples:
|
||||
|
||||
- Good: add `03-multi-system/` that demonstrates `forAllSystems` in isolation.
|
||||
- Good: add a `checks` output to an existing flake with a one-line comment explaining what `nix flake check` will do with it.
|
||||
- Bad: combine overlays, NixOS modules, and home-manager into one "comprehensive" example.
|
||||
- Bad: edit `notes/` to describe an approach no example in the repo uses.
|
||||
|
||||
## Writing Style
|
||||
|
||||
- Use Oxford commas in inline lists: "a, b, and c" not "a, b, c".
|
||||
- Do not use em dashes. Restructure the sentence, or use a colon or semicolon instead.
|
||||
- Avoid colorful adjectives and adverbs. Write "dev shell" not "lightweight dev shell", "overlay" not "flexible overlay".
|
||||
- Use noun phrases for checklist items, not imperative verbs. Write "input pinning" not "pin inputs".
|
||||
- Headings in Markdown files must be in title case: "Build from Source" not "Build from source". Minor words (a, an, the, and, but, or, for, in, on, at, to, by, of) stay lowercase unless they are the first word.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
- `01-devshell/`, `02-*/`, `NN-<topic>/`: self-contained numbered examples. Each directory is a flake root.
|
||||
- `notes/`: prose companions numbered to match reading order.
|
||||
- `001-glossary.md`: vocabulary reference.
|
||||
- `002-nix-primer.md`: the Nix language and store model.
|
||||
- `003-flakes.md`: flake anatomy, schema, and common patterns.
|
||||
- `Makefile`: discovery-based helpers that run formatting, linting, and `nix flake check` across all examples.
|
||||
- `AGENTS.md`: this file.
|
||||
- `.pre-commit-config.yaml`, `.editorconfig`, `.gitattributes`, `.gitignore`: repository hygiene.
|
||||
- `pyproject.toml`: Python environment metadata used only to install `pre-commit`.
|
||||
|
||||
New examples follow `NN-<short-topic>/` where `NN` is a two-digit ordinal. Topics grow roughly from simpler to more involved: dev shell, package, multi-system, NixOS module, home-manager, overlay.
|
||||
|
||||
## Example Layout Constraints
|
||||
|
||||
- Each example owns exactly one `flake.nix` at its root and commits its `flake.lock`.
|
||||
- Examples do not import each other. Copy and adapt if a pattern needs to be shown twice.
|
||||
- An example may depend only on flakes it declares in its own `inputs`.
|
||||
- Prefer `nixpkgs` pinned to `nixos-unstable` for consistency across examples unless the example's point is pinning strategy.
|
||||
- Keep the `outputs` attrset flat enough that `nix flake show` reads as a single screen.
|
||||
- If an example exposes `checks.<system>.*`, those checks must pass under `nix flake check`.
|
||||
|
||||
## Nix and Flake Conventions
|
||||
|
||||
- Target Nix with `experimental-features = nix-command flakes` enabled (already the case on this machine).
|
||||
- Prefer `pkgs.mkShell` for dev shells; reach for `mkShellNoCC` only when explaining the distinction.
|
||||
- Use `nixpkgs.lib.genAttrs` or `flake-utils.lib.eachDefaultSystem` for multi-system outputs; pick one per example and say which in a comment.
|
||||
- Use `follows` to unify transitive `nixpkgs` inputs when pulling in ecosystem flakes.
|
||||
- Prefer `inherit` over repetition in attrsets.
|
||||
- Avoid top-level `with` statements; keep `with` narrowly scoped to package lists.
|
||||
- Format every `.nix` file with `nixfmt` (RFC 166 style) before committing.
|
||||
|
||||
## Required Validation
|
||||
|
||||
Run these checks for any non-trivial change:
|
||||
|
||||
1. `make fmt-check`
|
||||
2. `make lint`
|
||||
3. `make check`
|
||||
|
||||
These map to `nixfmt --check`, `statix check` plus `deadnix`, and `nix flake check` across every numbered example.
|
||||
|
||||
For notes-only changes, `make fmt-check` and a manual read-through suffice.
|
||||
|
||||
## First Contribution Flow
|
||||
|
||||
Use this sequence for your first change:
|
||||
|
||||
1. Read the relevant `notes/` file and the nearest existing example.
|
||||
2. Add the smallest possible flake or expression demonstrating the new concept.
|
||||
3. Add a short header comment in the new `flake.nix` stating what the example teaches.
|
||||
4. Run `nix flake check` inside the new example directory.
|
||||
5. Run `make fmt-check` and `make lint` from the repository root.
|
||||
6. Add or update the matching entry in `notes/` if the concept is not yet covered there.
|
||||
|
||||
Example scopes that are good first tasks:
|
||||
|
||||
- Add `02-package/` with a trivial `stdenv.mkDerivation` and one-line install phase.
|
||||
- Add a `checks` output to `01-devshell/` that asserts a tool is on `$PATH`.
|
||||
- Add a short section to `notes/003-flakes.md` referencing a newly added example.
|
||||
- Convert an existing example from a hand-rolled `forAllSystems` to `flake-utils`, or vice versa, with a comment explaining the tradeoff.
|
||||
|
||||
## Testing Expectations
|
||||
|
||||
- This repository has no runtime test suite; "tests" are `nix flake check` outcomes and successful builds of each example's default output.
|
||||
- Any example that exposes non-trivial behavior (a derivation, a module) should expose a `checks.<system>.*` attribute that `nix flake check` exercises.
|
||||
- Do not merge changes that regress `make check`.
|
||||
|
||||
## Change Design Checklist
|
||||
|
||||
Before coding:
|
||||
|
||||
1. Identify which existing example or notes file the change belongs to, or whether it needs a new `NN-<topic>/`.
|
||||
2. Confirm the change teaches one concept, not several.
|
||||
3. Confirm `nixpkgs` input choice is consistent with surrounding examples.
|
||||
|
||||
Before submitting:
|
||||
|
||||
1. Verify `make fmt-check`, `make lint`, and `make check` pass.
|
||||
2. Verify every modified flake's `flake.lock` is committed.
|
||||
3. Verify `notes/` accurately reflects what the examples now demonstrate.
|
||||
|
||||
## Review Guidelines (P0/P1 Focus)
|
||||
|
||||
Review output should be concise and only include critical issues.
|
||||
|
||||
- `P0`: must-fix defects (a flake fails to evaluate, an example documents the wrong mechanism, notes contradict the code).
|
||||
- `P1`: high-priority defects (eval warnings, missing `flake.lock`, unpinned or inconsistent inputs, misleading comment).
|
||||
|
||||
Do not include:
|
||||
|
||||
- style-only nitpicks,
|
||||
- praise or summary of what is already good,
|
||||
- exhaustive restatement of the patch.
|
||||
|
||||
Use this review format:
|
||||
|
||||
1. `Severity` (`P0`/`P1`)
|
||||
2. `File:line`
|
||||
3. `Issue`
|
||||
4. `Why it matters`
|
||||
5. `Minimal fix direction`
|
||||
|
||||
## Practical Notes for Agents
|
||||
|
||||
- Prefer targeted edits over broad mechanical rewrites across examples.
|
||||
- If two examples disagree on a convention, prefer the newer one and update the older example in a dedicated commit.
|
||||
- When uncertain whether a concept deserves its own example, start by expanding the notes; promote to an example once the idea stabilizes.
|
||||
- Keep presentational prose in `notes/`. Keep runnable material in numbered directories. Do not cross the streams.
|
||||
- Keep user-facing naming consistent with the repository name: `nix-playground`. The directory spelling `nix-playgraound` is intentional and should not be "fixed".
|
||||
|
||||
## Commit and PR Hygiene
|
||||
|
||||
- Keep commits scoped to one logical change: one example, one notes update, one convention shift.
|
||||
- Commit `flake.lock` in the same commit that introduces or updates the `flake.nix` it belongs to.
|
||||
- PR descriptions should include:
|
||||
1. what concept the change teaches or clarifies,
|
||||
2. which example directories or notes files are affected,
|
||||
3. any new `inputs` added and why,
|
||||
4. output of `make check` (pass/fail).
|
||||
|
||||
Suggested PR checklist:
|
||||
|
||||
- [ ] `make fmt-check` passes
|
||||
- [ ] `make lint` passes
|
||||
- [ ] `make check` passes
|
||||
- [ ] `flake.lock` committed for every new or updated `flake.nix`
|
||||
- [ ] Notes updated where the change introduces or changes a concept
|
||||
133
Makefile
Normal file
133
Makefile
Normal file
@ -0,0 +1,133 @@
|
||||
# Variables
|
||||
# Every numbered directory at the repository root is treated as a self-contained flake example.
|
||||
EXAMPLES := $(sort $(wildcard [0-9][0-9]-*))
|
||||
|
||||
# Tools are invoked via `nix run` so nothing needs to be pre-installed beyond Nix itself.
|
||||
# Override any of these if you have the tools on $PATH and want faster startup.
|
||||
NIX ?= nix
|
||||
NIXFMT ?= $(NIX) run nixpkgs#nixfmt-rfc-style --
|
||||
STATIX ?= $(NIX) run nixpkgs#statix --
|
||||
DEADNIX ?= $(NIX) run nixpkgs#deadnix --
|
||||
|
||||
# Selector for single-example targets (dev, show, update-one).
|
||||
EXAMPLE ?=
|
||||
|
||||
# Default target
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
.PHONY: help
|
||||
help: ## Show help messages for all available targets
|
||||
@grep -E '^[a-zA-Z_-]+:.*## .*$$' Makefile | \
|
||||
awk 'BEGIN {FS = ":.*## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
.PHONY: examples
|
||||
examples: ## List discovered example directories
|
||||
@echo "Examples:"
|
||||
@for e in $(EXAMPLES); do echo " - $$e"; done
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: ## Format every .nix file with nixfmt (RFC 166 style)
|
||||
@echo "Formatting .nix files..."
|
||||
@find . -type f -name '*.nix' -not -path './.git/*' -print0 | xargs -0 -r $(NIXFMT)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check: ## Check formatting of every .nix file (non-zero exit on drift)
|
||||
@echo "Checking .nix formatting..."
|
||||
@find . -type f -name '*.nix' -not -path './.git/*' -print0 | xargs -0 -r $(NIXFMT) --check
|
||||
|
||||
.PHONY: lint
|
||||
lint: ## Run statix and deadnix against every example
|
||||
@echo "Running statix..."
|
||||
@$(STATIX) check .
|
||||
@echo "Running deadnix..."
|
||||
@$(DEADNIX) --fail .
|
||||
|
||||
.PHONY: fix-lint
|
||||
fix-lint: ## Apply statix and deadnix autofixes where possible
|
||||
@echo "Applying statix fixes..."
|
||||
@$(STATIX) fix .
|
||||
@echo "Applying deadnix fixes..."
|
||||
@$(DEADNIX) --edit .
|
||||
|
||||
.PHONY: check
|
||||
check: ## Run `nix flake check` in every example directory
|
||||
@if [ -z "$(EXAMPLES)" ]; then echo "No examples found."; exit 0; fi
|
||||
@set -e; for e in $(EXAMPLES); do \
|
||||
echo ">>> nix flake check $$e"; \
|
||||
(cd "$$e" && $(NIX) flake check); \
|
||||
done
|
||||
|
||||
.PHONY: build
|
||||
build: ## Run `nix build` on the default package of every example (skips those without one)
|
||||
@if [ -z "$(EXAMPLES)" ]; then echo "No examples found."; exit 0; fi
|
||||
@set -e; for e in $(EXAMPLES); do \
|
||||
if $(NIX) eval --no-warn-dirty "$$e#packages.$$($(NIX) eval --impure --raw --expr 'builtins.currentSystem').default" --apply 'x: true' >/dev/null 2>&1; then \
|
||||
echo ">>> nix build $$e"; \
|
||||
(cd "$$e" && $(NIX) build --no-link); \
|
||||
else \
|
||||
echo "--- $$e has no default package, skipping"; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
.PHONY: show
|
||||
show: ## Print `nix flake show` for every example
|
||||
@if [ -z "$(EXAMPLES)" ]; then echo "No examples found."; exit 0; fi
|
||||
@for e in $(EXAMPLES); do \
|
||||
echo ">>> nix flake show $$e"; \
|
||||
(cd "$$e" && $(NIX) flake show) || true; \
|
||||
done
|
||||
|
||||
.PHONY: update
|
||||
update: ## Run `nix flake update` in every example (updates all flake.lock files)
|
||||
@if [ -z "$(EXAMPLES)" ]; then echo "No examples found."; exit 0; fi
|
||||
@set -e; for e in $(EXAMPLES); do \
|
||||
echo ">>> nix flake update $$e"; \
|
||||
(cd "$$e" && $(NIX) flake update); \
|
||||
done
|
||||
|
||||
.PHONY: update-one
|
||||
update-one: ## Run `nix flake update` in EXAMPLE=<dir>
|
||||
@if [ -z "$(EXAMPLE)" ]; then echo "Usage: make update-one EXAMPLE=<dir>"; exit 1; fi
|
||||
@(cd "$(EXAMPLE)" && $(NIX) flake update)
|
||||
|
||||
.PHONY: dev
|
||||
dev: ## Enter `nix develop` in EXAMPLE=<dir>
|
||||
@if [ -z "$(EXAMPLE)" ]; then echo "Usage: make dev EXAMPLE=<dir>"; exit 1; fi
|
||||
@(cd "$(EXAMPLE)" && $(NIX) develop)
|
||||
|
||||
.PHONY: metadata
|
||||
metadata: ## Print `nix flake metadata` for every example
|
||||
@for e in $(EXAMPLES); do \
|
||||
echo ">>> nix flake metadata $$e"; \
|
||||
(cd "$$e" && $(NIX) flake metadata) || true; \
|
||||
done
|
||||
|
||||
.PHONY: clean
|
||||
clean: ## Remove `result` symlinks produced by `nix build`
|
||||
@echo "Removing result symlinks..."
|
||||
@find . -maxdepth 3 -type l \( -name 'result' -o -name 'result-*' \) -print -delete
|
||||
|
||||
.PHONY: gc
|
||||
gc: ## Run `nix store gc` to garbage collect unreferenced store paths
|
||||
@echo "Running nix store gc..."
|
||||
@$(NIX) store gc
|
||||
|
||||
.PHONY: setup-hooks
|
||||
setup-hooks: ## Install Git hooks (pre-commit)
|
||||
@echo "Setting up Git hooks..."
|
||||
@if ! command -v pre-commit >/dev/null 2>&1; then \
|
||||
echo "pre-commit not found. Install it via 'pip install pre-commit' or enter a shell that provides it."; \
|
||||
exit 1; \
|
||||
fi
|
||||
@pre-commit install --hook-type pre-commit
|
||||
@pre-commit install --hook-type pre-push
|
||||
@pre-commit install-hooks
|
||||
|
||||
.PHONY: test-hooks
|
||||
test-hooks: ## Run pre-commit hooks across all files
|
||||
@echo "Running pre-commit hooks on all files..."
|
||||
@pre-commit run --all-files --show-diff-on-failure
|
||||
|
||||
.PHONY: all
|
||||
all: fmt-check lint check ## Run format check, lint, and flake checks
|
||||
@echo "All checks passed."
|
||||
59
notes/001-glossary.md
Normal file
59
notes/001-glossary.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Glossary
|
||||
|
||||
Core vocabulary you'll hit in the first hour of Nix. Skim, don't memorize; come back as needed.
|
||||
|
||||
## The Store and Builds
|
||||
|
||||
- Nix store: `/nix/store`. Immutable, content-addressed directory where *everything* Nix builds lives. Each entry is prefixed by a hash of its inputs.
|
||||
- Store path: a single entry under `/nix/store`, e.g. `/nix/store/abc123…-hello-2.12.1`. The hash makes it unique per set of build inputs.
|
||||
- Derivation (`.drv`): a *recipe* for a build, covering inputs, builder script, env vars, and outputs. Produced by evaluating Nix code. Not the built artifact itself.
|
||||
- Realisation, or "realising a derivation": actually executing the `.drv` to produce the output store path(s). "Build" colloquially means this.
|
||||
- Output path: the store path a derivation produces. A derivation can have multiple outputs (e.g. `out`, `dev`, `man`, `lib`).
|
||||
- Closure: a store path plus all its runtime dependencies, transitively. What you need to copy to another machine to actually use something.
|
||||
- Hash: content/input hash that names store paths. Changing any input changes the hash, which changes the path; this is how Nix guarantees no cache collisions.
|
||||
- Fixed-output derivation (FOD): a derivation whose output hash you declare up front (e.g. source tarballs, `fetchurl`). Allowed network access during build; everything else is sandboxed offline.
|
||||
|
||||
## Language
|
||||
|
||||
- Nix (the language): lazy, purely functional, dynamically typed. Every `.nix` file is one expression.
|
||||
- Attrset: `{ a = 1; b = "x"; }`. The main data structure.
|
||||
- Lambda: `x: x + 1` or `{ a, b }: a + b`. Functions take one argument; multi-arg functions are attrset-destructured.
|
||||
- `let ... in`: local bindings, for example `let x = 1; in x + 1`.
|
||||
- `with expr; body`: brings `expr`'s attrs into scope for `body`. Common in `with pkgs; [ hello jq ]`. Useful but can shadow bindings, so use sparingly.
|
||||
- `inherit`: shorthand to pull names from outer scope into an attrset: `{ inherit pkgs system; }` ≡ `{ pkgs = pkgs; system = system; }`.
|
||||
- `import`: evaluate another `.nix` file. `import ./foo.nix { }` imports and calls it.
|
||||
- `callPackage`: a nixpkgs convention that auto-supplies function arguments from a package set. You'll see it everywhere in nixpkgs.
|
||||
|
||||
## Package Management
|
||||
|
||||
- nixpkgs: the community-maintained collection of Nix expressions for ~100k packages. Lives at `github:NixOS/nixpkgs`.
|
||||
- Channel: a named, periodically-updated snapshot of nixpkgs (e.g. `nixos-unstable`, `nixos-25.11`). Pre-flakes way to pin. Flakes mostly replace this with lockfiles.
|
||||
- Overlay: a function that extends or overrides a package set. Lets you patch, pin, or add packages without forking nixpkgs.
|
||||
- Profile: a symlink tree representing "what's installed" for a user or system. `nix-env`, `nix profile`, NixOS, and home-manager all manage profiles.
|
||||
- Generation: a versioned snapshot of a profile. Every change creates a new generation; old ones stay until garbage-collected. This is how rollback works.
|
||||
- Garbage collection (`nix-collect-garbage`): deletes store paths not reachable from any "GC root" (profiles, running processes, and `result` symlinks).
|
||||
|
||||
## Flakes
|
||||
|
||||
- Flake: a directory with a `flake.nix`. Has pinned `inputs` and structured `outputs`. Reproducible, composable, and schema-driven.
|
||||
- `flake.lock`: JSON file pinning the exact revision (and hash) of each input. Commit it.
|
||||
- Flake reference: URL-like string identifying a flake: `github:NixOS/nixpkgs`, `path:./foo`, `git+https://…`, or `nixpkgs` (registry alias).
|
||||
- Registry: short-name to flake-ref mapping. `nixpkgs` resolves via the global registry by default.
|
||||
- Pure evaluation: flakes evaluate in a sandbox with no env vars, no arbitrary filesystem reads, and only declared inputs. This is what makes them reproducible.
|
||||
|
||||
## NixOS and home-manager
|
||||
|
||||
- NixOS module: a function `{ config, lib, pkgs, ... }: { options = …; config = …; }`. The unit of NixOS configuration.
|
||||
- home-manager: user-level declarative config for dotfiles, per-user packages, and services. Works standalone or as a NixOS module.
|
||||
|
||||
## CLI: Old vs. New
|
||||
|
||||
| Old (pre-flakes) | New (flakes) |
|
||||
|---|---|
|
||||
| `nix-build` | `nix build` |
|
||||
| `nix-shell` | `nix develop` |
|
||||
| `nix-env -iA` | `nix profile install` |
|
||||
| `nix-channel` | `flake.lock` + inputs |
|
||||
| `<nixpkgs>` (NIX_PATH) | `inputs.nixpkgs` |
|
||||
|
||||
Both still work. Flakes are the direction of travel; old commands aren't going away soon.
|
||||
205
notes/002-nix-primer.md
Normal file
205
notes/002-nix-primer.md
Normal file
@ -0,0 +1,205 @@
|
||||
# Nix Primer
|
||||
|
||||
Nix is two things bundled:
|
||||
|
||||
1. A pure, lazy functional language for describing builds.
|
||||
2. A package manager that takes those descriptions and produces content-addressed store paths.
|
||||
|
||||
Confusingly, both are called "Nix." Separate them in your head and everything gets easier.
|
||||
|
||||
---
|
||||
|
||||
## 1. The Mental Model: Eval, Then Build
|
||||
|
||||
Every `nix build` or `nix develop` runs in two phases:
|
||||
|
||||
```
|
||||
.nix source ──(evaluate)──▶ .drv file ──(realise)──▶ /nix/store/...-output
|
||||
│ │ │
|
||||
└── Nix language └── Recipe └── Actual files
|
||||
(pure, lazy) (on disk) (content-addressed)
|
||||
```
|
||||
|
||||
- Evaluation runs the Nix language. Pure, in-memory, fast. Produces `.drv` files.
|
||||
- Realisation actually builds things. Sandboxed. Slow. Outputs go to `/nix/store`.
|
||||
|
||||
You can evaluate without building (`nix eval`, `nix-instantiate`) and you can build without touching the language if you already have a `.drv`. Most errors you'll see are *eval* errors: typos in attrsets, missing arguments, and undefined names.
|
||||
|
||||
---
|
||||
|
||||
## 2. The Language in 5 Minutes
|
||||
|
||||
### Primitives and Collections
|
||||
|
||||
```nix
|
||||
42 # integer
|
||||
3.14 # float
|
||||
"hello ${name}" # string with interpolation
|
||||
''multi-line # indented string (strips common leading whitespace)
|
||||
string''
|
||||
true false null
|
||||
[ 1 2 3 ] # list (space-separated, NOT comma)
|
||||
{ a = 1; b = "two"; } # attrset
|
||||
/foo/bar # path (literal, not a string!)
|
||||
./relative/path # path relative to current file
|
||||
```
|
||||
|
||||
Paths are their own type. When used, they're copied into the store and replaced with the store path.
|
||||
|
||||
### Functions
|
||||
|
||||
Single-argument, curried:
|
||||
|
||||
```nix
|
||||
add = x: y: x + y;
|
||||
add 2 3 # => 5
|
||||
```
|
||||
|
||||
Attrset destructuring (how "named args" work):
|
||||
|
||||
```nix
|
||||
mkUser = { name, age ? 0, ... }: { inherit name age; };
|
||||
mkUser { name = "ada"; } # => { name = "ada"; age = 0; }
|
||||
```
|
||||
|
||||
The `...` means "ignore extra attrs." Without it, passing extras is an error.
|
||||
|
||||
### Bindings
|
||||
|
||||
```nix
|
||||
let
|
||||
x = 1;
|
||||
y = x + 1; # can reference earlier bindings (and each other: lazy)
|
||||
in x + y
|
||||
```
|
||||
|
||||
```nix
|
||||
{ inherit x y; } # ≡ { x = x; y = y; }
|
||||
{ inherit (pkgs) hello jq; } # pulls hello/jq from pkgs into this attrset
|
||||
```
|
||||
|
||||
### `with` (Use Cautiously)
|
||||
|
||||
```nix
|
||||
with pkgs; [ hello jq ripgrep ]
|
||||
# ≡
|
||||
[ pkgs.hello pkgs.jq pkgs.ripgrep ]
|
||||
```
|
||||
|
||||
Convenient in package lists. Avoid at the top of a file: it silently shadows names and makes debugging hard.
|
||||
|
||||
### Laziness
|
||||
|
||||
Nothing is evaluated until needed. This is why you can have large attrsets like `nixpkgs` (100k+ packages) and only pay for what you use.
|
||||
|
||||
```nix
|
||||
let broken = throw "nope"; ok = 1; in ok # => 1, never evaluates `broken`
|
||||
```
|
||||
|
||||
### `import`
|
||||
|
||||
```nix
|
||||
import ./foo.nix # evaluate foo.nix, return its value
|
||||
import ./foo.nix { x = 1; } # if foo.nix is a function, also call it
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Derivations: The Core Primitive
|
||||
|
||||
A derivation is "a build, described as data." You create one with `derivation { … }` (low-level) or `pkgs.stdenv.mkDerivation { … }` (the nixpkgs wrapper you'll actually use).
|
||||
|
||||
Minimal example:
|
||||
|
||||
```nix
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "hello-note";
|
||||
version = "0.1";
|
||||
src = ./.; # path: gets copied into the store
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
echo '#!/bin/sh' > $out/bin/hello-note
|
||||
echo 'echo hello from nix' >> $out/bin/hello-note
|
||||
chmod +x $out/bin/hello-note
|
||||
'';
|
||||
}
|
||||
```
|
||||
|
||||
Key ideas:
|
||||
|
||||
- `$out` is the output store path the build writes into.
|
||||
- Phases (`unpackPhase`, `buildPhase`, `installPhase`, …) are shell snippets `stdenv` runs in order. Override the ones you need.
|
||||
- No network in the build sandbox, except for fixed-output derivations (whose hash you declare up front).
|
||||
- Every input (source, compiler, env vars) becomes part of the hash. Change anything and you get a fresh store path.
|
||||
|
||||
---
|
||||
|
||||
## 4. Store Model: Why This Is All Different
|
||||
|
||||
Traditional package managers put files in `/usr/bin`, `/usr/lib`, and similar shared locations. One version at a time. Upgrades mutate shared state.
|
||||
|
||||
Nix puts every build in `/nix/store/<hash>-<name>/` with the hash derived from all its inputs (recursively). That gives you:
|
||||
|
||||
- Atomic upgrades and rollbacks: switching "versions" is just switching symlinks.
|
||||
- Multiple versions coexisting: they live at different hashes.
|
||||
- Reproducibility: same inputs produce the same hash and the same output (in theory; in practice, 99% there).
|
||||
- Binary caching: if someone else already built exactly these inputs, you can download their output. This is what `cache.nixos.org` does.
|
||||
- Garbage-collectable: anything not referenced by a "GC root" can be deleted safely.
|
||||
|
||||
---
|
||||
|
||||
## 5. How You'll Actually Use Nix Day-to-Day
|
||||
|
||||
Ordered roughly by power-to-complexity:
|
||||
|
||||
1. `nix run nixpkgs#cowsay -- moo`: run a package without installing.
|
||||
2. `nix shell nixpkgs#jq nixpkgs#ripgrep`: throwaway shell with those tools.
|
||||
3. Dev shell via `flake.nix`: per-project pinned toolchain. *This is the most common thing flakes are used for.*
|
||||
4. `nix build`: produce artifacts (binaries, containers, and ISOs).
|
||||
5. NixOS: declare your whole OS config (packages, services, users, and network) in Nix.
|
||||
6. home-manager: same idea, at the user level.
|
||||
|
||||
Each step up locks in more of your environment. You don't need to adopt all of it at once.
|
||||
|
||||
---
|
||||
|
||||
## 6. Minimum Useful Commands
|
||||
|
||||
```bash
|
||||
# Language / introspection
|
||||
nix repl # REPL. Try `:l <nixpkgs>` then `hello`.
|
||||
nix eval --expr '1 + 1'
|
||||
nix-instantiate --eval -E '{ a = 1; }.a'
|
||||
|
||||
# Running & building
|
||||
nix run nixpkgs#hello
|
||||
nix shell nixpkgs#jq
|
||||
nix build nixpkgs#hello # writes ./result symlink
|
||||
nix-store --query --references ./result # show closure
|
||||
|
||||
# Housekeeping
|
||||
nix store gc # garbage collect
|
||||
nix store optimise # dedupe identical files via hardlinks
|
||||
nix why-depends ./result nixpkgs#glibc # trace dependency chains
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Gotchas That Trip Up Newcomers
|
||||
|
||||
- Lists use spaces, not commas. `[ 1 2 3 ]`. Commas are a syntax error.
|
||||
- `rec { }` makes an attrset self-referential. Without `rec`, attrs can't reference each other. You'll see this in package definitions.
|
||||
- Paths vs. strings. `./foo` is a path (copied to store on use). `"./foo"` is a string (not auto-copied). Mixing them causes surprises.
|
||||
- `builtins.*` is the always-available low-level namespace. `lib.*` (from nixpkgs) is the high-level helper library. Both exist; they're different.
|
||||
- Evaluation errors are lazy. An error deep in an unused branch only fires when you actually use it. `nix-instantiate --eval --strict` forces full evaluation.
|
||||
- Hash errors on first build of an FOD: you'll see `got: sha256-…`. Copy that hash into your expression. This is expected workflow.
|
||||
|
||||
---
|
||||
|
||||
## 8. What to Read or Try Next
|
||||
|
||||
- Open `nix repl`, type `:l <nixpkgs>`, then `hello`, `hello.drv`, `hello.outPath`. Poke around.
|
||||
- Read [`nix.dev`](https://nix.dev), the current best introduction.
|
||||
- See `003-flakes.md` for the flakes-specific layer on top of all of this.
|
||||
241
notes/003-flakes.md
Normal file
241
notes/003-flakes.md
Normal file
@ -0,0 +1,241 @@
|
||||
# Flakes
|
||||
|
||||
A flake is a directory containing `flake.nix` (and, once evaluated, `flake.lock`). It's a packaging convention on top of regular Nix that gives you:
|
||||
|
||||
- Pinned inputs via `flake.lock` for reproducible builds.
|
||||
- A fixed output schema so tools know where to look (`devShells.<system>.default`, `packages.<system>.default`, and so on).
|
||||
- Composability, since flakes depend on other flakes by URL.
|
||||
- Pure evaluation, with no silent dependence on `NIX_PATH`, env vars, or random files.
|
||||
|
||||
Flakes are still marked "experimental" but are now the way most new Nix code is written.
|
||||
|
||||
---
|
||||
|
||||
## 1. Anatomy of a `flake.nix`
|
||||
|
||||
```nix
|
||||
{
|
||||
description = "…"; # optional human-readable summary
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
# Pattern: make a transitive input follow yours, to avoid duplicates
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
flake-utils.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, ... }: {
|
||||
# arbitrary attrs here, but names with meaning to `nix` CLI must match the schema
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Two top-level keys matter: `inputs` and `outputs`. `description` is cosmetic.
|
||||
|
||||
### Inputs
|
||||
|
||||
Each input has a flake reference URL. Common forms:
|
||||
|
||||
```
|
||||
github:owner/repo # latest default branch
|
||||
github:owner/repo/branch-or-tag # specific ref
|
||||
github:owner/repo/abc123 # specific commit
|
||||
git+https://example.com/repo.git
|
||||
git+ssh://git@example.com/repo
|
||||
path:./local-flake # local path
|
||||
tarball+https://…/src.tar.gz
|
||||
nixpkgs # registry alias (resolves via `nix registry`)
|
||||
```
|
||||
|
||||
Non-flake sources (a repo without `flake.nix`):
|
||||
```nix
|
||||
inputs.some-src = { url = "github:owner/repo"; flake = false; };
|
||||
# Access raw files via self.inputs.some-src
|
||||
```
|
||||
|
||||
### Outputs
|
||||
|
||||
A function: `{ self, ...inputs }: <attrset>`. The attrset keys are *conventions* the `nix` CLI knows about:
|
||||
|
||||
| Output path | What it is | Command |
|
||||
|---|---|---|
|
||||
| `devShells.<system>.<name>` | A `mkShell`, a dev environment | `nix develop [.#<name>]` |
|
||||
| `packages.<system>.<name>` | A derivation, a build target | `nix build [.#<name>]` |
|
||||
| `apps.<system>.<name>` | `{ type = "app"; program = …; }` | `nix run [.#<name>]` |
|
||||
| `nixosConfigurations.<host>` | `nixpkgs.lib.nixosSystem { … }` | `nixos-rebuild switch --flake .#<host>` |
|
||||
| `homeConfigurations.<user>` | home-manager config | `home-manager switch --flake .#<user>` |
|
||||
| `nixosModules.<name>` | reusable NixOS module | imported by other flakes |
|
||||
| `overlays.<name>` | nixpkgs overlay | imported by other flakes |
|
||||
| `formatter.<system>` | a formatter derivation | `nix fmt` |
|
||||
| `checks.<system>.<name>` | derivations run by `nix flake check` | `nix flake check` |
|
||||
| `templates.<name>` | scaffold for `nix flake init` | `nix flake init -t .#<name>` |
|
||||
|
||||
Default attrs `default` are picked when you omit the name: `nix build` ≡ `nix build .#default`.
|
||||
|
||||
`.<system>` is the platform triple: `x86_64-linux`, `aarch64-linux`, `aarch64-darwin`, or `x86_64-darwin`.
|
||||
|
||||
---
|
||||
|
||||
## 2. The Lockfile
|
||||
|
||||
`flake.lock` pins the exact commit and NAR hash of every input, transitively. Generated or updated automatically on first use or via `nix flake update`.
|
||||
|
||||
- Commit it. Without it, "my flake" means different things on different machines.
|
||||
- Update explicitly. `nix flake update` (all inputs) or `nix flake update nixpkgs` (one input).
|
||||
- Inspect. `nix flake metadata` prints the resolved URLs and revisions.
|
||||
- `follows` deduplicates: if both `flake-utils` and `foo` want their own `nixpkgs`, `follows = "nixpkgs";` forces them to use yours. Without this, you can end up with multiple nixpkgs copies and subtle version skew.
|
||||
|
||||
---
|
||||
|
||||
## 3. Multi-System Support
|
||||
|
||||
A flake output has to enumerate systems explicitly. Two common patterns:
|
||||
|
||||
### (a) Manual `forAllSystems`
|
||||
|
||||
```nix
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
|
||||
forAllSystems = f:
|
||||
nixpkgs.lib.genAttrs systems (system: f (import nixpkgs { inherit system; }));
|
||||
in {
|
||||
packages = forAllSystems (pkgs: { default = pkgs.hello; });
|
||||
devShells = forAllSystems (pkgs: { default = pkgs.mkShell { packages = [ pkgs.jq ]; }; });
|
||||
};
|
||||
```
|
||||
|
||||
Zero external deps, explicit.
|
||||
|
||||
### (b) `flake-utils` / `flake-parts`
|
||||
|
||||
```nix
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let pkgs = import nixpkgs { inherit system; }; in {
|
||||
packages.default = pkgs.hello;
|
||||
devShells.default = pkgs.mkShell { packages = [ pkgs.jq ]; };
|
||||
});
|
||||
```
|
||||
|
||||
Less boilerplate, at the cost of one dependency. For larger projects, `flake-parts` is increasingly popular: it gives you a module system for flakes themselves.
|
||||
|
||||
---
|
||||
|
||||
## 4. Flake URIs: `.#<name>` Syntax
|
||||
|
||||
Everywhere you point `nix` at a flake, the form is `<flake-ref>#<attr>`:
|
||||
|
||||
```bash
|
||||
nix build . # current dir, packages.<sys>.default
|
||||
nix build .#foo # packages.<sys>.foo
|
||||
nix build github:owner/repo#foo # remote flake
|
||||
nix develop .#ci # devShells.<sys>.ci
|
||||
nix run nixpkgs#hello # pkgs.hello from the nixpkgs registry flake
|
||||
nixos-rebuild switch --flake .#my-host
|
||||
```
|
||||
|
||||
The `<system>` portion is auto-inferred from your machine; you don't spell it out in the CLI.
|
||||
|
||||
---
|
||||
|
||||
## 5. Pure Evaluation: What You Lose, What You Gain
|
||||
|
||||
Flakes evaluate in pure mode:
|
||||
|
||||
- No reading `NIX_PATH` or `<nixpkgs>`.
|
||||
- No arbitrary `builtins.getEnv` or reading files outside the flake.
|
||||
- `builtins.currentTime` and `builtins.currentSystem` are blocked.
|
||||
- Source is pulled from the flake's input tree, not the working directory, unless you're in `self`. Files not tracked by git are invisible to the flake by default.
|
||||
|
||||
This last point surprises people: `git add` a file before `nix build` can see it. (`nix build --impure` escapes this, but then you're not reproducible.)
|
||||
|
||||
In exchange: anyone with your `flake.nix` plus `flake.lock` gets bit-identical evaluation.
|
||||
|
||||
---
|
||||
|
||||
## 6. Common Commands
|
||||
|
||||
```bash
|
||||
nix flake init # scaffold a flake.nix in current dir
|
||||
nix flake init -t github:nix-community/templates#rust
|
||||
nix flake show # tree of all outputs
|
||||
nix flake metadata # locked inputs + paths
|
||||
nix flake check # evaluate & build all `checks` outputs
|
||||
nix flake update # bump all inputs
|
||||
nix flake update nixpkgs # bump one input
|
||||
nix flake lock --override-input nixpkgs github:NixOS/nixpkgs/nixos-24.11
|
||||
nix develop # enter devShells.<sys>.default
|
||||
nix develop .#ci # named shell
|
||||
nix build # build packages.<sys>.default → ./result
|
||||
nix run # run apps.<sys>.default (or packages.<sys>.default)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Recurring Patterns Worth Recognizing
|
||||
|
||||
### Rust / Go / Python Dev Shell
|
||||
```nix
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = [ pkgs.cargo pkgs.rustc pkgs.rust-analyzer ];
|
||||
env.RUST_LOG = "debug";
|
||||
shellHook = ''export FOO=bar'';
|
||||
};
|
||||
```
|
||||
|
||||
### Package a Local Source Tree
|
||||
```nix
|
||||
packages.default = pkgs.rustPlatform.buildRustPackage {
|
||||
pname = "my-tool";
|
||||
version = "0.1.0";
|
||||
src = ./.;
|
||||
cargoLock.lockFile = ./Cargo.lock;
|
||||
};
|
||||
```
|
||||
|
||||
### Expose a NixOS Module
|
||||
```nix
|
||||
nixosModules.default = import ./module.nix;
|
||||
# Consumer does: imports = [ inputs.my-flake.nixosModules.default ];
|
||||
```
|
||||
|
||||
### NixOS System Config as a Flake
|
||||
```nix
|
||||
nixosConfigurations.my-laptop = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [ ./configuration.nix ];
|
||||
};
|
||||
# Apply with: nixos-rebuild switch --flake .#my-laptop
|
||||
```
|
||||
|
||||
### Overlay as an Output
|
||||
```nix
|
||||
overlays.default = final: prev: {
|
||||
my-patched-foo = prev.foo.overrideAttrs (old: { … });
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Debugging Tips
|
||||
|
||||
- `nix flake show --all-systems` to see outputs for every platform, not just yours.
|
||||
- `nix eval .#packages.x86_64-linux.default.drvPath` to get the `.drv` without building.
|
||||
- `nix eval --json .#devShells.x86_64-linux.default.buildInputs | jq` to inspect shell deps.
|
||||
- `--show-trace` on any command for full eval backtraces.
|
||||
- `nix repl .` loads your flake's outputs into a REPL: useful for poking at attrs.
|
||||
- If an input seems stuck on an old rev, check `flake.lock`; `nix flake update <name>` fixes it.
|
||||
|
||||
---
|
||||
|
||||
## 9. When Flakes Are Overkill
|
||||
|
||||
- Running a one-off tool: `nix run nixpkgs#ripgrep`, no flake needed.
|
||||
- A tiny personal script: plain `default.nix` plus `nix-build` is still fine.
|
||||
- Large monorepos with complex CI: consider `flake-parts` or hybrid setups (flake for the interface, regular Nix for guts).
|
||||
|
||||
Flakes are a packaging interface, not a requirement. The old non-flake world still works; flakes just compose better once you have more than one Nix project.
|
||||
11
pyproject.toml
Normal file
11
pyproject.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[project]
|
||||
name = "nix-playground"
|
||||
version = "0.1.0"
|
||||
description = "The Python environment for the `nix-playground` project"
|
||||
|
||||
requires-python = ">=3.10,<4.0"
|
||||
|
||||
dependencies = [
|
||||
"pre-commit (>=4.2.0,<5.0.0)",
|
||||
"icecream (>=2.1.4,<3.0.0)",
|
||||
]
|
||||
Loading…
x
Reference in New Issue
Block a user