The base commit

This commit is contained in:
Hassan Abedi 2026-05-21 12:28:09 +02:00
commit 447f298ac5
7 changed files with 517 additions and 0 deletions

22
.editorconfig Normal file
View 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]
max_line_length = 100
[*.md]
max_line_length = 150
trim_trailing_whitespace = false
[*.{sh,nix,flake}]
indent_size = 2
[*.{yaml,yml,json}]
indent_size = 2

38
.gitattributes vendored Normal file
View 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

86
.gitignore vendored Normal file
View File

@ -0,0 +1,86 @@
# 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
.agents/

44
.pre-commit-config.yaml Normal file
View 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 ]

183
AGENTS.md Normal file
View File

@ -0,0 +1,183 @@
# 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
- `NN-<topic>/`: self-contained numbered examples. Each matching top-level directory is a flake root.
- `notes/NNN-*.md`: prose companions numbered to match reading order.
- Lower note numbers cover shared foundations such as the glossary, the Nix language, and flakes.
- Later note numbers may cover specific example tracks or cross-example guides.
- `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.
Do not assume the directory list or note list in this file is exhaustive. The repository is expected to grow over time, and agents should discover the
current layout from the filesystem when needed.
Example ordering should still feel like a readable path from simpler to more involved, but the repository may branch into themed subtracks, for example
Nix and flake outputs, Haskell with Nix, or future ecosystem-specific tracks. Keep each example focused on one concept even when the broader sequence
branches.
## 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.
When the repository contains multiple themed tracks, "the nearest existing example" means the nearest example in the relevant track, not necessarily the
numerically closest directory overall.
## 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.
- Prefer layout guidance based on naming patterns and discovery, not hard-coded counts of examples or notes. If you need the current tree, inspect it.
- 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
View 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 --
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."

11
pyproject.toml Normal file
View File

@ -0,0 +1,11 @@
[project]
name = "storage-engine-playground"
version = "0.1.0"
description = "The Python environment for the `storage-engine-playground` project"
requires-python = ">=3.10,<4.0"
dependencies = [
"pre-commit (>=4.2.0,<5.0.0)",
"icecream (>=2.1.4,<3.0.0)",
]