From: Scott Worley Date: Fri, 2 Jan 2026 06:57:59 +0000 (-0800) Subject: 25.11: Formatting: nixfmt-classic → nixfmt-rfc-style X-Git-Url: http://git.scottworley.com/auto-upgrade-with-pinch/commitdiff_plain/a084161200e7edcdb818651e8b23a0757507579e?ds=sidebyside;hp=701e41fe3e4f8a6b61cefd3761794d899ce25d46 25.11: Formatting: nixfmt-classic → nixfmt-rfc-style --- diff --git a/default.nix b/default.nix index 1413fef..348ba97 100644 --- a/default.nix +++ b/default.nix @@ -1,10 +1,13 @@ -{ pkgs ? import { }, }: +{ + pkgs ? import { }, +}: -pkgs.lib.makeScope pkgs.newScope (self: - with self; { +pkgs.lib.makeScope pkgs.newScope ( + self: with self; { homeless-gpg = callPackage ./pkgs/homeless-gpg.nix { }; keyed-gpg = callPackage ./pkgs/keyed-gpg.nix { }; - }) + } +) diff --git a/modules/auto-upgrade.nix b/modules/auto-upgrade.nix index 46e3e7f..c52f0bc 100644 --- a/modules/auto-upgrade.nix +++ b/modules/auto-upgrade.nix @@ -4,7 +4,12 @@ # under the terms of the GNU General Public License as published by the # Free Software Foundation, version 3. -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: with lib; let local-pkgs = import ../. { inherit pkgs; }; @@ -67,39 +72,39 @@ let auto-upgrade-script = pkgs.writeShellScript "auto-upgrade" '' ${pkgs.coreutils}/bin/nice -n 17 \ ${pkgs.util-linux}/bin/ionice -c 3 \ - ${pkgs.util-linux}/bin/flock /run/auto-upgrade-with-pinch ${ - pkgs.writeShellScript "auto-upgrade-with-lock-held" '' - set -eo pipefail - - dry_run=false - pinch_args=() - if [[ "$1" == --dry-run ]];then - dry_run=true - pinch_args=( --dry-run ) - fi - - hydrate() { - if "$dry_run";then - echo "Would run: $*" - else - "$@" - fi - } - - die() { - echo "$*" >&2 - exit 1 - } - - in_tmpdir() { - d=$(${pkgs.coreutils}/bin/mktemp -d) - pushd "$d" + ${pkgs.util-linux}/bin/flock /run/auto-upgrade-with-pinch ${pkgs.writeShellScript "auto-upgrade-with-lock-held" '' + set -eo pipefail + + dry_run=false + pinch_args=() + if [[ "$1" == --dry-run ]];then + dry_run=true + pinch_args=( --dry-run ) + fi + + hydrate() { + if "$dry_run";then + echo "Would run: $*" + else "$@" - popd - ${pkgs.coreutils}/bin/rm -r "$d" - } - - ${optionalString (cfg.upgradeConfigOwnershipPolicy != "any") ('' + fi + } + + die() { + echo "$*" >&2 + exit 1 + } + + in_tmpdir() { + d=$(${pkgs.coreutils}/bin/mktemp -d) + pushd "$d" + "$@" + popd + ${pkgs.coreutils}/bin/rm -r "$d" + } + + ${optionalString (cfg.upgradeConfigOwnershipPolicy != "any") ( + '' verify_ownership() { if [[ "$1" != /* ]];then die "Unexpected relative path: $1" @@ -145,68 +150,69 @@ let fi fi ''; - }."${cfg.upgradeConfigOwnershipPolicy}" + } + ."${cfg.upgradeConfigOwnershipPolicy}" } } '' - + concatMapStringsSep "\n" (f: "verify_ownership ${escapeShellArg f}") - cfg.upgradeConfig)} - - config=$(${pkgs.nix}/bin/nix-instantiate --eval --strict --json -A config \ - --arg upgradeConfig ${ - escapeShellArg ("[" - + lib.concatMapStringsSep " " lib.strings.escapeNixString - cfg.upgradeConfig + "]") - } ${../upgrade-config.nix}) - - config_query() { - ${pkgs.jq}/bin/jq -r "$@" <<< "$config" - } - - repo_query() { - config_query --arg path "$1" ".repos[\$ARGS.named.path]$2" - } - - userenv_query() { - config_query --arg user "$1" ".userEnvironments[\$ARGS.named.user]$2" - } - - # Pull updates - while read path;do - hydrate /run/wrappers/bin/sudo -u "$(repo_query "$path" .user)" \ - ${pull-repo-script} "$path" "$(repo_query "$path" "")" - done < <( config_query '.repos | keys []' ) - - # Update channels - config_query '.pinchFiles[]' | ${pkgs.findutils}/bin/xargs --no-run-if-empty --delimiter=\\n ${pkgs.pinch}/bin/pinch update "''${pinch_args[@]}" - - # Build - in_tmpdir hydrate ${config.system.build.nixos-rebuild}/bin/nixos-rebuild build - while read user;do - pushd / - hydrate /run/wrappers/bin/sudo -u "$user" \ - ${pkgs.nix}/bin/nix-build --no-out-link '' -A "$(userenv_query "$user" .package)" - popd - done < <( config_query '.userEnvironments | keys []' ) - sync - - # Install - hydrate ${config.system.build.nixos-rebuild}/bin/nixos-rebuild switch + + concatMapStringsSep "\n" (f: "verify_ownership ${escapeShellArg f}") cfg.upgradeConfig + )} + + config=$(${pkgs.nix}/bin/nix-instantiate --eval --strict --json -A config \ + --arg upgradeConfig ${ + escapeShellArg ( + "[" + lib.concatMapStringsSep " " lib.strings.escapeNixString cfg.upgradeConfig + "]" + ) + } ${../upgrade-config.nix}) + + config_query() { + ${pkgs.jq}/bin/jq -r "$@" <<< "$config" + } + + repo_query() { + config_query --arg path "$1" ".repos[\$ARGS.named.path]$2" + } + + userenv_query() { + config_query --arg user "$1" ".userEnvironments[\$ARGS.named.user]$2" + } + + # Pull updates + while read path;do + hydrate /run/wrappers/bin/sudo -u "$(repo_query "$path" .user)" \ + ${pull-repo-script} "$path" "$(repo_query "$path" "")" + done < <( config_query '.repos | keys []' ) + + # Update channels + config_query '.pinchFiles[]' | ${pkgs.findutils}/bin/xargs --no-run-if-empty --delimiter=\\n ${pkgs.pinch}/bin/pinch update "''${pinch_args[@]}" + + # Build + in_tmpdir hydrate ${config.system.build.nixos-rebuild}/bin/nixos-rebuild build + while read user;do + pushd / + hydrate /run/wrappers/bin/sudo -u "$user" \ + ${pkgs.nix}/bin/nix-build --no-out-link '' -A "$(userenv_query "$user" .package)" + popd + done < <( config_query '.userEnvironments | keys []' ) + sync + + # Install + hydrate ${config.system.build.nixos-rebuild}/bin/nixos-rebuild switch + sync + while read user;do + remove_arg=-r + if [[ "$(userenv_query "$user" .otherPackagesAction)" == keep ]];then + remove_arg= + fi + hydrate /run/wrappers/bin/sudo -u "$user" \ + ${pkgs.nix}/bin/nix-env -f '' $remove_arg -iA "$(userenv_query "$user" .package)" sync - while read user;do - remove_arg=-r - if [[ "$(userenv_query "$user" .otherPackagesAction)" == keep ]];then - remove_arg= - fi - hydrate /run/wrappers/bin/sudo -u "$user" \ - ${pkgs.nix}/bin/nix-env -f '' $remove_arg -iA "$(userenv_query "$user" .package)" - sync - done < <( config_query '.userEnvironments | keys []' ) - '' - } + done < <( config_query '.userEnvironments | keys []' ) + ''} ''; -in { +in +{ options = { system.autoUpgradeWithPinch = { @@ -252,7 +258,11 @@ in { }; upgradeConfigOwnershipPolicy = mkOption { - type = types.enum [ "root" "wheel" "any" ]; + type = types.enum [ + "root" + "wheel" + "any" + ]; default = "root"; description = '' Verify ownership of upgrade config files before using them for @@ -268,13 +278,20 @@ in { config = lib.mkIf cfg.enable { - security.sudo.extraRules = lib.mkAfter [{ - groups = [ "users" ]; - commands = [{ - command = "${auto-upgrade-script}"; - options = [ "NOPASSWD" "NOSETENV" ]; - }]; - }]; + security.sudo.extraRules = lib.mkAfter [ + { + groups = [ "users" ]; + commands = [ + { + command = "${auto-upgrade-script}"; + options = [ + "NOPASSWD" + "NOSETENV" + ]; + } + ]; + } + ]; # NOSETENV above still allows through ~17 vars, including PATH. Block those # as well: security.sudo.extraConfig = '' @@ -299,10 +316,13 @@ in { restartIfChanged = false; unitConfig.X-StopOnRemoval = false; serviceConfig.Type = "oneshot"; - environment = config.nix.envVars // { - inherit (config.environment.sessionVariables) NIX_PATH; - HOME = "/root"; - } // config.networking.proxy.envVars; + environment = + config.nix.envVars + // { + inherit (config.environment.sessionVariables) NIX_PATH; + HOME = "/root"; + } + // config.networking.proxy.envVars; path = with pkgs; [ config.nix.package.out diff --git a/overlays/pinch.nix b/overlays/pinch.nix index 9422fe9..d716720 100644 --- a/overlays/pinch.nix +++ b/overlays/pinch.nix @@ -1,39 +1,66 @@ self: super: let - fallback-git-cache = self.python3Packages.callPackage - ({ buildPythonPackage, fetchgit, setuptools, git, backoff, mypy, }: - buildPythonPackage rec { - pname = "git-cache"; - version = "1.5.0"; - src = fetchgit { - url = "https://git.scottworley.com/pub/git/git-cache"; - rev = "v${version}"; - hash = "sha256-g4TS/zX3e29Q3ThsCAX2wLLlYbi8fdux5uqAc+b/Oww="; - }; - pyproject = true; - build-system = [ setuptools ]; - propagatedBuildInputs = [ backoff ]; - nativeCheckInputs = [ git mypy ]; - doCheck = true; - checkPhase = "./test.sh"; - }) { }; + fallback-git-cache = self.python3Packages.callPackage ( + { + buildPythonPackage, + fetchgit, + setuptools, + git, + backoff, + mypy, + }: + buildPythonPackage rec { + pname = "git-cache"; + version = "1.5.0"; + src = fetchgit { + url = "https://git.scottworley.com/pub/git/git-cache"; + rev = "v${version}"; + hash = "sha256-g4TS/zX3e29Q3ThsCAX2wLLlYbi8fdux5uqAc+b/Oww="; + }; + pyproject = true; + build-system = [ setuptools ]; + propagatedBuildInputs = [ backoff ]; + nativeCheckInputs = [ + git + mypy + ]; + doCheck = true; + checkPhase = "./test.sh"; + } + ) { }; - fallback-pinch = self.python3Packages.callPackage - ({ buildPythonPackage, fetchgit, setuptools, nix, git, mypy, git-cache, }: - buildPythonPackage rec { - pname = "pinch"; - version = "3.3.2"; - src = fetchgit { - url = "https://git.scottworley.com/pub/git/pinch"; - rev = "v${version}"; - hash = "sha256-UB1hAEX7bD2TfdDv5EOWH1aaLluvzvpW80EjdCBuCCU="; - }; - pyproject = true; - build-system = [ setuptools ]; - propagatedBuildInputs = [ git-cache ]; - nativeCheckInputs = [ nix git mypy ]; - doCheck = true; - checkPhase = "./test.sh"; - }) { git-cache = self.python3Packages.git-cache or fallback-git-cache; }; + fallback-pinch = self.python3Packages.callPackage ( + { + buildPythonPackage, + fetchgit, + setuptools, + nix, + git, + mypy, + git-cache, + }: + buildPythonPackage rec { + pname = "pinch"; + version = "3.3.2"; + src = fetchgit { + url = "https://git.scottworley.com/pub/git/pinch"; + rev = "v${version}"; + hash = "sha256-UB1hAEX7bD2TfdDv5EOWH1aaLluvzvpW80EjdCBuCCU="; + }; + pyproject = true; + build-system = [ setuptools ]; + propagatedBuildInputs = [ git-cache ]; + nativeCheckInputs = [ + nix + git + mypy + ]; + doCheck = true; + checkPhase = "./test.sh"; + } + ) { git-cache = self.python3Packages.git-cache or fallback-git-cache; }; -in { pinch = super.pinch or fallback-pinch; } +in +{ + pinch = super.pinch or fallback-pinch; +} diff --git a/overlays/polite-merge.nix b/overlays/polite-merge.nix index da4db98..85e80c5 100644 --- a/overlays/polite-merge.nix +++ b/overlays/polite-merge.nix @@ -1,20 +1,26 @@ self: super: { - polite-merge = if builtins.hasAttr "polite-merge" super then - super.polite-merge - else - self.callPackage ({ fetchgit, git, stdenv, }: - stdenv.mkDerivation rec { - pname = "polite-merge"; - version = "2.4.2"; - src = fetchgit { - url = "https://git.scottworley.com/pub/git/polite-merge"; - rev = "v${version}"; - hash = "sha256-CUNKLCwIFwwVaA9opw9yql5AGej/ozQv8k1YR/cfV4I="; - }; - postUnpack = "patchShebangs ."; - nativeCheckInputs = [ git ]; - doCheck = true; - preInstall = "export prefix"; - }) { }; + polite-merge = + if builtins.hasAttr "polite-merge" super then + super.polite-merge + else + self.callPackage ( + { + fetchgit, + git, + stdenv, + }: + stdenv.mkDerivation rec { + pname = "polite-merge"; + version = "2.4.2"; + src = fetchgit { + url = "https://git.scottworley.com/pub/git/polite-merge"; + rev = "v${version}"; + hash = "sha256-CUNKLCwIFwwVaA9opw9yql5AGej/ozQv8k1YR/cfV4I="; + }; + postUnpack = "patchShebangs ."; + nativeCheckInputs = [ git ]; + doCheck = true; + preInstall = "export prefix"; + } + ) { }; } - diff --git a/pkgs/homeless-gpg.nix b/pkgs/homeless-gpg.nix index 221193f..a47eee7 100644 --- a/pkgs/homeless-gpg.nix +++ b/pkgs/homeless-gpg.nix @@ -1,4 +1,8 @@ -{ coreutils, gnupg, writeShellScript }: +{ + coreutils, + gnupg, + writeShellScript, +}: writeShellScript "homeless-gpg" '' set -eo pipefail diff --git a/pkgs/keyed-gpg.nix b/pkgs/keyed-gpg.nix index b675822..6fe1ed9 100644 --- a/pkgs/keyed-gpg.nix +++ b/pkgs/keyed-gpg.nix @@ -1,7 +1,13 @@ # Following the instructions at https://tribut.de/blog/git-commit-signatures-trusted-keys # Use with git with -c gpg.program='keyedgpg /path/to/keyfile.asc' -{ coreutils, gawk, homeless-gpg, lib, writeShellScript, }: +{ + coreutils, + gawk, + homeless-gpg, + lib, + writeShellScript, +}: keyfiles: writeShellScript "keyed-gpg" '' set -eo pipefail @@ -20,4 +26,3 @@ writeShellScript "keyed-gpg" '' ${homeless-gpg} --keyring="$keyring" "''${trusted_key_args[@]}" "$@" '' - diff --git a/upgrade-config.nix b/upgrade-config.nix index 67c4ec1..ba2bb5a 100644 --- a/upgrade-config.nix +++ b/upgrade-config.nix @@ -1,152 +1,176 @@ -{ upgradeConfig, lib ? (import { }).lib, }: +{ + upgradeConfig, + lib ? (import { }).lib, +}: with lib; evalModules { - modules = upgradeConfig ++ [{ - options = { + modules = upgradeConfig ++ [ + { + options = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Whether to periodically upgrade NixOS to the latest version. - Presumes that /etc/nixos is a git repo with a remote and - contains a pinch file called "channels". - ''; - }; + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to periodically upgrade NixOS to the latest version. + Presumes that /etc/nixos is a git repo with a remote and + contains a pinch file called "channels". + ''; + }; - dates = mkOption { - default = "04:40"; - type = types.str; - description = '' - Specification (in the format described by - systemd.time - 7) of the time at - which the update will occur. - ''; - }; + dates = mkOption { + default = "04:40"; + type = types.str; + description = '' + Specification (in the format described by + systemd.time + 7) of the time at + which the update will occur. + ''; + }; - repos = mkOption { - description = '' - Git repositories to pull before running pinch. These are maintained - as git checkouts at specified places in the filesystem with specified - ownership rather than kept read-only in the nix store so that humans - can use them both as points of intervention in the automation and to - author and push changes back up. - ''; - default = { }; - type = types.attrsOf (types.submodule { - options = { - url = mkOption { - description = "Remote git repo."; - type = types.str; - }; - remoteName = mkOption { - description = ''Name of the git remote. Customarily "origin".''; - type = types.str; - default = "origin"; - }; - onRemoteURLMismatch = mkOption { - description = '' - What to do if the remote URL in the git repo doesn't match the - URL configured here. - ''; - type = types.enum [ "update" "abort" ]; - default = "update"; - }; - onBranchMismatch = mkOption { - description = '' - What to do if a different branch is currently checked out. + repos = mkOption { + description = '' + Git repositories to pull before running pinch. These are maintained + as git checkouts at specified places in the filesystem with specified + ownership rather than kept read-only in the nix store so that humans + can use them both as points of intervention in the automation and to + author and push changes back up. + ''; + default = { }; + type = types.attrsOf ( + types.submodule { + options = { + url = mkOption { + description = "Remote git repo."; + type = types.str; + }; + remoteName = mkOption { + description = ''Name of the git remote. Customarily "origin".''; + type = types.str; + default = "origin"; + }; + onRemoteURLMismatch = mkOption { + description = '' + What to do if the remote URL in the git repo doesn't match the + URL configured here. + ''; + type = types.enum [ + "update" + "abort" + ]; + default = "update"; + }; + onBranchMismatch = mkOption { + description = '' + What to do if a different branch is currently checked out. - (Changes from remoteBranch are only ever - merged into localBranch, so if a different - branch is checked out, no remote changes will be merged.) - ''; - type = types.enum [ "continue" "abort" ]; - default = "continue"; - }; - user = mkOption { - description = "User as which to run 'git fetch'"; - type = types.str; - }; - localBranch = mkOption { - description = ""; - type = types.str; - default = "master"; + (Changes from remoteBranch are only ever + merged into localBranch, so if a different + branch is checked out, no remote changes will be merged.) + ''; + type = types.enum [ + "continue" + "abort" + ]; + default = "continue"; + }; + user = mkOption { + description = "User as which to run 'git fetch'"; + type = types.str; + }; + localBranch = mkOption { + description = ""; + type = types.str; + default = "master"; + }; + remoteBranch = mkOption { + type = types.str; + default = "master"; + }; + requireSignature = mkOption { + type = types.bool; + default = true; + description = '' + Only pull when the tip of the remote ref is signed by a key + specifed in signingKeys. + ''; + }; + }; + } + ); + example = { + "/etc/nixos" = { + url = "https://github.com/chkno/auto-upgrade-demo-nixos"; + user = "root"; + signingKeys = [ ./admins.asc ]; }; - remoteBranch = mkOption { - type = types.str; - default = "master"; + "/home/alice/.config/nixpkgs" = { + url = "https://github.com/chkno/auto-upgrade-demo-user-nixpkgs"; + user = "alice"; + signingKeys = [ + ./admins.asc + ./alice.asc + ]; }; - requireSignature = mkOption { - type = types.bool; - default = true; - description = '' - Only pull when the tip of the remote ref is signed by a key - specifed in signingKeys. - ''; - }; - }; - }); - example = { - "/etc/nixos" = { - url = "https://github.com/chkno/auto-upgrade-demo-nixos"; - user = "root"; - signingKeys = [ ./admins.asc ]; - }; - "/home/alice/.config/nixpkgs" = { - url = "https://github.com/chkno/auto-upgrade-demo-user-nixpkgs"; - user = "alice"; - signingKeys = [ ./admins.asc ./alice.asc ]; }; }; - }; - pinchFiles = mkOption { - description = '' - Pinch files to use for channel updates. Typically these are inside - repos' paths. - ''; - type = types.listOf types.path; - default = [ ]; - example = [ "/etc/nixos/channels" ]; - }; + pinchFiles = mkOption { + description = '' + Pinch files to use for channel updates. Typically these are inside + repos' paths. + ''; + type = types.listOf types.path; + default = [ ]; + example = [ "/etc/nixos/channels" ]; + }; - userEnvironments = mkOption { - description = '' - User environments to update as part of an upgrade run. - ''; - default = { }; - type = types.attrsOf (types.submodule { - options = { - package = mkOption { - type = types.str; - default = "userPackages"; - description = '' - The name of the single package that will be updated. You'll - want to create an 'entire user environment' package as shown in - https://nixos.wiki/wiki/FAQ#How_can_I_manage_software_with_nix-env_like_with_configuration.nix.3F - ''; - }; - otherPackagesAction = mkOption { - type = types.enum [ "remove" "keep" "abort" ]; - default = "remove"; - description = '' - What to do with packages other than package. + userEnvironments = mkOption { + description = '' + User environments to update as part of an upgrade run. + ''; + default = { }; + type = types.attrsOf ( + types.submodule { + options = { + package = mkOption { + type = types.str; + default = "userPackages"; + description = '' + The name of the single package that will be updated. You'll + want to create an 'entire user environment' package as shown in + https://nixos.wiki/wiki/FAQ#How_can_I_manage_software_with_nix-env_like_with_configuration.nix.3F + ''; + }; + otherPackagesAction = mkOption { + type = types.enum [ + "remove" + "keep" + "abort" + ]; + default = "remove"; + description = '' + What to do with packages other than package. - THIS DEFAULTS TO "remove", WHICH IS POTENTIALLY SOMEWHAT - DESTRUCTIVE! This is the default because it is the recommended - setting -- This module recommends managing your environment - through your one entire-environment package. - This keeps your environment declarative and ensures that all - packages receive regular updates. - ''; - # It seems like "upgrade" ought to be another choice here, powered - # by "nix-env --upgrade". But when I tried this, it didn't work. - }; + THIS DEFAULTS TO "remove", WHICH IS POTENTIALLY SOMEWHAT + DESTRUCTIVE! This is the default because it is the recommended + setting -- This module recommends managing your environment + through your one entire-environment package. + This keeps your environment declarative and ensures that all + packages receive regular updates. + ''; + # It seems like "upgrade" ought to be another choice here, powered + # by "nix-env --upgrade". But when I tried this, it didn't work. + }; + }; + } + ); + example = { + alice = { }; }; - }); - example = { alice = { }; }; + }; }; - }; - }]; + } + ]; }