Add two Nix/Flake examples (with their note files)
This commit is contained in:
parent
e5906d9163
commit
1353687dc2
20
39-flake-apps/README.md
Normal file
20
39-flake-apps/README.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# 39-flake-apps
|
||||||
|
|
||||||
|
This example shows dedicated `apps.<system>` outputs.
|
||||||
|
|
||||||
|
It includes:
|
||||||
|
|
||||||
|
- one default app that prints a rollout report,
|
||||||
|
- one named app, `promote`, that prints the next rollout wave,
|
||||||
|
- package outputs that back those apps, and
|
||||||
|
- a check that runs both app programs.
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake show
|
||||||
|
nix run
|
||||||
|
nix run .#promote
|
||||||
|
nix build .#report
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
27
39-flake-apps/flake.lock
generated
Normal file
27
39-flake-apps/flake.lock
generated
Normal file
@ -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
|
||||||
|
}
|
||||||
112
39-flake-apps/flake.nix
Normal file
112
39-flake-apps/flake.nix
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
# Exposes `apps.<system>` entries that point at small runnable programs.
|
||||||
|
description = "A minimal flake apps example";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{ self, nixpkgs, ... }:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
|
||||||
|
releaseName = "search-index-2026-04";
|
||||||
|
releasePlan = [
|
||||||
|
{
|
||||||
|
stage = "staging";
|
||||||
|
percentage = 10;
|
||||||
|
owner = "team-search";
|
||||||
|
gates = [
|
||||||
|
"smoke-tests"
|
||||||
|
"error-budget"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
stage = "canary";
|
||||||
|
percentage = 35;
|
||||||
|
owner = "team-search";
|
||||||
|
gates = [
|
||||||
|
"dashboard-review"
|
||||||
|
"support-readiness"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
stage = "production";
|
||||||
|
percentage = 100;
|
||||||
|
owner = "team-search";
|
||||||
|
gates = [
|
||||||
|
"canary-slo"
|
||||||
|
"stakeholder-signoff"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
renderWave =
|
||||||
|
wave:
|
||||||
|
"${wave.stage}: ${toString wave.percentage}% owned by ${wave.owner}; gates: ${builtins.concatStringsSep ", " wave.gates}";
|
||||||
|
|
||||||
|
reportText = builtins.concatStringsSep "\n" (
|
||||||
|
[
|
||||||
|
"release: ${releaseName}"
|
||||||
|
"waves:"
|
||||||
|
]
|
||||||
|
++ map renderWave releasePlan
|
||||||
|
);
|
||||||
|
|
||||||
|
nextWave = builtins.elemAt releasePlan 1;
|
||||||
|
|
||||||
|
reportPackage = pkgs.writeShellApplication {
|
||||||
|
name = "show-rollout-report";
|
||||||
|
text = ''
|
||||||
|
cat <<'EOF'
|
||||||
|
${reportText}
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
promotePackage = pkgs.writeShellApplication {
|
||||||
|
name = "suggest-next-promotion";
|
||||||
|
text = ''
|
||||||
|
echo "next wave: ${nextWave.stage} (${toString nextWave.percentage}%) after ${builtins.concatStringsSep " and " nextWave.gates}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.${system} = {
|
||||||
|
default = reportPackage;
|
||||||
|
report = reportPackage;
|
||||||
|
promote = promotePackage;
|
||||||
|
};
|
||||||
|
|
||||||
|
apps.${system} = {
|
||||||
|
default = {
|
||||||
|
type = "app";
|
||||||
|
program = "${reportPackage}/bin/show-rollout-report";
|
||||||
|
meta.description = "Print the rollout report for a release train.";
|
||||||
|
};
|
||||||
|
|
||||||
|
promote = {
|
||||||
|
type = "app";
|
||||||
|
program = "${promotePackage}/bin/suggest-next-promotion";
|
||||||
|
meta.description = "Print the next rollout wave that should be promoted.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
checks.${system}.apps-run = pkgs.runCommand "flake-apps-run" { } ''
|
||||||
|
report="$(${self.apps.${system}.default.program})"
|
||||||
|
promote="$(${self.apps.${system}.promote.program})"
|
||||||
|
|
||||||
|
printf '%s\n' "$report" | grep -q '^release: ${releaseName}$'
|
||||||
|
printf '%s\n' "$report" | grep -q '^production: 100% owned by team-search; gates: canary-slo, stakeholder-signoff$'
|
||||||
|
|
||||||
|
if [ "$promote" != "next wave: canary (35%) after dashboard-review and support-readiness" ]; then
|
||||||
|
echo "unexpected promotion output: $promote" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ok > "$out"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
19
40-path-inputs/README.md
Normal file
19
40-path-inputs/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# 40-path-inputs
|
||||||
|
|
||||||
|
This example shows a local `path:` input that points at another flake inside the example directory.
|
||||||
|
|
||||||
|
It includes:
|
||||||
|
|
||||||
|
- one nested flake in `catalog/`,
|
||||||
|
- pure release metadata exposed from that nested flake through `lib`,
|
||||||
|
- one package that renders the imported data, and
|
||||||
|
- a check that proves the parent flake is reading the child flake output.
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake metadata
|
||||||
|
nix build
|
||||||
|
./result/bin/show-local-plan
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
48
40-path-inputs/catalog/flake.nix
Normal file
48
40-path-inputs/catalog/flake.nix
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
# Exposes pure release metadata for the parent flake that imports this
|
||||||
|
# directory with `path:./catalog`.
|
||||||
|
description = "Local release catalog for the path input example";
|
||||||
|
|
||||||
|
outputs = _: {
|
||||||
|
lib = {
|
||||||
|
releaseName = "checkout-2026-05";
|
||||||
|
|
||||||
|
serviceOwners = {
|
||||||
|
api = "team-checkout";
|
||||||
|
billing = "team-revenue";
|
||||||
|
worker = "team-fulfillment";
|
||||||
|
};
|
||||||
|
|
||||||
|
rolloutWaves = [
|
||||||
|
{
|
||||||
|
name = "staging";
|
||||||
|
services = [
|
||||||
|
"api"
|
||||||
|
"worker"
|
||||||
|
];
|
||||||
|
approvals = [ "qa" ];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "canary";
|
||||||
|
services = [ "api" ];
|
||||||
|
approvals = [
|
||||||
|
"sre"
|
||||||
|
"support"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "production";
|
||||||
|
services = [
|
||||||
|
"api"
|
||||||
|
"billing"
|
||||||
|
"worker"
|
||||||
|
];
|
||||||
|
approvals = [
|
||||||
|
"change-advisory"
|
||||||
|
"finance-signoff"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
39
40-path-inputs/flake.lock
generated
Normal file
39
40-path-inputs/flake.lock
generated
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"catalog": {
|
||||||
|
"locked": {
|
||||||
|
"path": "./catalog",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"path": "./catalog",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"parent": []
|
||||||
|
},
|
||||||
|
"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": {
|
||||||
|
"catalog": "catalog",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
65
40-path-inputs/flake.nix
Normal file
65
40-path-inputs/flake.nix
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
# Uses a local `path:` input to consume pure data from another flake.
|
||||||
|
description = "A minimal local path input example";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
catalog.url = "path:./catalog";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
catalog,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
|
||||||
|
inherit (catalog.lib)
|
||||||
|
releaseName
|
||||||
|
serviceOwners
|
||||||
|
rolloutWaves
|
||||||
|
;
|
||||||
|
|
||||||
|
renderWave =
|
||||||
|
wave:
|
||||||
|
"${wave.name}: ${builtins.concatStringsSep ", " wave.services} with approvals ${builtins.concatStringsSep ", " wave.approvals}";
|
||||||
|
|
||||||
|
ownerLines = pkgs.lib.mapAttrsToList (service: owner: "${service}: ${owner}") serviceOwners;
|
||||||
|
|
||||||
|
planText = builtins.concatStringsSep "\n" (
|
||||||
|
[
|
||||||
|
"release: ${releaseName}"
|
||||||
|
"owners:"
|
||||||
|
]
|
||||||
|
++ ownerLines
|
||||||
|
++ [
|
||||||
|
"waves:"
|
||||||
|
]
|
||||||
|
++ map renderWave rolloutWaves
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.${system}.default = pkgs.writeShellApplication {
|
||||||
|
name = "show-local-plan";
|
||||||
|
text = ''
|
||||||
|
cat <<'EOF'
|
||||||
|
${planText}
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
checks.${system}.catalog-is-used = pkgs.runCommand "local-path-input-check" { } ''
|
||||||
|
plan="$(${pkgs.lib.getExe self.packages.${system}.default})"
|
||||||
|
|
||||||
|
printf '%s\n' "$plan" | grep -q '^release: checkout-2026-05$'
|
||||||
|
printf '%s\n' "$plan" | grep -q '^billing: team-revenue$'
|
||||||
|
printf '%s\n' "$plan" | grep -q '^production: api, billing, worker with approvals change-advisory, finance-signoff$'
|
||||||
|
|
||||||
|
echo ok > "$out"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -48,6 +48,11 @@ tarball+https://…/src.tar.gz
|
|||||||
nixpkgs # registry alias (resolves via `nix registry`)
|
nixpkgs # registry alias (resolves via `nix registry`)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This repository now has focused examples for two of those cases:
|
||||||
|
|
||||||
|
- `39-flake-apps/` covers `apps.<system>` outputs, and
|
||||||
|
- `40-path-inputs/` covers a local `path:` input.
|
||||||
|
|
||||||
Non-flake sources (a repo without `flake.nix`):
|
Non-flake sources (a repo without `flake.nix`):
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
|
|||||||
62
notes/042-flake-apps.md
Normal file
62
notes/042-flake-apps.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Flake Apps
|
||||||
|
|
||||||
|
This note covers `39-flake-apps/`, which exposes runnable `apps.<system>` outputs instead of relying only on packages.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. What an App Output Is
|
||||||
|
|
||||||
|
An app output is a small attrset with a type and a program path:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
apps.${system}.default = {
|
||||||
|
type = "app";
|
||||||
|
program = "${reportPackage}/bin/show-rollout-report";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
`nix run` looks for this schema. The program must already exist in the Nix store, so apps usually point at a package output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Why This Example Still Defines Packages
|
||||||
|
|
||||||
|
The runnable programs are built with `writeShellApplication`, then exposed in two ways:
|
||||||
|
|
||||||
|
- `packages.<system>.report` and `packages.<system>.promote` build the scripts, and
|
||||||
|
- `apps.<system>.default` and `apps.<system>.promote` tell `nix run` which program to execute.
|
||||||
|
|
||||||
|
That separation keeps the build logic in packages and the command-line entry points in apps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. What the Example Demonstrates
|
||||||
|
|
||||||
|
This example keeps the data a little richer than a one-line hello script:
|
||||||
|
|
||||||
|
- one release train,
|
||||||
|
- three rollout waves, and
|
||||||
|
- one named app for the next promotion step.
|
||||||
|
|
||||||
|
That makes it clear that app outputs are just pointers to real programs. They do not replace packages or change how the program is built.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. What the Check Verifies
|
||||||
|
|
||||||
|
`nix flake check` does not run apps automatically, so the example adds a check that executes both app program paths and verifies their output.
|
||||||
|
|
||||||
|
That is the pattern to remember: expose the app through `apps`, then add an explicit check if you want CI to prove the command works.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Commands to Try
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd 39-flake-apps
|
||||||
|
|
||||||
|
nix flake show
|
||||||
|
nix run
|
||||||
|
nix run .#promote
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
57
notes/043-local-path-inputs.md
Normal file
57
notes/043-local-path-inputs.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Local Path Inputs
|
||||||
|
|
||||||
|
This note covers `40-path-inputs/`, which uses `inputs.catalog.url = "path:./catalog";` to pull another local flake into the parent flake.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. What a Local Path Input Looks Like
|
||||||
|
|
||||||
|
A local input is declared with a `path:` flake reference:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
inputs.catalog.url = "path:./catalog";
|
||||||
|
```
|
||||||
|
|
||||||
|
The target directory must itself be a flake, so `catalog/` contains its own `flake.nix`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. What the Child Flake Exposes
|
||||||
|
|
||||||
|
The child flake in this example does not build packages. It exports pure data through `lib`:
|
||||||
|
|
||||||
|
- one release name,
|
||||||
|
- one service-owner map, and
|
||||||
|
- one rollout plan.
|
||||||
|
|
||||||
|
The parent flake reads those values and turns them into a runnable script.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Why This Example Uses `lib`
|
||||||
|
|
||||||
|
Using `lib` keeps the focus on the input boundary, not on packaging details in both flakes.
|
||||||
|
|
||||||
|
That is the important part of `path:` inputs: once the child is a flake, its outputs are consumed the same way as any other flake input.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. The Git-Tracked File Rule Still Applies
|
||||||
|
|
||||||
|
This is still a flake input, so Nix reads it from the flake source tree. Files inside `catalog/` must be tracked, or the parent flake will not see
|
||||||
|
them during pure evaluation.
|
||||||
|
|
||||||
|
That rule is easy to miss when the child flake lives in the same repository.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Commands to Try
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd 40-path-inputs
|
||||||
|
|
||||||
|
nix flake metadata
|
||||||
|
nix build
|
||||||
|
./result/bin/show-local-plan
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
Loading…
x
Reference in New Issue
Block a user