#!/usr/bin/env bash # Bumps the NixOS release branch this flake tracks — flake.nix's # `nixpkgs.url` and `home-manager.url` — in one place, via targeted # substitution of just those two lines. Deliberately does NOT touch any # `system.stateVersion` anywhere in the repo: per NixOS's own docs, that # value must stay fixed at whatever it was on a host's first install (it # pins on-disk data-format defaults, not "which nixpkgs release am I on"), # so it's never something a channel bump should follow. # # scripts/codex-maintenance.sh's own `nixos-25.11` pin (used only to fetch # nixpkgs-fmt/statix — see CLAUDE.md) is a separate, independently-versioned # reference on purpose: it doesn't have to track the flake's own nixpkgs # input, since the tooling just needs to build, not match. Bump it with # --tooling if you want it moved too; the default run leaves it alone. set -euo pipefail repo_root="$(cd "$(dirname "$0")/.." && pwd)" flake_nix="${repo_root}/flake.nix" maintenance_sh="${repo_root}/scripts/codex-maintenance.sh" claude_md="${repo_root}/CLAUDE.md" usage() { cat < [--tooling ] New NixOS release for flake.nix's nixpkgs.url and home-manager.url, e.g. 26.11 --tooling Also bump scripts/codex-maintenance.sh's separate nixpkgs-fmt/statix pin (and its mention in CLAUDE.md) to this release. Independent of the first argument — pass the same value if you want both in sync, a different one if you don't. Examples: $0 26.11 $0 26.11 --tooling 26.11 EOF } release_re='^[0-9]{2}\.(05|11)$' if [[ $# -eq 0 || "$1" == "-h" || "$1" == "--help" ]]; then usage exit 0 fi new_release="$1" shift tooling_release="" while [[ $# -gt 0 ]]; do case "$1" in --tooling) tooling_release="${2:?--tooling requires a release argument}" shift 2 ;; *) echo "Unknown argument: $1" >&2 usage >&2 exit 1 ;; esac done check_release_format() { local release="$1" if [[ ! "$release" =~ $release_re ]]; then echo "ERROR: '$release' doesn't look like a NixOS release (expected e.g. 26.11)" >&2 exit 1 fi } check_branch_exists() { local repo_url="$1" branch="$2" echo "Checking '$branch' exists on $repo_url..." if ! git ls-remote --exit-code --heads "$repo_url" "$branch" >/dev/null; then echo "ERROR: branch '$branch' not found on $repo_url. Typo, or not cut yet?" >&2 exit 1 fi } check_release_format "$new_release" current_release="$(grep -oE 'nixos-[0-9]{2}\.[0-9]{2}' "$flake_nix" | head -1 | sed 's/^nixos-//')" if [[ -z "$current_release" ]]; then echo "ERROR: couldn't find flake.nix's current nixpkgs release" >&2 exit 1 fi if [[ "$current_release" == "$new_release" ]]; then echo "flake.nix is already on $new_release." else echo "Bumping flake.nix's nixpkgs/home-manager release: $current_release -> $new_release" check_branch_exists "https://github.com/NixOS/nixpkgs.git" "nixos-$new_release" check_branch_exists "https://github.com/nix-community/home-manager.git" "release-$new_release" sed -i \ -e "s|github:NixOS/nixpkgs/nixos-${current_release}|github:NixOS/nixpkgs/nixos-${new_release}|" \ -e "s|github:nix-community/home-manager/release-${current_release}|github:nix-community/home-manager/release-${new_release}|" \ "$flake_nix" echo "Updated:" grep -n "nixos-${new_release}\|release-${new_release}" "$flake_nix" fi if [[ -n "$tooling_release" ]]; then check_release_format "$tooling_release" current_tooling_release="$(grep -oE 'nixos-[0-9]{2}\.[0-9]{2}' "$maintenance_sh" | head -1 | sed 's/^nixos-//')" if [[ "$current_tooling_release" == "$tooling_release" ]]; then echo "codex-maintenance.sh's tooling pin is already on $tooling_release." else echo "Bumping codex-maintenance.sh's nixpkgs-fmt/statix pin: $current_tooling_release -> $tooling_release" check_branch_exists "https://github.com/NixOS/nixpkgs.git" "nixos-$tooling_release" sed -i "s|github:NixOS/nixpkgs/nixos-${current_tooling_release}|github:NixOS/nixpkgs/nixos-${tooling_release}|g" \ "$maintenance_sh" sed -i "s|nixos-${current_tooling_release}|nixos-${tooling_release}|g" \ "$claude_md" echo "Updated:" grep -n "nixos-${tooling_release}" "$maintenance_sh" "$claude_md" fi fi echo echo "flake.lock still points at the old input revisions until refreshed. Either:" echo " nix flake update nixpkgs home-manager # just these two inputs" echo " nix flake update # everything — see docs/flake-lock-automation.md" echo echo "Then run 'bash scripts/codex-maintenance.sh --full-check --dry-run' before" echo "committing — a channel bump can shift option defaults across every host," echo "and only --dry-run actually builds anything to catch that."