flake.nix
Rama master · réplica de lectura del repositorio original.
# SPDX-License-Identifier: FSL-1.1-Apache-2.0
{
description = "AppRafter platform development environment";
inputs = {
# A RELEASE branch, not nixos-unstable. Unstable already carries
# kubernetes-helm 4.x and nixpkgs has no `kubernetes-helm_3` fallback, so on
# unstable a routine `nix flake update` would swap the dev shell's helm
# major — a toolchain change arriving as a side effect of a lockfile
# refresh, with nothing naming it. The release branch still moves (it is
# a branch, and flake.lock pins the rev), it just does not cross majors
# underneath us.
#
# HELM 3 HERE, HELM 4 IN CI — ON PURPOSE. Every workflow that installs
# helm pins 4.3.0 (scripts/upstream-pins.json `tool-helm`), while this
# shell and the dev container that mirrors it (`devcontainer-helm`) run
# this branch's helm 3, 3.20. `apprafter` runs whatever helm the user has
# installed, users have either major, and the two apply a release
# differently: helm 4 installs server-side, which is why re-running
# cluster-bootstrap after a loader change conflicts with Argo CD there
# (WI-369). The CLI has to work under both, and with CI on 4 this
# tool-belt is where helm 3 still gets run. Two facts bound the split:
# 3.20 receives no patches (the last was 3.20.2, 2026-04-09; helm 3 went on
# to 3.22), and helm 3 as a whole gets security fixes only until
# 2026-11-11. Move the shell and the dev container to helm 4 together
# before then: the next release branch (nixos-26.11) will carry helm 4,
# and if it is not out in time, pin a helm release binary here the way
# `cuePinned` pins cue.
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{
nixpkgs,
flake-utils,
...
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
# restic comes from nixpkgs (0.18.1 on nixos-26.05), unlike cue: the
# CLI's local backup verbs run it against the same repositories the
# in-cluster runner writes, so what matters is that both are on one
# MINOR, and the runner's Dockerfile already asserts that minor at
# build time (`ARG RESTIC_MINOR=`). It is read from there, not written
# a second time, and the dev shell warns when a lock update moves
# nixpkgs' restic to another minor.
resticMinor =
let
prefix = "ARG RESTIC_MINOR=";
lines = pkgs.lib.splitString "\n" (builtins.readFile ./cli/apprafter-backup/Dockerfile);
arg = pkgs.lib.findFirst (pkgs.lib.hasPrefix prefix) null lines;
in
if arg == null then null else pkgs.lib.removePrefix prefix arg;
resticWarning =
if resticMinor == null then
"flake.nix: no `ARG RESTIC_MINOR=` in cli/apprafter-backup/Dockerfile, so the dev shell's restic ${pkgs.restic.version} is checked against nothing."
else if pkgs.lib.versions.majorMinor pkgs.restic.version != resticMinor then
"flake.nix: nixpkgs ships restic ${pkgs.restic.version}, but the backup runner asserts ${resticMinor}.x (cli/apprafter-backup/Dockerfile RESTIC_MINOR): the CLI and the runner would work one repository with different restic minors."
else
null;
# CUE is pinned to the SAME version every other place in this repo pins
# it — the setup-cue inputs in .github/workflows, the CMP sidecar's
# Dockerfile ARG, and .devcontainer/post-create.sh.
#
# It is taken from the upstream release rather than from nixpkgs on
# purpose. `scripts/crd-check.sh` says it runs under `nix develop` so
# that cue is "the flake.lock-pinned version: ONE cue version across
# local and CI" — and until 2026-09 that claim was simply false. The
# flake gave whatever nixpkgs happened to carry (0.16.1) while CI's
# setup-cue installed 0.10.0, six minors apart, and the byte-identity
# gate that sentence exists to justify was comparing output from two
# different evaluators. Fetching the release binary is what makes the
# claim true, and it costs one hash instead of a Go rebuild.
#
# Bumping cue means editing THIS version and hash together with the
# workflow inputs, the Dockerfile ARG and the devcontainer script. The
# hash is the sha256 of the release tarball:
# nix-prefetch-url --type sha256 \
# https://github.com/cue-lang/cue/releases/download/vX.Y.Z/cue_vX.Y.Z_linux_amd64.tar.gz
# Written WITH the leading "v" so it is byte-identical to the string
# every other cue pin in this repo carries — the version watcher
# compares the captured strings literally and reports a mismatch as
# drift, which is the right behaviour and which a bare "0.17.1" here
# would trip on every run.
cueVersion = "v0.17.1";
cuePinned = pkgs.stdenv.mkDerivation {
pname = "cue";
version = pkgs.lib.removePrefix "v" cueVersion;
src = pkgs.fetchurl {
url = "https://github.com/cue-lang/cue/releases/download/${cueVersion}/cue_${cueVersion}_${
if pkgs.stdenv.hostPlatform.isDarwin then "darwin" else "linux"
}_${if pkgs.stdenv.hostPlatform.isAarch64 then "arm64" else "amd64"}.tar.gz";
sha256 =
{
"x86_64-linux" = "sha256-o5sMl2lQadldJ22Zvg9dursIHYAb/cm6Sbdu+vlOI2k=";
}
.${system} or (throw "flake.nix: no cue ${cueVersion} hash recorded for ${system} — add one beside the x86_64-linux entry");
};
sourceRoot = ".";
# A single static binary; there is nothing to build or patch.
dontBuild = true;
dontConfigure = true;
installPhase = "install -Dm755 cue $out/bin/cue";
};
in
{
devShells.default = pkgs.lib.warnIf (resticWarning != null) resticWarning (pkgs.mkShell {
name = "apprafter";
packages = with pkgs; [
# Configuration language — the version pinned above, NOT nixpkgs'.
# See the `cuePinned` comment for why.
cuePinned
# Argo CD ships custom resource health as Lua in `argocd-cm`, and a
# broken script fails SILENTLY — Argo logs it and falls back. This
# is what `scripts/check-argocd-health-lua.sh` runs them under.
lua
# Rust toolchain
cargo
rustc
rustfmt
clippy
rust-analyzer
# Dependency health, which the version watcher cannot see: an
# abandoned crate sits on its own final release forever and so is
# never "behind". `scripts/cargo-deny.sh` falls back to
# `nix run nixpkgs#cargo-deny` without this, but having it in the
# shell keeps the dev-loop run fast.
cargo-deny
# JavaScript / TypeScript runtime (for Backstage tooling)
bun
# Kubernetes tooling
kubectl
k9s
kubernetes-helm
k3d
kind # local e2e cluster on podman (rootless) — k3d needs docker
argocd
cilium-cli
# Talos / bare-metal
talosctl
# Container build / supply-chain
cosign
syft
trivy
grype
# Repo tooling
just
lefthook
age
sops
jq
git
# Backups: `apprafter backup create/list/show/check/prune`,
# `restore` and `export` run restic locally. Held to the runner's
# minor by `resticWarning` above.
restic
# Documentation site. ONE python env — `nix shell
# nixpkgs#python3Packages.mkdocs-material` ships no `mkdocs`
# binary, and adding `python3Packages.mkdocs` alongside it
# yields a second env whose site-packages lacks the theme
# ("Unrecognised theme name: 'material'"). literate-nav
# renders the generated CLI reference nav (docsgen SUMMARY.md);
# redirects is installed for the W5 IA move.
(python3.withPackages (ps: [
ps.mkdocs-material
ps.mkdocs-literate-nav
ps.mkdocs-redirects
]))
];
# mkdocs-material 9.7.6 prints a red MkDocs-2.0 advocacy banner on
# EVERY mkdocs invocation (material/templates/__init__.py gates it
# on this variable). It is upstream's opinion of a framework
# release this site does not run — the line readers react to,
# "Currently unlicensed - unsuitable for production use", is about
# MkDocs 2.0 and not about the theme here.
#
# Silenced in the devShell rather than in the Justfile because
# every call site goes through `nix develop`: `just docs-serve`,
# `just docs-build`, `scripts/docs-check.sh` and release-docs.yml.
# Three prefixed recipes would leave the CI logs noisy and would
# miss any future `nix develop --command mkdocs ...` typed by hand.
#
# It also shrinks a real hazard: scripts/docs-check.sh tees mkdocs
# stderr into a file it then pattern-matches, so third-party output
# in that stream is one grep collision away from a false failure.
NO_MKDOCS_2_WARNING = "1";
shellHook = ''
echo "AppRafter dev shell ready."
echo
echo "Useful commands:"
echo " just --list # available targets"
echo " just bootstrap # install git hooks"
echo " just lint # CUE + SPDX + docs + conditional Rust/TS"
echo " just e2e-up # local k3d cluster"
echo
'';
});
# Exposed so `scripts/cue` can reach the pinned binary WITHOUT
# `nix develop`, whose shellHook prints a banner onto stdout and would
# corrupt every caller that captures cue's output.
packages.cue = cuePinned;
formatter = pkgs.nixfmt;
}
);
}