From 5967d1bb611836d0e993607f04d21362beec1159 Mon Sep 17 00:00:00 2001 From: Hassan Abedi Date: Mon, 4 May 2026 11:29:17 +0200 Subject: [PATCH] Add two Nix/Flake examples --- 41-non-flake-inputs/README.md | 18 +++++ 41-non-flake-inputs/flake.lock | 40 ++++++++++ 41-non-flake-inputs/flake.nix | 65 +++++++++++++++++ .../release-data/manifest.json | 16 ++++ 42-lib-outputs/README.md | 19 +++++ 42-lib-outputs/flake.lock | 27 +++++++ 42-lib-outputs/flake.nix | 73 +++++++++++++++++++ notes/003-flakes.md | 12 ++- notes/044-non-flake-inputs.md | 56 ++++++++++++++ notes/045-lib-outputs.md | 56 ++++++++++++++ 10 files changed, 379 insertions(+), 3 deletions(-) create mode 100644 41-non-flake-inputs/README.md create mode 100644 41-non-flake-inputs/flake.lock create mode 100644 41-non-flake-inputs/flake.nix create mode 100644 41-non-flake-inputs/release-data/manifest.json create mode 100644 42-lib-outputs/README.md create mode 100644 42-lib-outputs/flake.lock create mode 100644 42-lib-outputs/flake.nix create mode 100644 notes/044-non-flake-inputs.md create mode 100644 notes/045-lib-outputs.md diff --git a/41-non-flake-inputs/README.md b/41-non-flake-inputs/README.md new file mode 100644 index 0000000..8bb9178 --- /dev/null +++ b/41-non-flake-inputs/README.md @@ -0,0 +1,18 @@ +# 41-non-flake-inputs + +This example shows a `flake = false` input that exposes raw files instead of flake outputs. + +It includes: + +- one local input in `release-data/` without its own `flake.nix`, +- one package that reads JSON from that input during evaluation, and +- a check that proves the raw source and derived values match. + +Useful commands: + +```bash +nix flake metadata +nix build +./result/bin/show-raw-release +nix flake check +``` diff --git a/41-non-flake-inputs/flake.lock b/41-non-flake-inputs/flake.lock new file mode 100644 index 0000000..cc63c51 --- /dev/null +++ b/41-non-flake-inputs/flake.lock @@ -0,0 +1,40 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1776548001, + "narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "releaseData": { + "flake": false, + "locked": { + "path": "./release-data", + "type": "path" + }, + "original": { + "path": "./release-data", + "type": "path" + }, + "parent": [] + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "releaseData": "releaseData" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/41-non-flake-inputs/flake.nix b/41-non-flake-inputs/flake.nix new file mode 100644 index 0000000..44cb6e4 --- /dev/null +++ b/41-non-flake-inputs/flake.nix @@ -0,0 +1,65 @@ +{ + # Consumes a non-flake input as raw source by setting `flake = false`. + description = "Consume a non-flake input as raw source"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + releaseData = { + url = "path:./release-data"; + flake = false; + }; + }; + + outputs = + { self, nixpkgs, ... }: + let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + manifest = builtins.fromJSON (builtins.readFile "${self.inputs.releaseData}/manifest.json"); + + renderWave = wave: "${wave.stage}: ${toString wave.percentage}% owned by ${wave.owner}"; + + reportText = builtins.concatStringsSep "\n" ( + [ + "release: ${manifest.release}" + "channel: ${manifest.channel}" + "waves:" + ] + ++ map renderWave manifest.waves + ); + in + { + packages.${system}.default = pkgs.writeShellApplication { + name = "show-raw-release"; + text = '' + cat <<'EOF' + ${reportText} + EOF + ''; + }; + + checks.${system}.release-data = + pkgs.runCommand "non-flake-input-check" + { + releaseName = manifest.release; + waveCount = toString (builtins.length manifest.waves); + rawManifest = builtins.readFile "${self.inputs.releaseData}/manifest.json"; + } + '' + if [ "$releaseName" != "edge-cache-2026-05" ]; then + echo "unexpected release name: $releaseName" >&2 + exit 1 + fi + + if [ "$waveCount" != "2" ]; then + echo "unexpected wave count: $waveCount" >&2 + exit 1 + fi + + printf '%s\n' "$rawManifest" | grep -q '"channel": "canary"' + + echo ok > "$out" + ''; + }; +} diff --git a/41-non-flake-inputs/release-data/manifest.json b/41-non-flake-inputs/release-data/manifest.json new file mode 100644 index 0000000..e95b40e --- /dev/null +++ b/41-non-flake-inputs/release-data/manifest.json @@ -0,0 +1,16 @@ +{ + "release": "edge-cache-2026-05", + "channel": "canary", + "waves": [ + { + "stage": "staging", + "percentage": 20, + "owner": "team-edge" + }, + { + "stage": "production", + "percentage": 100, + "owner": "team-edge" + } + ] +} diff --git a/42-lib-outputs/README.md b/42-lib-outputs/README.md new file mode 100644 index 0000000..eaab7c5 --- /dev/null +++ b/42-lib-outputs/README.md @@ -0,0 +1,19 @@ +# 42-lib-outputs + +This example shows a `lib` output for pure reusable helpers and data. + +It includes: + +- one `lib` attrset with rollout data and render helpers, +- one package that turns those helpers into a runnable report, and +- a check that proves the exported `lib` values can be consumed through the flake output. + +Useful commands: + +```bash +nix flake show +nix eval .#lib.ownerByService.api --raw +nix build +./result/bin/show-lib-plan +nix flake check +``` diff --git a/42-lib-outputs/flake.lock b/42-lib-outputs/flake.lock new file mode 100644 index 0000000..dfdfdf9 --- /dev/null +++ b/42-lib-outputs/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1776548001, + "narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/42-lib-outputs/flake.nix b/42-lib-outputs/flake.nix new file mode 100644 index 0000000..3f93354 --- /dev/null +++ b/42-lib-outputs/flake.nix @@ -0,0 +1,73 @@ +{ + # Exposes a `lib` attrset for pure helper values that other outputs can reuse. + description = "Expose reusable helpers through lib"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = + { self, nixpkgs, ... }: + let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + + playgroundLib = rec { + rolloutPlan = [ + { + service = "api"; + wave = 1; + owner = "team-edge"; + } + { + service = "worker"; + wave = 2; + owner = "team-batch"; + } + ]; + + ownerByService = builtins.listToAttrs ( + map (step: { + name = step.service; + value = step.owner; + }) rolloutPlan + ); + + renderStep = step: "wave ${toString step.wave}: ${step.service} owned by ${step.owner}"; + + renderPlan = builtins.concatStringsSep "\n" (map renderStep rolloutPlan); + }; + in + { + lib = playgroundLib; + + packages.${system}.default = pkgs.writeShellApplication { + name = "show-lib-plan"; + text = '' + cat <<'EOF' + ${self.lib.renderPlan} + EOF + ''; + }; + + checks.${system}.lib-output = + pkgs.runCommand "lib-output-check" + { + apiOwner = self.lib.ownerByService.api; + workerLine = self.lib.renderStep (builtins.elemAt self.lib.rolloutPlan 1); + } + '' + if [ "$apiOwner" != "team-edge" ]; then + echo "unexpected api owner: $apiOwner" >&2 + exit 1 + fi + + if [ "$workerLine" != "wave 2: worker owned by team-batch" ]; then + echo "unexpected worker line: $workerLine" >&2 + exit 1 + fi + + echo ok > "$out" + ''; + }; +} diff --git a/notes/003-flakes.md b/notes/003-flakes.md index 06b393e..83d9d25 100644 --- a/notes/003-flakes.md +++ b/notes/003-flakes.md @@ -48,10 +48,11 @@ tarball+https://…/src.tar.gz nixpkgs # registry alias (resolves via `nix registry`) ``` -This repository now has focused examples for two of those cases: +This repository now has focused examples for several of those cases: -- `39-flake-apps/` covers `apps.` outputs, and -- `40-path-inputs/` covers a local `path:` input. +- `39-flake-apps/` covers `apps.` outputs, +- `40-path-inputs/` covers a local flake `path:` input, and +- `41-non-flake-inputs/` covers a local `path:` input with `flake = false`. Non-flake sources (a repo without `flake.nix`): @@ -60,6 +61,8 @@ inputs.some-src = { url = "github:owner/repo"; flake = false; }; # Access raw files via self.inputs.some-src ``` +`41-non-flake-inputs/` shows that pattern with a local source tree and a JSON file. + ### Outputs A function: `{ self, ...inputs }: `. The attrset keys are *conventions* the `nix` CLI knows about: @@ -81,6 +84,9 @@ Default attrs `default` are picked when you omit the name: `nix build` ≡ `nix `.` is the platform triple: `x86_64-linux`, `aarch64-linux`, `aarch64-darwin`, or `x86_64-darwin`. +Arbitrary attrs are still allowed alongside those schema keys. `lib` is a common convention for pure helper functions and data; `42-lib-outputs/` +shows one focused example. + --- ## 2. The Lockfile diff --git a/notes/044-non-flake-inputs.md b/notes/044-non-flake-inputs.md new file mode 100644 index 0000000..05cd7c4 --- /dev/null +++ b/notes/044-non-flake-inputs.md @@ -0,0 +1,56 @@ +# Non-flake Inputs + +This note covers `41-non-flake-inputs/`, which declares `inputs.releaseData.flake = false;` so the input is consumed as raw source instead of as a flake. + +--- + +## 1. What `flake = false` Changes + +Normally, a flake input is expected to expose `outputs`. Setting `flake = false` tells Nix to treat the input as a plain source tree instead. + +The example reads that source through `self.inputs.releaseData`, so it works with files directly instead of importing another flake interface. + +--- + +## 2. Why This Example Uses a Local Path + +The example keeps the source local with: + +```nix +releaseData = { + url = "path:./release-data"; + flake = false; +}; +``` + +That makes the boundary obvious: + +- `release-data/` has no `flake.nix`, +- the parent flake reads `manifest.json` directly, and +- the package is built from values parsed out of that JSON file. + +The same pattern works with remote GitHub sources that are not flakes. The important part is `flake = false`, not the transport. + +--- + +## 3. What the Check Verifies + +The check looks at both layers: + +- parsed values such as the release name and wave count, and +- the raw JSON text from the input path. + +That keeps the example focused on the real distinction: a non-flake input gives you files to read, not outputs to import. + +--- + +## 4. Commands to Try + +```bash +cd 41-non-flake-inputs + +nix flake metadata +nix build +./result/bin/show-raw-release +nix flake check +``` diff --git a/notes/045-lib-outputs.md b/notes/045-lib-outputs.md new file mode 100644 index 0000000..6678b76 --- /dev/null +++ b/notes/045-lib-outputs.md @@ -0,0 +1,56 @@ +# Lib Outputs + +This note covers `42-lib-outputs/`, which exposes a `lib` attrset with pure data and helper functions that other outputs can reuse. + +--- + +## 1. Why `lib` Is Useful + +Only some output names have special meaning to the `nix` CLI. A flake can still expose arbitrary attrs for consumers. + +`lib` is a common convention for that. It is a good place for: + +- pure helper functions, +- derived lookup tables, and +- reusable domain data. + +This example keeps the `lib` attrset small so the distinction is clear: the flake exports logic, not just build targets. + +--- + +## 2. What the Example Exports + +The `lib` output contains: + +- `rolloutPlan`, a small list of pure data, +- `ownerByService`, derived from that list, and +- `renderStep` plus `renderPlan`, which turn the data into text. + +The package output then prints `self.lib.renderPlan`, so the runnable artifact is built from the same exported helper values that outside consumers can evaluate. + +--- + +## 3. Why the Check Uses `self.lib` + +The check reaches back through the public output path: + +```nix +apiOwner = self.lib.ownerByService.api; +workerLine = self.lib.renderStep (builtins.elemAt self.lib.rolloutPlan 1); +``` + +That matters because it proves the helper data is not only local implementation detail in a `let` block. It is part of the flake interface. + +--- + +## 4. Commands to Try + +```bash +cd 42-lib-outputs + +nix flake show +nix eval .#lib.ownerByService.api --raw +nix build +./result/bin/show-lib-plan +nix flake check +```