From a77be6e29887bf1766564d178701b638de2548ea Mon Sep 17 00:00:00 2001 From: Romain Gautier Date: Thu, 2 Jul 2026 11:51:20 +0200 Subject: [PATCH] add nix best practices skill --- .agents/skills/nix-best-practices/SKILL.md | 349 +++++++++++++++++++++ skills-lock.json | 6 + 2 files changed, 355 insertions(+) create mode 100644 .agents/skills/nix-best-practices/SKILL.md diff --git a/.agents/skills/nix-best-practices/SKILL.md b/.agents/skills/nix-best-practices/SKILL.md new file mode 100644 index 0000000..afc659c --- /dev/null +++ b/.agents/skills/nix-best-practices/SKILL.md @@ -0,0 +1,349 @@ +--- +name: nix-best-practices +description: Use when working with Nix flakes, overlays, shell.nix, or flake.nix files. +--- + +# Nix Best Practices + +## Flake Structure + +Standard flake.nix structure: + +```nix +{ + description = "Project description"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + }; + in { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + # packages here + ]; + }; + }); +} +``` + +## Follows Pattern (Avoid Duplicate Nixpkgs) + +When adding overlay inputs, use `follows` to share the parent nixpkgs and avoid downloading multiple versions: + +```nix +inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + # Overlay follows parent nixpkgs + some-overlay.url = "github:owner/some-overlay"; + some-overlay.inputs.nixpkgs.follows = "nixpkgs"; + + # Chain follows through intermediate inputs + another-overlay.url = "github:owner/another-overlay"; + another-overlay.inputs.nixpkgs.follows = "some-overlay"; +}; +``` + +All inputs must be listed in outputs function even if not directly used: + +```nix +outputs = { self, nixpkgs, some-overlay, another-overlay, ... }: +``` + +## Applying Overlays + +Overlays modify or add packages to nixpkgs: + +```nix +let + pkgs = import nixpkgs { + inherit system; + overlays = [ + overlay1.overlays.default + overlay2.overlays.default + # Inline overlay + (final: prev: { + myPackage = prev.myPackage.override { ... }; + }) + ]; + }; +in +``` + +## Handling Unfree Packages + +### Option 1: nixpkgs-unfree (Recommended for Teams) + +Use numtide/nixpkgs-unfree for EULA-licensed packages without requiring user config: + +```nix +inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs-unfree.url = "github:numtide/nixpkgs-unfree/nixos-unstable"; + nixpkgs-unfree.inputs.nixpkgs.follows = "nixpkgs"; + + # Unfree overlay follows nixpkgs-unfree + proprietary-tool.url = "github:owner/proprietary-tool-overlay"; + proprietary-tool.inputs.nixpkgs.follows = "nixpkgs-unfree"; +}; +``` + +This chains: `proprietary-tool` → `nixpkgs-unfree` → `nixpkgs` + +### Option 2: User Config + +Users add to `~/.config/nixpkgs/config.nix`: + +```nix +{ allowUnfree = true; } +``` + +### Option 3: Specific Packages (Flake) + +```nix +let + pkgs = import nixpkgs { + inherit system; + config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ + "specific-package" + ]; + }; +in +``` + +Note: `config.allowUnfree` in flake.nix doesn't work with `nix develop` - use nixpkgs-unfree or user config. + +## Creating Binary Overlay Repos + +When nixpkgs builds a community version lacking features (common with open-core tools), create an overlay that fetches official binaries. + +### Pattern (see 0xBigBoss/atlas-overlay, 0xBigBoss/bun-overlay) + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + + version = "1.0.0"; + + # Platform-specific binaries + sources = { + "x86_64-linux" = { + url = "https://example.com/tool-linux-amd64-v${version}"; + sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + "aarch64-linux" = { + url = "https://example.com/tool-linux-arm64-v${version}"; + sha256 = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="; + }; + "x86_64-darwin" = { + url = "https://example.com/tool-darwin-amd64-v${version}"; + sha256 = "sha256-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC="; + }; + "aarch64-darwin" = { + url = "https://example.com/tool-darwin-arm64-v${version}"; + sha256 = "sha256-DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD="; + }; + }; + + source = sources.${system} or (throw "Unsupported system: ${system}"); + + toolPackage = pkgs.stdenv.mkDerivation { + pname = "tool"; + inherit version; + + src = pkgs.fetchurl { + inherit (source) url sha256; + }; + + sourceRoot = "."; + dontUnpack = true; + + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/tool + chmod +x $out/bin/tool + ''; + + meta = with pkgs.lib; { + description = "Tool description"; + homepage = "https://example.com"; + license = licenses.unfree; # or appropriate license + platforms = builtins.attrNames sources; + }; + }; + in { + packages.default = toolPackage; + packages.tool = toolPackage; + + overlays.default = final: prev: { + tool = toolPackage; + }; + }) + // { + overlays.default = final: prev: { + tool = self.packages.${prev.system}.tool; + }; + }; +} +``` + +### Getting SHA256 Hashes + +```bash +nix-prefetch-url https://example.com/tool-linux-amd64-v1.0.0 +# Returns hash in base32, convert to SRI format: +nix hash to-sri --type sha256 +``` + +Or use SRI directly: +```bash +nix-prefetch-url --type sha256 https://example.com/tool-linux-amd64-v1.0.0 +``` + +## Dev Shell Patterns + +### Basic Shell + +```nix +devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + nodejs + python3 + ]; + + shellHook = '' + echo "Dev environment ready" + ''; +}; +``` + +### With Environment Variables + +```nix +devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ postgresql ]; + + # Set at shell entry + DATABASE_URL = "postgres://localhost/dev"; + + # Or in shellHook for dynamic values + shellHook = '' + export PROJECT_ROOT="$(pwd)" + ''; +}; +``` + +### Native Dependencies (C Libraries) + +```nix +devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + openssl + postgresql + ]; + + # Expose headers and libraries + shellHook = '' + export C_INCLUDE_PATH="${pkgs.openssl.dev}/include:$C_INCLUDE_PATH" + export LIBRARY_PATH="${pkgs.openssl.out}/lib:$LIBRARY_PATH" + export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH" + ''; +}; +``` + +## Direnv Integration + +`.envrc` for flake projects: + +```bash +use flake +``` + +For unfree packages without nixpkgs-unfree: + +```bash +export NIXPKGS_ALLOW_UNFREE=1 +use flake --impure +``` + +## Common Commands + +```bash +# Update all inputs +nix flake update + +# Update specific input +nix flake update some-input + +# Check flake validity +nix flake check + +# Show flake metadata +nix flake metadata + +# Enter dev shell +nix develop + +# Run command in dev shell +nix develop -c + +# Build package +nix build .#packageName + +# Run package +nix run .#packageName +``` + +## Troubleshooting + +### "unexpected argument" Error + +All inputs must be listed in outputs function: +```nix +# Wrong +outputs = { self, nixpkgs }: ... + +# Right (if you have more inputs) +outputs = { self, nixpkgs, other-input, ... }: ... +``` + +### Unfree Package Errors with nix develop + +`config.allowUnfree` in flake.nix doesn't propagate to `nix develop`. Use: +1. nixpkgs-unfree input (recommended) +2. User's `~/.config/nixpkgs/config.nix` +3. `NIXPKGS_ALLOW_UNFREE=1 nix develop --impure` + +### Duplicate Nixpkgs Downloads + +Use `follows` to chain inputs to a single nixpkgs source. + +### Overlay Not Applied + +Ensure overlay is in the `overlays` list when importing nixpkgs: +```nix +pkgs = import nixpkgs { + inherit system; + overlays = [ my-overlay.overlays.default ]; +}; +``` + +### Hash Mismatch + +Re-fetch with `nix-prefetch-url` and update the hash. Hashes change when upstream updates binaries at the same URL. diff --git a/skills-lock.json b/skills-lock.json index ebf563f..d2179fc 100644 --- a/skills-lock.json +++ b/skills-lock.json @@ -7,6 +7,12 @@ "skillPath": "skills/frontend-design/SKILL.md", "computedHash": "4eabc66183767153e404b39d1b839b1c37f2d82d86f0a0d7e880a579d8d62336" }, + "nix-best-practices": { + "source": "alleneubank/claude-code", + "sourceType": "github", + "skillPath": ".claude/skills/nix-best-practices/SKILL.md", + "computedHash": "f6d14bf40d4b505dd50cb389711eae74bc1459740adb27f4641cd75c21042311" + }, "skill-creator": { "source": "anthropics/skills", "sourceType": "github", -- 2.51.2