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`)
|
||||
```
|
||||
|
||||
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`):
|
||||
|
||||
```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