From bf003999ed42d7168c9f3ffd3534fa1b51df4faf Mon Sep 17 00:00:00 2001 From: Anton Mosich Date: Tue, 2 Dec 2025 20:27:33 +0100 Subject: [PATCH 01/80] accounts.contacts: fix eval error You would encounter an eval error when a module (such as pimsync) would try to access an attribute of `accounts.contacts..local`, since it would default to `null`. The same problem was encountered in the `accounts.calendar` module, and fixed in 2c157e22dcf2c08e38a95b557ba6785e882b8bc0 which has the same solution. Closes #8258 Reported-by: redbeardymcgee --- modules/accounts/contacts.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/accounts/contacts.nix b/modules/accounts/contacts.nix index 780f6bb5..506b66a1 100644 --- a/modules/accounts/contacts.nix +++ b/modules/accounts/contacts.nix @@ -102,8 +102,8 @@ let }; local = mkOption { - type = types.nullOr (localModule name); - default = null; + type = localModule name; + default = { }; description = '' Local configuration for the contacts. ''; From c3d1e5c65ae564450bed700d546b88414ec32e43 Mon Sep 17 00:00:00 2001 From: Anton Mosich Date: Tue, 2 Dec 2025 20:47:02 +0100 Subject: [PATCH 02/80] pimsync: extend test to contacts If I had added such a test right away, I would have encountered #8258 myself. I mistakenly believed the contact and calendar modules to be the same. --- tests/modules/programs/pimsync/basic.nix | 16 ++++++++++++++++ tests/modules/programs/pimsync/basic.scfg | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/tests/modules/programs/pimsync/basic.nix b/tests/modules/programs/pimsync/basic.nix index 2b9b7434..2f9ae61e 100644 --- a/tests/modules/programs/pimsync/basic.nix +++ b/tests/modules/programs/pimsync/basic.nix @@ -22,6 +22,22 @@ basePath = ".local/state/calendar"; }; + accounts.contact = { + accounts.carddav = { + pimsync.enable = true; + remote = { + passwordCommand = [ + "pass" + "carddav" + ]; + type = "carddav"; + url = "https://carddav.example.com"; + userName = "bob"; + }; + }; + basePath = ".local/state/contact"; + }; + programs.pimsync = { enable = true; settings = [ diff --git a/tests/modules/programs/pimsync/basic.scfg b/tests/modules/programs/pimsync/basic.scfg index e0f6bb22..a035e788 100644 --- a/tests/modules/programs/pimsync/basic.scfg +++ b/tests/modules/programs/pimsync/basic.scfg @@ -28,4 +28,21 @@ pair calendar-http { storage_a http-local storage_b http-remote } +storage carddav-local { + fileext .vcf + path /home/hm-user/.local/state/contact/carddav + type vdir/vcard +} +storage carddav-remote { + type carddav + url https://carddav.example.com + username bob + password { + cmd pass carddav + } +} +pair contacts-carddav { + storage_a carddav-local + storage_b carddav-remote +} status_path /test/dir From ab8e4b2b5ab14df0fce5510092b6497194b7b247 Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Tue, 2 Dec 2025 04:33:03 +0000 Subject: [PATCH 03/80] home-environment: extend release check to include pkgs `lib` comes from the Nixpkgs used to instantiate Home Manager itself and cannot change within the module fixpoint. However, `pkgs` is configurable (via `nixpkgs.*` or `_module.args`) and may come from a different Nixpkgs instance from the one providing `lib`. Mismatches between Home Manager's release and the release of the `pkgs` instance are more common and also more likely to cause subtle issues. This change extends the release check to include `pkgs.lib.trivial.release` so that such mismatches can be detected and reported. --- modules/home-environment.nix | 21 +++++++--- tests/modules/home-environment/default.nix | 1 + .../nixpkgs-release-check-pkgs.nix | 39 +++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 tests/modules/home-environment/nixpkgs-release-check-pkgs.nix diff --git a/modules/home-environment.nix b/modules/home-environment.nix index a562047b..60238360 100644 --- a/modules/home-environment.nix +++ b/modules/home-environment.nix @@ -570,14 +570,25 @@ in warnings = let hmRelease = config.home.version.release; - nixpkgsRelease = lib.trivial.release; - releaseMismatch = config.home.enableNixpkgsReleaseCheck && hmRelease != nixpkgsRelease; + libRelease = lib.trivial.release; + pkgsRelease = pkgs.lib.trivial.release; + releaseMismatch = hmRelease != libRelease || hmRelease != pkgsRelease; + + versionsSummary = + if libRelease == pkgsRelease then + '' + Home Manager version ${hmRelease} and + Nixpkgs version ${libRelease}.'' + else + '' + Home Manager version: ${hmRelease} + Nixpkgs version used to evaluate Home Manager: ${libRelease} + Nixpkgs version used for packages (`pkgs`): ${pkgsRelease}''; in - lib.optional releaseMismatch '' + lib.optional (config.home.enableNixpkgsReleaseCheck && releaseMismatch) '' You are using - Home Manager version ${hmRelease} and - Nixpkgs version ${nixpkgsRelease}. + ${lib.replaceString "\n" "\n " versionsSummary} Using mismatched versions is likely to cause errors and unexpected behavior. It is therefore highly recommended to use a release of Home diff --git a/tests/modules/home-environment/default.nix b/tests/modules/home-environment/default.nix index c38ad5ce..778982a3 100644 --- a/tests/modules/home-environment/default.nix +++ b/tests/modules/home-environment/default.nix @@ -2,4 +2,5 @@ home-session-path = ./session-path.nix; home-session-search-variables = ./session-search-variables.nix; home-session-variables = ./session-variables.nix; + home-nixpkgs-release-check-pkgs = ./nixpkgs-release-check-pkgs.nix; } diff --git a/tests/modules/home-environment/nixpkgs-release-check-pkgs.nix b/tests/modules/home-environment/nixpkgs-release-check-pkgs.nix new file mode 100644 index 00000000..d0ad01c5 --- /dev/null +++ b/tests/modules/home-environment/nixpkgs-release-check-pkgs.nix @@ -0,0 +1,39 @@ +{ lib, ... }: +let + releaseInfo = lib.importJSON ../../../release.json; + hmRelease = releaseInfo.release; + pkgsRelease = ""; +in +{ + test.asserts.warnings.expected = [ + '' + You are using + + Home Manager version: ${hmRelease} + Nixpkgs version used to evaluate Home Manager: ${hmRelease} + Nixpkgs version used for packages (`pkgs`): ${pkgsRelease} + + Using mismatched versions is likely to cause errors and unexpected + behavior. It is therefore highly recommended to use a release of Home + Manager that corresponds with your chosen release of Nixpkgs. + + If you insist then you can disable this warning by adding + + home.enableNixpkgsReleaseCheck = false; + + to your configuration. + '' + ]; + + nixpkgs.overlays = [ + (final: prev: { + lib = prev.lib.extend ( + final: prev: { + trivial = prev.trivial // { + release = pkgsRelease; + }; + } + ); + }) + ]; +} From 06f81463bb5468cff3a1b053377633546f3a096d Mon Sep 17 00:00:00 2001 From: Mirko Lenz Date: Mon, 24 Nov 2025 08:16:29 +0100 Subject: [PATCH 04/80] infat: add module --- .../misc/news/2025/11/2025-11-27_08-22-14.nix | 10 +++ modules/programs/infat.nix | 81 +++++++++++++++++++ tests/modules/programs/infat/default.nix | 6 ++ .../programs/infat/example-settings.nix | 39 +++++++++ tests/modules/programs/infat/no-settings.nix | 13 +++ 5 files changed, 149 insertions(+) create mode 100644 modules/misc/news/2025/11/2025-11-27_08-22-14.nix create mode 100644 modules/programs/infat.nix create mode 100644 tests/modules/programs/infat/default.nix create mode 100644 tests/modules/programs/infat/example-settings.nix create mode 100644 tests/modules/programs/infat/no-settings.nix diff --git a/modules/misc/news/2025/11/2025-11-27_08-22-14.nix b/modules/misc/news/2025/11/2025-11-27_08-22-14.nix new file mode 100644 index 00000000..8a10b642 --- /dev/null +++ b/modules/misc/news/2025/11/2025-11-27_08-22-14.nix @@ -0,0 +1,10 @@ +{ pkgs, ... }: +{ + time = "2025-11-27T07:22:14+00:00"; + condition = pkgs.stdenv.hostPlatform.isDarwin; + message = '' + A new module is available: 'programs.infat'. + Infat is a command line tool to set default openers + for file formats and url schemes on macOS. + ''; +} diff --git a/modules/programs/infat.nix b/modules/programs/infat.nix new file mode 100644 index 00000000..8ec05a67 --- /dev/null +++ b/modules/programs/infat.nix @@ -0,0 +1,81 @@ +{ + lib, + config, + pkgs, + ... +}: +let + cfg = config.programs.infat; + tomlFormat = pkgs.formats.toml { }; + + configDir = + if config.xdg.enable then + config.xdg.configHome + else + "${config.home.homeDirectory}/Library/Application Support"; + + configFile = "${configDir}/infat/config.toml"; +in +{ + meta.maintainers = with lib.maintainers; [ + mirkolenz + ]; + + options = { + programs.infat = { + enable = lib.mkEnableOption "infat"; + package = lib.mkPackageOption pkgs "infat" { nullable = true; }; + settings = lib.mkOption { + type = tomlFormat.type; + default = { }; + example = lib.literalExpression '' + { + extensions = { + md = "TextEdit"; + html = "Safari"; + pdf = "Preview"; + }; + schemes = { + mailto = "Mail"; + web = "Safari"; + }; + types = { + plain-text = "VSCode"; + }; + } + ''; + description = '' + Configuration written to + {file}`$XDG_CONFIG_HOME/infat/config.toml`. + ''; + }; + autoActivate = lib.mkEnableOption "auto-activate infat" // { + default = true; + example = false; + description = '' + Automatically activate infat on startup. + This is useful if you want to use infat as a + default application handler for certain file types. + If you don't want this, set this to false. + This option is only effective if `settings` is set. + ''; + }; + }; + }; + config = lib.mkIf cfg.enable { + assertions = [ + (lib.hm.assertions.assertPlatform "programs.infat" pkgs lib.platforms.darwin) + ]; + home = { + packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + file.${configFile} = lib.mkIf (cfg.settings != { }) { + source = tomlFormat.generate "infat-settings.toml" cfg.settings; + }; + activation = lib.mkIf (cfg.settings != { } && cfg.package != null && cfg.autoActivate) { + infat = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + run ${lib.getExe cfg.package} --config "${configFile}" $VERBOSE_ARG + ''; + }; + }; + }; +} diff --git a/tests/modules/programs/infat/default.nix b/tests/modules/programs/infat/default.nix new file mode 100644 index 00000000..8fdd5ecc --- /dev/null +++ b/tests/modules/programs/infat/default.nix @@ -0,0 +1,6 @@ +{ lib, pkgs, ... }: + +lib.optionalAttrs pkgs.stdenv.hostPlatform.isDarwin { + infat-example-settings = ./example-settings.nix; + infat-no-settings = ./no-settings.nix; +} diff --git a/tests/modules/programs/infat/example-settings.nix b/tests/modules/programs/infat/example-settings.nix new file mode 100644 index 00000000..a9980d78 --- /dev/null +++ b/tests/modules/programs/infat/example-settings.nix @@ -0,0 +1,39 @@ +{ pkgs, ... }: + +{ + programs.infat = { + enable = true; + settings = { + extensions = { + md = "TextEdit"; + }; + schemes = { + web = "Safari"; + }; + types = { + plain-text = "VSCode"; + }; + }; + }; + + test.stubs.infat = { }; + + nmt.script = + let + expectedConfigPath = "home-files/.config/infat/config.toml"; + expectedConfigContent = pkgs.writeText "infat.config.expected" '' + [extensions] + md = "TextEdit" + + [schemes] + web = "Safari" + + [types] + plain-text = "VSCode" + ''; + in + '' + assertFileExists "${expectedConfigPath}" + assertFileContent "${expectedConfigPath}" "${expectedConfigContent}" + ''; +} diff --git a/tests/modules/programs/infat/no-settings.nix b/tests/modules/programs/infat/no-settings.nix new file mode 100644 index 00000000..fdb970a4 --- /dev/null +++ b/tests/modules/programs/infat/no-settings.nix @@ -0,0 +1,13 @@ +{ + programs.infat.enable = true; + + test.stubs.infat = { }; + + nmt.script = + let + expectedConfigPath = "home-files/.config/infat/config.toml"; + in + '' + assertPathNotExists "${expectedConfigPath}" + ''; +} From db44f38047af9073520502850b29d02da69c4a0a Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Sat, 29 Nov 2025 09:25:51 -0300 Subject: [PATCH 05/80] ludusavi: add test --- tests/darwinScrublist.nix | 1 + tests/modules/services/ludusavi/config.yaml | 9 ++++++++ tests/modules/services/ludusavi/default.nix | 1 + tests/modules/services/ludusavi/settings.nix | 23 ++++++++++++++++++++ 4 files changed, 34 insertions(+) create mode 100644 tests/modules/services/ludusavi/config.yaml create mode 100644 tests/modules/services/ludusavi/default.nix create mode 100644 tests/modules/services/ludusavi/settings.nix diff --git a/tests/darwinScrublist.nix b/tests/darwinScrublist.nix index 128609ae..3979629b 100644 --- a/tests/darwinScrublist.nix +++ b/tests/darwinScrublist.nix @@ -96,6 +96,7 @@ let "lf" "lieer" "lsd" + "ludusavi" "mbsync" "meli" "mergiraf" diff --git a/tests/modules/services/ludusavi/config.yaml b/tests/modules/services/ludusavi/config.yaml new file mode 100644 index 00000000..ede8d110 --- /dev/null +++ b/tests/modules/services/ludusavi/config.yaml @@ -0,0 +1,9 @@ +backup: + path: ~/.local/state/backups/ludusavi +language: en-US +restore: + path: ~/.local/state/backups/ludusavi +roots: +- path: ~/.local/share/Steam + store: steam +theme: light diff --git a/tests/modules/services/ludusavi/default.nix b/tests/modules/services/ludusavi/default.nix new file mode 100644 index 00000000..2efb726c --- /dev/null +++ b/tests/modules/services/ludusavi/default.nix @@ -0,0 +1 @@ +{ ludusavi-settings = ./settings.nix; } diff --git a/tests/modules/services/ludusavi/settings.nix b/tests/modules/services/ludusavi/settings.nix new file mode 100644 index 00000000..a99975e1 --- /dev/null +++ b/tests/modules/services/ludusavi/settings.nix @@ -0,0 +1,23 @@ +{ + services.ludusavi = { + enable = true; + settings = { + language = "en-US"; + theme = "light"; + roots = [ + { + path = "~/.local/share/Steam"; + store = "steam"; + } + ]; + backup.path = "~/.local/state/backups/ludusavi"; + restore.path = "~/.local/state/backups/ludusavi"; + }; + }; + + nmt.script = '' + assertFileExists home-files/.config/ludusavi/config.yaml + assertFileContent home-files/.config/ludusavi/config.yaml \ + ${./config.yaml} + ''; +} From eca5f967cd56d7287e29a57f1c8c119e64b73e5d Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Sat, 29 Nov 2025 09:29:38 -0300 Subject: [PATCH 06/80] ludusavi: use config.xdg.stateHome instead of $XDG_STATE_HOME --- modules/services/ludusavi.nix | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/modules/services/ludusavi.nix b/modules/services/ludusavi.nix index c301b82b..ab5602dc 100644 --- a/modules/services/ludusavi.nix +++ b/modules/services/ludusavi.nix @@ -49,9 +49,17 @@ in default = { manifest.url = "https://raw.githubusercontent.com/mtkennerly/ludusavi-manifest/master/data/manifest.yaml"; roots = [ ]; - backup.path = "$XDG_STATE_HOME/backups/ludusavi"; - restore.path = "$XDG_STATE_HOME/backups/ludusavi"; + backup.path = "${config.xdg.stateHome}/backups/ludusavi"; + restore.path = "${config.xdg.stateHome}/backups/ludusavi"; }; + defaultText = '' + { + manifest.url = "https://raw.githubusercontent.com/mtkennerly/ludusavi-manifest/master/data/manifest.yaml"; + roots = [ ]; + backup.path = "$XDG_STATE_HOME/backups/ludusavi"; + restore.path = "$XDG_STATE_HOME/backups/ludusavi"; + } + ''; example = { language = "en-US"; theme = "light"; @@ -94,6 +102,24 @@ in Service = { Type = "oneshot"; ExecStart = "${lib.getExe cfg.package} backup --force"; + ExecStartPre = "${pkgs.writeShellScript "ludusavi-migrate-backup" '' + old_base_dir="${config.home.homeDirectory}/\$XDG_STATE_HOME" + old_dir="$old_base_dir/backups/ludusavi" + new_base_dir="${config.xdg.stateHome}/backups" + new_dir="$new_base_dir/ludusavi" + + if [[ -d "$old_base_dir" ]]; then + echo "Migrating old Ludusavi's backup... (See home-manager/#8234)" + if [[ ! -d "$new_base_dir" ]]; then + mkdir -p "$new_base_dir" + fi + + mv "$old_dir" "$new_dir" + rmdir "$old_base_dir/backups" + rmdir "$old_base_dir" + echo "Migration completed successfully." + fi + ''}"; } // lib.optionalAttrs cfg.backupNotification { ExecStartPost = "${lib.getExe pkgs.libnotify} 'Ludusavi' 'Backup completed' -i com.mtkennerly.ludusavi -a 'Ludusavi'"; From 93d907a205ca7a4cd524a3c5f9e926904c7485bc Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Mon, 1 Dec 2025 09:47:31 -0300 Subject: [PATCH 07/80] news: ludusavi's default backup path changed entry --- .../misc/news/2025/12/2025-12-01_09-35-38.nix | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 modules/misc/news/2025/12/2025-12-01_09-35-38.nix diff --git a/modules/misc/news/2025/12/2025-12-01_09-35-38.nix b/modules/misc/news/2025/12/2025-12-01_09-35-38.nix new file mode 100644 index 00000000..40899855 --- /dev/null +++ b/modules/misc/news/2025/12/2025-12-01_09-35-38.nix @@ -0,0 +1,16 @@ +{ config, ... }: + +{ + time = "2025-12-01T12:35:38+00:00"; + condition = config.services.ludusavi.enable; + message = '' + BREAKING CHANGE: + + The `ludusavi` module has changed its default backup and restore path. + The new module implements a mechanism to automatically migrate the backups + to the new path, but if it doesn't work and you can't find your backups in + `ludusavi`, they should be in the old path: ~/\$XDG_STATE_HOME/backups/ludusavi/ + (that means a directory literally called $XDG_STATE_HOME in your home, rather than + the env var expanded). For more info, see pull #8234. + ''; +} From 281e9398cc94cad3e5fc4206fefb83e7bb166476 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 17 Nov 2025 22:04:08 +0100 Subject: [PATCH 08/80] swayidle: improve confusing "events" configuration With swayidle one can configure two different kinds of hooks: - Idle timeouts are executed after the session has been idle for a specific amount of time. - Events are executed when systemd notifies us that for example the user session is locked or that the device is about to suspend. While not obvious, there is a significant difference between how these two kinds are configured: there can be several timeouts with separate commands to be executed, but each event can only be specified once. If an event is specified multiple times, then the last command wins. This can be very easy to miss in swayidle's documentation. Furthermore, because the config is a list of `{ event = "..."; command = "..."; }` attrset, we double down on this confusion and make it seem like having multiple handlers for an event was actually supported. Fix this by converting from a list of "event" submodules to an attrset where the key is the event name and the value is the command to be executed. This makes it impossible to specify multiple commands for a single event by accident. If a user _does_ want to have multiple commands executed on any event they can for example use `pkgs.writeShellScript` and manually chain the commands in that script. --- modules/services/swayidle.nix | 73 +++++++++++++------ .../services/swayidle/basic-configuration.nix | 14 +--- tests/modules/services/swayidle/default.nix | 1 + .../swayidle/legacy-configuration.nix | 51 +++++++++++++ 4 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 tests/modules/services/swayidle/legacy-configuration.nix diff --git a/modules/services/swayidle.nix b/modules/services/swayidle.nix index 481dd440..5af0b467 100644 --- a/modules/services/swayidle.nix +++ b/modules/services/swayidle.nix @@ -42,21 +42,30 @@ in }; }; - eventModule = { + eventsModule = { options = { - event = mkOption { - type = types.enum [ - "before-sleep" - "after-resume" - "lock" - "unlock" - ]; - description = "Event name."; + before-sleep = mkOption { + type = types.nullOr types.str; + default = null; + description = "Command to run before suspending."; }; - command = mkOption { - type = types.str; - description = "Command to run when event occurs."; + after-resume = mkOption { + type = types.nullOr types.str; + default = null; + description = "Command to run after resuming."; + }; + + lock = mkOption { + type = types.nullOr types.str; + default = null; + description = "Command to run when the logind session is locked."; + }; + + unlock = mkOption { + type = types.nullOr types.str; + default = null; + description = "Command to run when the logind session is unlocked."; }; }; }; @@ -80,13 +89,31 @@ in }; events = mkOption { - type = with types; listOf (submodule eventModule); + type = + with types; + (coercedTo (listOf attrs)) ( + events: + lib.warn + '' + The syntax of services.swayidle.events has changed. While it + previously accepted a list of events, it now accepts an attrset + keyed by the event name. + '' + ( + lib.listToAttrs ( + map (e: { + name = e.event; + value = e.command; + }) events + ) + ) + ) (submodule eventsModule); default = [ ]; example = literalExpression '' - [ - { event = "before-sleep"; command = "''${pkgs.swaylock}/bin/swaylock -fF"; } - { event = "lock"; command = "lock"; } - ] + { + "before-sleep" = "''${pkgs.swaylock}/bin/swaylock -fF"; + "lock" = "lock"; + } ''; description = "Run command on occurrence of a event."; }; @@ -144,13 +171,17 @@ in t.resumeCommand ]; - mkEvent = e: [ - e.event - e.command + mkEvent = event: command: [ + event + command ]; + nonemptyEvents = lib.filterAttrs (event: command: command != null) cfg.events; + args = - cfg.extraArgs ++ (lib.concatMap mkTimeout cfg.timeouts) ++ (lib.concatMap mkEvent cfg.events); + cfg.extraArgs + ++ (lib.concatMap mkTimeout cfg.timeouts) + ++ (lib.flatten (lib.mapAttrsToList mkEvent nonemptyEvents)); in "${lib.getExe cfg.package} ${lib.escapeShellArgs args}"; }; diff --git a/tests/modules/services/swayidle/basic-configuration.nix b/tests/modules/services/swayidle/basic-configuration.nix index b5534c75..5da059b1 100644 --- a/tests/modules/services/swayidle/basic-configuration.nix +++ b/tests/modules/services/swayidle/basic-configuration.nix @@ -19,16 +19,10 @@ resumeCommand = ''swaymsg "output * dpms on"''; } ]; - events = [ - { - event = "before-sleep"; - command = "swaylock -fF"; - } - { - event = "lock"; - command = "swaylock -fF"; - } - ]; + events = { + before-sleep = "swaylock -fF"; + lock = "swaylock -fF"; + }; }; nmt.script = '' diff --git a/tests/modules/services/swayidle/default.nix b/tests/modules/services/swayidle/default.nix index 62db035b..75c88d28 100644 --- a/tests/modules/services/swayidle/default.nix +++ b/tests/modules/services/swayidle/default.nix @@ -2,4 +2,5 @@ lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { swayidle-basic-configuration = ./basic-configuration.nix; + swayidle-legacy-configuration = ./legacy-configuration.nix; } diff --git a/tests/modules/services/swayidle/legacy-configuration.nix b/tests/modules/services/swayidle/legacy-configuration.nix new file mode 100644 index 00000000..e07834fa --- /dev/null +++ b/tests/modules/services/swayidle/legacy-configuration.nix @@ -0,0 +1,51 @@ +{ config, ... }: +{ + services.swayidle = { + enable = true; + package = config.lib.test.mkStubPackage { outPath = "@swayidle@"; }; + events = [ + { + event = "lock"; + command = "swaylock -fF"; + } + { + event = "before-sleep"; + command = "swaylock -fF"; + } + ]; + }; + + test.asserts.evalWarnings.expected = [ + '' + The syntax of services.swayidle.events has changed. While it + previously accepted a list of events, it now accepts an attrset + keyed by the event name. + '' + ]; + + nmt.script = '' + serviceFile=home-files/.config/systemd/user/swayidle.service + + assertFileExists "$serviceFile" + + serviceFileNormalized="$(normalizeStorePaths "$serviceFile")" + + assertFileContent "$serviceFileNormalized" ${builtins.toFile "expected.service" '' + [Install] + WantedBy=graphical-session.target + + [Service] + Environment=PATH=@bash-interactive@/bin + ExecStart=@swayidle@/bin/dummy -w before-sleep 'swaylock -fF' lock 'swaylock -fF' + Restart=always + Type=simple + + [Unit] + After=graphical-session.target + ConditionEnvironment=WAYLAND_DISPLAY + Description=Idle manager for Wayland + Documentation=man:swayidle(1) + PartOf=graphical-session.target + ''} + ''; +} From bcc7afa1d873ead5932fbb4d42f1916a9627c084 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 04:17:25 +0000 Subject: [PATCH 09/80] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/2fad6eac6077f03fe109c4d4eb171cf96791faa4?narHash=sha256-sKoIWfnijJ0%2B9e4wRvIgm/HgE27bzwQxcEmo2J/gNpI%3D' (2025-11-27) → 'github:NixOS/nixpkgs/2d293cbfa5a793b4c50d17c05ef9e385b90edf6c?narHash=sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4%3D' (2025-11-30) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 84ba9850..a7457cc9 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1764242076, - "narHash": "sha256-sKoIWfnijJ0+9e4wRvIgm/HgE27bzwQxcEmo2J/gNpI=", + "lastModified": 1764517877, + "narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2fad6eac6077f03fe109c4d4eb171cf96791faa4", + "rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c", "type": "github" }, "original": { From 43173abcb4a48adb1d56be30ed17d9afaf4310ae Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Wed, 3 Dec 2025 04:02:13 +0000 Subject: [PATCH 10/80] pimsync: Make storage names unique This includes the calendar/contacts prefix in the storage name as well as the pair name to ensure that if the same name is used for contacts and calendar then it is correctly referenced. --- modules/programs/pimsync/default.nix | 8 ++-- tests/modules/programs/pimsync/basic.nix | 4 +- tests/modules/programs/pimsync/basic.scfg | 46 +++++++++++------------ 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/modules/programs/pimsync/default.nix b/modules/programs/pimsync/default.nix index b8edb7df..bf772c8f 100644 --- a/modules/programs/pimsync/default.nix +++ b/modules/programs/pimsync/default.nix @@ -51,7 +51,7 @@ localStorage = calendar: name: acc: { name = "storage"; - params = [ "${name}-local" ]; + params = [ "${if calendar then "calendar" else "contacts"}-${name}-local" ]; children = (attrsToDirectives { inherit (acc.local) path; @@ -63,7 +63,7 @@ remoteStorage = calendar: name: acc: { name = "storage"; - params = [ "${name}-remote" ]; + params = [ "${if calendar then "calendar" else "contacts"}-${name}-remote" ]; children = (attrsToDirectives { inherit (acc.remote) url; @@ -91,8 +91,8 @@ params = lib.singleton "${if calendar then "calendar" else "contacts"}-${name}"; children = (attrsToDirectives { - storage_a = "${name}-local"; - storage_b = "${name}-remote"; + storage_a = "${if calendar then "calendar" else "contacts"}-${name}-local"; + storage_b = "${if calendar then "calendar" else "contacts"}-${name}-remote"; }) ++ acc.pimsync.extraPairDirectives; }; diff --git a/tests/modules/programs/pimsync/basic.nix b/tests/modules/programs/pimsync/basic.nix index 2f9ae61e..f029ea1f 100644 --- a/tests/modules/programs/pimsync/basic.nix +++ b/tests/modules/programs/pimsync/basic.nix @@ -1,6 +1,6 @@ { accounts.calendar = { - accounts.caldav = { + accounts.mine = { pimsync.enable = true; remote = { passwordCommand = [ @@ -23,7 +23,7 @@ }; accounts.contact = { - accounts.carddav = { + accounts.mine = { pimsync.enable = true; remote = { passwordCommand = [ diff --git a/tests/modules/programs/pimsync/basic.scfg b/tests/modules/programs/pimsync/basic.scfg index a035e788..6d09cbb3 100644 --- a/tests/modules/programs/pimsync/basic.scfg +++ b/tests/modules/programs/pimsync/basic.scfg @@ -1,14 +1,18 @@ -storage caldav-local { - fileext .ics - path /home/hm-user/.local/state/calendar/caldav - type vdir/icalendar -} -storage http-local { +storage calendar-http-local { fileext .ics path /home/hm-user/.local/state/calendar/http type vdir/icalendar } -storage caldav-remote { +storage calendar-mine-local { + fileext .ics + path /home/hm-user/.local/state/calendar/mine + type vdir/icalendar +} +storage calendar-http-remote { + type webcal + url https://example.com/calendar +} +storage calendar-mine-remote { type caldav url https://caldav.example.com username alice @@ -16,24 +20,20 @@ storage caldav-remote { cmd pass caldav } } -storage http-remote { - type webcal - url https://example.com/calendar -} -pair calendar-caldav { - storage_a caldav-local - storage_b caldav-remote -} pair calendar-http { - storage_a http-local - storage_b http-remote + storage_a calendar-http-local + storage_b calendar-http-remote } -storage carddav-local { +pair calendar-mine { + storage_a calendar-mine-local + storage_b calendar-mine-remote +} +storage contacts-mine-local { fileext .vcf - path /home/hm-user/.local/state/contact/carddav + path /home/hm-user/.local/state/contact/mine type vdir/vcard } -storage carddav-remote { +storage contacts-mine-remote { type carddav url https://carddav.example.com username bob @@ -41,8 +41,8 @@ storage carddav-remote { cmd pass carddav } } -pair contacts-carddav { - storage_a carddav-local - storage_b carddav-remote +pair contacts-mine { + storage_a contacts-mine-local + storage_b contacts-mine-remote } status_path /test/dir From 28741978a3840a9769f2b3160e0b423a79cb8f42 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 3 Dec 2025 14:25:11 +0000 Subject: [PATCH 11/80] programs/ssh: use 'toKeyValue' for 'extraOptions' A first step towards refactoring the module. Unfortunately the config is not consistent in using comma-separated values or repeated keys for lists, since the user can always add commas by themselves, we should default to repeated keys in the generator. --- modules/programs/ssh.nix | 8 +++++++- .../forwards-dynamic-valid-bind-no-asserts-expected.conf | 2 ++ .../modules/programs/ssh/match-blocks-attrs-expected.conf | 4 ++++ .../ssh/match-blocks-match-and-hosts-expected.conf | 3 +++ tests/modules/programs/ssh/old-defaults-expected.conf | 1 + .../programs/ssh/old-defaults-extra-config-expected.conf | 1 + tests/modules/programs/ssh/renamed-options-expected.conf | 1 + 7 files changed, 19 insertions(+), 1 deletion(-) diff --git a/modules/programs/ssh.nix b/modules/programs/ssh.nix index f73da437..ed7185df 100644 --- a/modules/programs/ssh.nix +++ b/modules/programs/ssh.nix @@ -451,7 +451,13 @@ let ++ map (f: " LocalForward" + addressPort f.bind + addressPort f.host) cf.localForwards ++ map (f: " RemoteForward" + addressPort f.bind + addressPort f.host) cf.remoteForwards ++ map (f: " DynamicForward" + addressPort f) cf.dynamicForwards - ++ mapAttrsToList (n: v: " ${n} ${v}") cf.extraOptions + ++ [ + (lib.generators.toKeyValue { + mkKeyValue = lib.generators.mkKeyValueDefault { } " "; + listsAsDuplicateKeys = true; + indent = " "; + } cf.extraOptions) + ] ); in diff --git a/tests/modules/programs/ssh/forwards-dynamic-valid-bind-no-asserts-expected.conf b/tests/modules/programs/ssh/forwards-dynamic-valid-bind-no-asserts-expected.conf index 1c4c4f86..fc7e4acf 100644 --- a/tests/modules/programs/ssh/forwards-dynamic-valid-bind-no-asserts-expected.conf +++ b/tests/modules/programs/ssh/forwards-dynamic-valid-bind-no-asserts-expected.conf @@ -1,7 +1,9 @@ Host dynamicBindAddressWithPort DynamicForward [127.0.0.1]:3000 + Host dynamicBindPathNoPort DynamicForward /run/user/1000/gnupg/S.gpg-agent.extra + diff --git a/tests/modules/programs/ssh/match-blocks-attrs-expected.conf b/tests/modules/programs/ssh/match-blocks-attrs-expected.conf index edff6264..7e488eb8 100644 --- a/tests/modules/programs/ssh/match-blocks-attrs-expected.conf +++ b/tests/modules/programs/ssh/match-blocks-attrs-expected.conf @@ -2,8 +2,10 @@ Host * !github.com Port 516 IdentityFile file1 IdentityFile file2 + Host abc ProxyJump jump-host + Host xyz SetEnv BAR="_bar_ 42" FOO="foo12" ServerAliveInterval 60 @@ -13,8 +15,10 @@ Host xyz RemoteForward [localhost]:8081 [10.0.0.2]:80 RemoteForward /run/user/1000/gnupg/S.gpg-agent.extra /run/user/1000/gnupg/S.gpg-agent DynamicForward [localhost]:2839 + Host ordered Port 1 + diff --git a/tests/modules/programs/ssh/match-blocks-match-and-hosts-expected.conf b/tests/modules/programs/ssh/match-blocks-match-and-hosts-expected.conf index b5f3dad8..6fb60b1e 100644 --- a/tests/modules/programs/ssh/match-blocks-match-and-hosts-expected.conf +++ b/tests/modules/programs/ssh/match-blocks-match-and-hosts-expected.conf @@ -1,9 +1,12 @@ Host * !github.com Port 516 + Host abc Port 2222 + Match host xyz canonical Port 2223 + diff --git a/tests/modules/programs/ssh/old-defaults-expected.conf b/tests/modules/programs/ssh/old-defaults-expected.conf index 65290800..75a34ce4 100644 --- a/tests/modules/programs/ssh/old-defaults-expected.conf +++ b/tests/modules/programs/ssh/old-defaults-expected.conf @@ -11,4 +11,5 @@ Host * ControlMaster no ControlPath ~/.ssh/master-%r@%n:%p ControlPersist no + diff --git a/tests/modules/programs/ssh/old-defaults-extra-config-expected.conf b/tests/modules/programs/ssh/old-defaults-extra-config-expected.conf index e14c44e8..46f398dd 100644 --- a/tests/modules/programs/ssh/old-defaults-extra-config-expected.conf +++ b/tests/modules/programs/ssh/old-defaults-extra-config-expected.conf @@ -11,6 +11,7 @@ Host * ControlMaster no ControlPath ~/.ssh/master-%r@%n:%p ControlPersist no + MyExtraOption no AnotherOption 3 diff --git a/tests/modules/programs/ssh/renamed-options-expected.conf b/tests/modules/programs/ssh/renamed-options-expected.conf index e586953c..f01bc02e 100644 --- a/tests/modules/programs/ssh/renamed-options-expected.conf +++ b/tests/modules/programs/ssh/renamed-options-expected.conf @@ -11,4 +11,5 @@ Host * ControlMaster yes ControlPath ~/.ssh/myfile-%r@%n:%p ControlPersist 10m + From 12e7786854b82323471f1aa59bc5ddd4693a20eb Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 3 Dec 2025 14:39:03 +0000 Subject: [PATCH 12/80] programs/ssh: use 'toList' --- modules/programs/ssh.nix | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/modules/programs/ssh.nix b/modules/programs/ssh.nix index ed7185df..351a42b0 100644 --- a/modules/programs/ssh.nix +++ b/modules/programs/ssh.nix @@ -151,14 +151,7 @@ let identityFile = mkOption { type = with types; either (listOf str) (nullOr str); default = [ ]; - apply = - p: - if p == null then - [ ] - else if lib.isString p then - [ p ] - else - p; + apply = p: if p == null then [ ] else lib.toList p; description = '' Specifies files from which the user identity is read. Identities will be tried in the given order. @@ -168,14 +161,7 @@ let identityAgent = mkOption { type = with types; either (listOf str) (nullOr str); default = [ ]; - apply = - p: - if p == null then - [ ] - else if lib.isString p then - [ p ] - else - p; + apply = p: if p == null then [ ] else lib.toList p; description = '' Specifies the location of the ssh identity agent. ''; @@ -265,14 +251,7 @@ let certificateFile = mkOption { type = with types; either (listOf str) (nullOr str); default = [ ]; - apply = - p: - if p == null then - [ ] - else if lib.isString p then - [ p ] - else - p; + apply = p: if p == null then [ ] else lib.toList p; description = '' Specifies files from which the user certificate is read. ''; From af324afa72de6d23162f0cdcb0716afe6e626683 Mon Sep 17 00:00:00 2001 From: trash-panda-v91-beta <42897550+trash-panda-v91-beta@users.noreply.github.com> Date: Wed, 3 Dec 2025 14:33:37 +0100 Subject: [PATCH 13/80] cargo: fix typo in tests testing docker-cli instead of cargo in empty-config.nix --- tests/modules/programs/cargo/empty-config.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/modules/programs/cargo/empty-config.nix b/tests/modules/programs/cargo/empty-config.nix index 78aa00f8..63357112 100644 --- a/tests/modules/programs/cargo/empty-config.nix +++ b/tests/modules/programs/cargo/empty-config.nix @@ -1,5 +1,5 @@ { - programs.docker-cli = { + programs.cargo = { settings = { net = { git-fetch-with-cli = true; From fca4cba863e76c26cfe48e5903c2ff4bac2b2d5d Mon Sep 17 00:00:00 2001 From: Xaver Wenhart Date: Wed, 3 Dec 2025 19:36:40 +0100 Subject: [PATCH 14/80] parallel: package nullable --- modules/programs/parallel.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/programs/parallel.nix b/modules/programs/parallel.nix index 4bb39a24..3ae7581d 100644 --- a/modules/programs/parallel.nix +++ b/modules/programs/parallel.nix @@ -20,7 +20,7 @@ in options.programs.parallel = { enable = mkEnableOption "GNU Parallel"; - package = lib.mkPackageOption pkgs "parallel-full" { }; + package = lib.mkPackageOption pkgs "parallel-full" { nullable = true; }; will-cite = mkOption { type = types.bool; @@ -33,7 +33,7 @@ in config = mkIf cfg.enable { home = { - packages = [ cfg.package ]; + packages = lib.mkIf (cfg.package != null) [ cfg.package ]; file.".parallel/will-cite" = mkIf cfg.will-cite { text = "generated by home manager (programs.parallel.will-cite)"; }; From d441981b200305ebb8e2e2921395f51d207fded6 Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Wed, 3 Dec 2025 16:28:02 +0100 Subject: [PATCH 15/80] thunderbird: fix aliases SMTP configuration not being listed as usable Signed-off-by: tsrk. --- modules/programs/thunderbird.nix | 10 +++++++++- .../thunderbird/thunderbird-expected-first-darwin.js | 2 +- .../thunderbird/thunderbird-expected-first-linux.js | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/programs/thunderbird.nix b/modules/programs/thunderbird.nix index 1fa1e8dc..83b0b2eb 100644 --- a/modules/programs/thunderbird.nix +++ b/modules/programs/thunderbird.nix @@ -9,6 +9,7 @@ let attrValues concatStringsSep filter + flatten length literalExpression mapAttrsToList @@ -918,7 +919,14 @@ in calendarAccounts = getAccountsForProfile name enabledCalendarAccountsWithId; contactAccounts = getAccountsForProfile name enabledContactAccountsWithId; - smtp = filter (a: a.smtp != null) emailAccounts; + accountsSmtp = filter (a: a.smtp != null) emailAccounts; + aliasesSmtp = + let + getAliasesWithSmtp = a: filter (al: builtins.isAttrs al && al.smtp != null) a.aliases; + getAliasesWithId = a: map (al: al // { id = getId a al; }) (getAliasesWithSmtp a); + in + flatten (map getAliasesWithId emailAccounts); + smtp = accountsSmtp ++ aliasesSmtp; feedAccounts = addId (attrValues profile.feedAccounts); diff --git a/tests/modules/programs/thunderbird/thunderbird-expected-first-darwin.js b/tests/modules/programs/thunderbird/thunderbird-expected-first-darwin.js index 852a91b2..bd016760 100644 --- a/tests/modules/programs/thunderbird/thunderbird-expected-first-darwin.js +++ b/tests/modules/programs/thunderbird/thunderbird-expected-first-darwin.js @@ -120,7 +120,7 @@ user_pref("mail.smtpserver.smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfa user_pref("mail.smtpserver.smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f.port", 456); user_pref("mail.smtpserver.smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f.try_ssl", 3); user_pref("mail.smtpserver.smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f.username", "home.manager"); -user_pref("mail.smtpservers", "smtp_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc,smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f"); +user_pref("mail.smtpservers", "smtp_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc,smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f,smtp_b24ca86ede61ed219e6c87cfec261aae2c72785f812489ea943d114d1f39f55b"); user_pref("privacy.donottrackheader.enabled", true); user_pref("mail.html_compose", false); diff --git a/tests/modules/programs/thunderbird/thunderbird-expected-first-linux.js b/tests/modules/programs/thunderbird/thunderbird-expected-first-linux.js index 8cbf7be3..bf32de26 100644 --- a/tests/modules/programs/thunderbird/thunderbird-expected-first-linux.js +++ b/tests/modules/programs/thunderbird/thunderbird-expected-first-linux.js @@ -120,7 +120,7 @@ user_pref("mail.smtpserver.smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfa user_pref("mail.smtpserver.smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f.port", 456); user_pref("mail.smtpserver.smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f.try_ssl", 3); user_pref("mail.smtpserver.smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f.username", "home.manager"); -user_pref("mail.smtpservers", "smtp_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc,smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f"); +user_pref("mail.smtpservers", "smtp_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc,smtp_cda3f13b64c1db7d4b58ce07a31304a362d7dcaf14476bfabcca913ae41ada9f,smtp_b24ca86ede61ed219e6c87cfec261aae2c72785f812489ea943d114d1f39f55b"); user_pref("privacy.donottrackheader.enabled", true); user_pref("mail.html_compose", false); From a521eab881f7bd9cd60a4528f84c17c489b3b75f Mon Sep 17 00:00:00 2001 From: Bernardo Meurer Date: Wed, 3 Dec 2025 22:52:03 +0000 Subject: [PATCH 16/80] home-environment: add home.uid option Add a home.uid option similar to home.username. When set, the activation script verifies the current UID matches the expected value using the new checkUid function. When using the NixOS or nix-darwin modules, home.uid is auto-discovered from users.users..uid when that value is set. This is useful for constructing paths that depend on the user's UID, such as /run/user/ paths for gpg-agent sockets or other user-specific runtime directories. --- modules/home-environment.nix | 10 ++++++++++ modules/lib-bash/activation-init.sh | 11 +++++++++++ nixos/common.nix | 1 + tests/modules/home-environment/default.nix | 2 ++ tests/modules/home-environment/uid-null.nix | 7 +++++++ tests/modules/home-environment/uid.nix | 7 +++++++ 6 files changed, 38 insertions(+) create mode 100644 tests/modules/home-environment/uid-null.nix create mode 100644 tests/modules/home-environment/uid.nix diff --git a/modules/home-environment.nix b/modules/home-environment.nix index 60238360..32c3ce6b 100644 --- a/modules/home-environment.nix +++ b/modules/home-environment.nix @@ -193,6 +193,13 @@ in description = "The user's username."; }; + home.uid = mkOption { + type = types.nullOr types.ints.unsigned; + default = null; + example = 1000; + description = "The user's uid."; + }; + home.homeDirectory = mkOption { type = types.path; defaultText = literalExpression '' @@ -842,6 +849,9 @@ in if [[ ! -v SKIP_SANITY_CHECKS ]]; then checkUsername ${lib.escapeShellArg config.home.username} checkHomeDirectory ${lib.escapeShellArg config.home.homeDirectory} + ${lib.optionalString (config.home.uid != null) '' + checkUid ${toString config.home.uid} + ''} fi ${lib.optionalString config.home.activationGenerateGcRoot '' diff --git a/modules/lib-bash/activation-init.sh b/modules/lib-bash/activation-init.sh index b16d8741..f8fe1103 100755 --- a/modules/lib-bash/activation-init.sh +++ b/modules/lib-bash/activation-init.sh @@ -117,6 +117,17 @@ function checkHomeDirectory() { fi } +function checkUid() { + local expectedUid="$1" + local actualUid + actualUid="$(id -u)" + + if [[ "$actualUid" != "$expectedUid" ]]; then + _iError 'Error: UID is "%s" but we expect "%s"' "$actualUid" "$expectedUid" + exit 1 + fi +} + # Note, the VERBOSE_ECHO variable is deprecated and should not be used inside # the Home Manager project. It is provided here for backwards compatibility. if [[ -v VERBOSE ]]; then diff --git a/nixos/common.nix b/nixos/common.nix index b5bcfa57..68eaa3aa 100644 --- a/nixos/common.nix +++ b/nixos/common.nix @@ -53,6 +53,7 @@ let home.username = config.users.users.${name}.name; home.homeDirectory = config.users.users.${name}.home; + home.uid = mkIf (config.users.users.${name}.uid != null) config.users.users.${name}.uid; # Forward `nix.enable` from the OS configuration. The # conditional is to check whether nix-darwin is new enough diff --git a/tests/modules/home-environment/default.nix b/tests/modules/home-environment/default.nix index 778982a3..c8dc446b 100644 --- a/tests/modules/home-environment/default.nix +++ b/tests/modules/home-environment/default.nix @@ -3,4 +3,6 @@ home-session-search-variables = ./session-search-variables.nix; home-session-variables = ./session-variables.nix; home-nixpkgs-release-check-pkgs = ./nixpkgs-release-check-pkgs.nix; + home-uid = ./uid.nix; + home-uid-null = ./uid-null.nix; } diff --git a/tests/modules/home-environment/uid-null.nix b/tests/modules/home-environment/uid-null.nix new file mode 100644 index 00000000..6f1b915c --- /dev/null +++ b/tests/modules/home-environment/uid-null.nix @@ -0,0 +1,7 @@ +{ + # home.uid defaults to null, so checkUid should not be called in the activation script + + nmt.script = '' + assertFileNotRegex activate "checkUid [0-9]+" + ''; +} diff --git a/tests/modules/home-environment/uid.nix b/tests/modules/home-environment/uid.nix new file mode 100644 index 00000000..06093971 --- /dev/null +++ b/tests/modules/home-environment/uid.nix @@ -0,0 +1,7 @@ +{ + home.uid = 1000; + + nmt.script = '' + assertFileContains activate "checkUid 1000" + ''; +} From 9379fbf4f53f04cd9f8cae9b5f4a25ce5aa29b5f Mon Sep 17 00:00:00 2001 From: "Urocissa Caerulea.Tw" Date: Thu, 4 Dec 2025 05:17:03 +0100 Subject: [PATCH 17/80] Translate using Weblate (Chinese (Traditional Han script)) Currently translated at 100.0% (39 of 39 strings) Translate using Weblate (Chinese (Traditional Han script)) Currently translated at 100.0% (17 of 17 strings) Co-authored-by: Urocissa Caerulea.Tw Translate-URL: https://hosted.weblate.org/projects/home-manager/cli/zh_Hant/ Translate-URL: https://hosted.weblate.org/projects/home-manager/modules/zh_Hant/ Translation: Home Manager/Home Manager CLI Translation: Home Manager/Home Manager Modules --- home-manager/po/zh_Hant.po | 70 +++++++++++++++++++------------------- modules/po/zh_Hant.po | 42 +++++++++++------------ 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/home-manager/po/zh_Hant.po b/home-manager/po/zh_Hant.po index c2fb789b..2bf6463c 100644 --- a/home-manager/po/zh_Hant.po +++ b/home-manager/po/zh_Hant.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Home Manager\n" "Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n" "POT-Creation-Date: 2025-07-22 10:59+0200\n" -"PO-Revision-Date: 2025-03-07 18:58+0000\n" -"Last-Translator: 807 \n" +"PO-Revision-Date: 2025-12-04 04:17+0000\n" +"Last-Translator: \"Urocissa Caerulea.Tw\" \n" "Language-Team: Chinese (Traditional Han script) \n" "Language: zh_Hant\n" @@ -17,21 +17,21 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.10.3-dev\n" +"X-Generator: Weblate 5.15-dev\n" #. translators: For example: "home-manager: missing argument for --cores" #: home-manager/home-manager:16 msgid "%s: missing argument for %s" -msgstr "%s: 缺少參數 %s" +msgstr "%s:缺少 %s 的引數" #. translators: For example: "home-manager: --rollback can only be used after switch" #: home-manager/home-manager:22 msgid "%s: %s can only be used after %s" -msgstr "" +msgstr "%s:%s 只能在 %s 之後使用" #: home-manager/home-manager:71 msgid "No configuration file found at %s" -msgstr "未在 %s 處找到配置檔案" +msgstr "在 %s 找不到設定檔" #. translators: The first '%s' specifier will be replaced by either #. 'home.nix' or 'flake.nix'. @@ -41,12 +41,12 @@ msgid "" "Keeping your Home Manager %s in %s is deprecated,\n" "please move it to %s" msgstr "" -"保持你的 Home Manager 在 %s 中,%s 已被拋棄,\n" -"請將它移動到 %s" +"將 Home Manager 的 %s 放在 %s 中, 已經被棄用,\n" +"請改放到 %s" #: home-manager/home-manager:99 msgid "No configuration file found. Please create one at %s" -msgstr "未找到配置檔案。請在 %s 處建立一份" +msgstr "找不到設定檔。請在 %s 建立新的設定檔" #: home-manager/home-manager:114 msgid "Home Manager not found at %s." @@ -57,7 +57,7 @@ msgstr "在 %s 中找不到 Home Manager。" msgid "" "The fallback Home Manager path %s has been deprecated and a file/directory " "was found there." -msgstr "備用的 Home Manager 路徑 %s 已被拋棄,但一個檔案/資料夾在這被找到。" +msgstr "備用的 Home Manager 路徑 %s 已被棄用,且在該處找到了檔案/目錄。" #. translators: This message will be seen by very few users that likely are familiar with English. So feel free to leave this untranslated. #: home-manager/home-manager:125 @@ -80,21 +80,21 @@ msgid "" "\n" " $ rm -r \"%s\"" msgstr "" -"要消除這個警告,請做以下其中一步。\n" +"若要移除此警告,請執行下列其中一項。\n" "\n" -"1. 告訴Home Manager去使用路徑,例如加入\n" +"1. 明確告知 Home Manager 使用該路徑,例如在\n" "\n" " { programs.home-manager.path = \"%s\"; }\n" "\n" -" 到你的配置中。\n" +" 中,加入您的設定。\n" "\n" -" 如果你想要直接引入Home Manager, 請你使用 `path` 參數r\n" +" 如果想直接匯入 Home Manager,可以在呼叫時使用 `path` 參數來指定路徑:\n" "\n" " pkgs.callPackage /path/to/home-manager-package { path = \"%s\"; }\n" "\n" -" 當呼叫 Home Manager 模組。\n" +" 這樣就能正確傳遞 Home Manager 的路徑。\n" "\n" -"2. 刪除無效的路徑\n" +"2. 移除已棄用的路徑。\n" "\n" " $ rm -r \"%s\"" @@ -104,33 +104,33 @@ msgstr "正在進行 Nix 完整性檢查" #: home-manager/home-manager:173 msgid "Could not find suitable profile directory, tried %s and %s" -msgstr "找不到合適的 profile 目錄,已經嘗試 %s 和 %s" +msgstr "找不到合適的設定檔目錄,已嘗試 %s 和 %s" #. translators: Here "flake" is a noun that refers to the Nix Flakes feature. #: home-manager/home-manager:230 msgid "Can't inspect options of a flake configuration" -msgstr "無法檢查 flake 配置中的選項" +msgstr "無法檢查 flake 設定的選項" #: home-manager/home-manager:305 home-manager/home-manager:328 #: home-manager/home-manager:734 home-manager/home-manager:1237 msgid "%s: unknown option '%s'" -msgstr "%s:未知選項 ‘%s’" +msgstr "%s:未知選項 '%s'" #: home-manager/home-manager:310 home-manager/home-manager:1238 msgid "Run '%s --help' for usage help" -msgstr "執行 ‘%s --help’ 獲取用法幫助" +msgstr "執行 '%s --help' 以取得使用說明" #: home-manager/home-manager:336 home-manager/home-manager:441 msgid "The file %s already exists, leaving it unchanged..." -msgstr "檔案 %s 已經存在,不更改它..." +msgstr "檔案 %s 已存在,保持不變..." #: home-manager/home-manager:338 home-manager/home-manager:443 msgid "Creating %s..." -msgstr "創建 %s 中..." +msgstr "正在建立 %s..." #: home-manager/home-manager:487 msgid "Creating initial Home Manager generation..." -msgstr "正在建立初始 Home Manager 世代 ..." +msgstr "正在建立初始 Home Manager 世代..." #. translators: The "%s" specifier will be replaced by a file path. #: home-manager/home-manager:492 @@ -142,12 +142,12 @@ msgid "" "to configure Home Manager. Run 'man home-configuration.nix' to\n" "see all available options." msgstr "" -"全部工作完成了!home-manager 工具現應已安裝,您可以編輯\n" +"全部完成!home-manager 工具現在應該已被安裝,您可以編輯\n" "\n" " %s\n" "\n" -"來配置 Home Manager。執行 ‘man home-configuration.nix’\n" -"來檢視所有可用選項。" +"來設定 Home Manager。執行 'man home-configuration.nix' 時\n" +"可查看所有可用選項。" #. translators: The "%s" specifier will be replaced by a URL. #: home-manager/home-manager:497 @@ -158,16 +158,16 @@ msgid "" "\n" "if the error seems to be the fault of Home Manager." msgstr "" -"啊哦,安裝失敗了!如果感覺是 Home Manager 造成的錯誤,請在下方\n" +"糟糕,安裝失敗了!如果感覺是 Home Manager 所造成的錯誤,請在此連結\n" "\n" " %s\n" "\n" -"處建立 Issue 告知我們。" +"中,建立 Issue 告知我們。" #. translators: Here "flake" is a noun that refers to the Nix Flakes feature. #: home-manager/home-manager:508 msgid "Can't instantiate a flake configuration" -msgstr "無法建立 flake 配置例項" +msgstr "無法實例化 flake 設定" #: home-manager/home-manager:584 msgid "" @@ -177,12 +177,12 @@ msgid_plural "" "There are %d unread and relevant news items.\n" "Read them by running the command \"%s news\"." msgstr[0] "" -"有 %d 條未讀的相關新聞或訊息。\n" -"可執行 “%s news” 命令進行閱讀。" +"有 %d 則未讀且相關的消息項目。\n" +"執行指令 \"%s news\" 來進行確認。" #: home-manager/home-manager:598 msgid "Unknown \"news.display\" setting \"%s\"." -msgstr "未知的 “news.display” 設定項 “%s”。" +msgstr "未知的 \"news.display\" 設定值 \"%s\"。" #: home-manager/home-manager:606 #, sh-format @@ -191,11 +191,11 @@ msgstr "請設定 $EDITOR 或 $VISUAL 環境變數" #: home-manager/home-manager:624 msgid "Cannot run build in read-only directory" -msgstr "無法在唯讀目錄中執行構建" +msgstr "無法在唯讀目錄中執行建置" #: home-manager/home-manager:787 msgid "The configuration did not contain the specialisation \"%s\"" -msgstr "" +msgstr "設定中不包含特化設定 \"%s\"" #: home-manager/home-manager:841 msgid "No generation with ID %s" @@ -203,7 +203,7 @@ msgstr "沒有 ID 為 %s 的世代" #: home-manager/home-manager:843 msgid "Cannot remove the current generation %s" -msgstr "無法移除當前世代 %s" +msgstr "無法移除目前的世代 %s" #: home-manager/home-manager:845 msgid "Removing generation %s" diff --git a/modules/po/zh_Hant.po b/modules/po/zh_Hant.po index 7b17221b..9c8a0f42 100644 --- a/modules/po/zh_Hant.po +++ b/modules/po/zh_Hant.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Home Manager Modules\n" "Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n" "POT-Creation-Date: 2025-07-22 10:59+0200\n" -"PO-Revision-Date: 2025-03-07 18:58+0000\n" -"Last-Translator: 807 \n" +"PO-Revision-Date: 2025-12-04 04:17+0000\n" +"Last-Translator: \"Urocissa Caerulea.Tw\" \n" "Language-Team: Chinese (Traditional Han script) \n" "Language: zh_Hant\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.10.3-dev\n" +"X-Generator: Weblate 5.15-dev\n" #: modules/files.nix:206 msgid "Creating home file links in %s" @@ -25,15 +25,15 @@ msgstr "正在 %s 中建立家目錄檔案連結" #: modules/files.nix:219 msgid "Cleaning up orphan links from %s" -msgstr "正在從 %s 清理孤立連結" +msgstr "正在清理 %s 中的孤立連結" #: modules/home-environment.nix:647 msgid "Creating new profile generation" -msgstr "正在建立新一代的配置文件中" +msgstr "正在建立新的世代設定檔" #: modules/home-environment.nix:650 msgid "No change so reusing latest profile generation" -msgstr "為發生改變,請重新使用新一代的配置文件" +msgstr "沒有變更,將重複使用最新的設定檔世代" #: modules/home-environment.nix:699 msgid "" @@ -50,18 +50,18 @@ msgid "" "\n" "Then try activating your Home Manager configuration again." msgstr "" -"糟糕,Nix 未能安裝您的新 Home Manager 配置文件!\n" +"糟糕,Nix 無法安裝您的新 Home Manager 設定檔!\n" "\n" -"也許這裏和使用 \"%s\" 安裝的包有衝突?\n" -"嘗試運行\n" +"可能與使用 \"%s\" 安裝的套件衝突?\n" +"請嘗試執行\n" "\n" " %s\n" "\n" -"如果有衝突的包,你可以用\n" +"如果有衝突的套件,您可以使用以下指令移除\n" "\n" " %s\n" "\n" -"來移除。然後嘗試再次啟用您的 Home Manager 配置。" +"然後再次嘗試啟用您的 Home Manager 設定。" #: modules/home-environment.nix:735 msgid "Activating %s" @@ -69,27 +69,27 @@ msgstr "正在啟用 %s" #: modules/home-environment.nix:807 msgid "%s: unknown option '%s'" -msgstr "" +msgstr "%s:未知選項 '%s'" #: modules/lib-bash/activation-init.sh:22 msgid "Migrating profile from %s to %s" -msgstr "正在從 %S 配置文件轉移到 %s 中" +msgstr "正在將設定檔從 %s 遷移至 %s" #: modules/lib-bash/activation-init.sh:54 msgid "Could not find suitable profile directory, tried %s and %s" -msgstr "找不到合適的 profile 目錄,已經嘗試 %s 和 %s" +msgstr "找不到合適的設定檔目錄,已嘗試 %s 和 %s" #: modules/lib-bash/activation-init.sh:106 msgid "Error: USER is set to \"%s\" but we expect \"%s\"" -msgstr "錯誤:USER 被設定為 「%s」但我們希望是 「%s」" +msgstr "錯誤:USER 被設定為「%s」,但我們的預期為「%s」" #: modules/lib-bash/activation-init.sh:115 msgid "Error: HOME is set to \"%s\" but we expect \"%s\"" -msgstr "錯誤:HOME 被設定為 「%s」但我們預期得到 「%s」" +msgstr "錯誤:HOME 被設定為「%s」,但我們的預期為「%s」" #: modules/lib-bash/activation-init.sh:132 msgid "Starting Home Manager activation" -msgstr "正在啟動 Home Manager 初始化程式" +msgstr "正在進行 Home Manager 啟用程序" #: modules/lib-bash/activation-init.sh:136 msgid "Sanity checking Nix" @@ -97,19 +97,19 @@ msgstr "正在進行 Nix 完整性檢查" #: modules/lib-bash/activation-init.sh:149 msgid "This is a dry run" -msgstr "這是試運行" +msgstr "這是模擬執行" #: modules/lib-bash/activation-init.sh:153 msgid "This is a live run" -msgstr "這是在實際運行" +msgstr "這是實際執行" #: modules/lib-bash/activation-init.sh:159 msgid "Using Nix version: %s" -msgstr "正在使用的 Nix 版本: %s" +msgstr "使用中的 Nix 版本:%s" #: modules/lib-bash/activation-init.sh:162 msgid "Activation variables:" -msgstr "啟用的變數:" +msgstr "啟用變數:" #~ msgid "Creating profile generation %s" #~ msgstr "正在建立配置檔案世代 %s" From 05a56dbf24f195c62286e3273a2671d3b4904b00 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 4 Dec 2025 12:13:20 -0600 Subject: [PATCH 18/80] claude-code: fix wrapper to inject --mcp-config after subcommands Manually wrap program to maintain options called from cli. Signed-off-by: Austin Horstman --- modules/programs/claude-code.nix | 23 +++++++++---------- .../programs/claude-code/expected-mcp-wrapper | 5 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/programs/claude-code.nix b/modules/programs/claude-code.nix index f5b16ac7..f4ac4306 100644 --- a/modules/programs/claude-code.nix +++ b/modules/programs/claude-code.nix @@ -345,24 +345,23 @@ in programs.claude-code.finalPackage = let - makeWrapperArgs = lib.flatten ( - lib.filter (x: x != [ ]) [ - (lib.optional (cfg.mcpServers != { }) [ - "--add-flags" - "--mcp-config ${jsonFormat.generate "claude-code-mcp-config.json" { inherit (cfg) mcpServers; }}" - ]) - ] - ); + mcpConfigFile = jsonFormat.generate "claude-code-mcp-config.json" { + inherit (cfg) mcpServers; + }; - hasWrapperArgs = makeWrapperArgs != [ ]; + needsWrapper = cfg.mcpServers != { }; + + wrapperScript = pkgs.writeShellScriptBin "claude" '' + exec "${lib.getExe cfg.package}" "$@" --mcp-config "${mcpConfigFile}" + ''; in - if hasWrapperArgs then + if needsWrapper then pkgs.symlinkJoin { name = "claude-code"; paths = [ cfg.package ]; - nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = '' - wrapProgram $out/bin/claude ${lib.escapeShellArgs makeWrapperArgs} + rm $out/bin/claude + ln -s ${wrapperScript}/bin/claude $out/bin/claude ''; inherit (cfg.package) meta; } diff --git a/tests/modules/programs/claude-code/expected-mcp-wrapper b/tests/modules/programs/claude-code/expected-mcp-wrapper index 636f0ff9..312d51ef 100644 --- a/tests/modules/programs/claude-code/expected-mcp-wrapper +++ b/tests/modules/programs/claude-code/expected-mcp-wrapper @@ -1,2 +1,3 @@ -#! /nix/store/00000000000000000000000000000000-bash/bin/bash -e -exec -a "$0" "/nix/store/00000000000000000000000000000000-claude-code/bin/.claude-wrapped" --mcp-config /nix/store/00000000000000000000000000000000-claude-code-mcp-config.json "$@" +#!/nix/store/00000000000000000000000000000000-bash/bin/bash +exec "/nix/store/00000000000000000000000000000000-claude-code/bin/claude-code" "$@" --mcp-config "/nix/store/00000000000000000000000000000000-claude-code-mcp-config.json" + From df7bac2b2bdbaae8f9100be4e4cd21c7155611e9 Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Thu, 4 Dec 2025 18:05:36 -0300 Subject: [PATCH 19/80] rclone: change cache directory to $XDG_CACHE_HOME/rclone --- modules/programs/rclone.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/rclone.nix b/modules/programs/rclone.nix index f7560fc2..df1a8a21 100644 --- a/modules/programs/rclone.nix +++ b/modules/programs/rclone.nix @@ -153,7 +153,7 @@ in default = { }; apply = lib.mergeAttrs { vfs-cache-mode = "full"; - cache-dir = "%C"; + cache-dir = "%C/rclone"; }; description = '' An attribute set of option values passed to `rclone mount`. To set From 24cc5c080c6cbe5b626d6f4e4d6ce809ca29f683 Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Wed, 3 Dec 2025 10:14:16 -0300 Subject: [PATCH 20/80] calibre: add module --- modules/programs/calibre.nix | 44 +++++++++++++++++++ tests/darwinScrublist.nix | 1 + tests/modules/programs/calibre/default.nix | 1 + .../modules/programs/calibre/plugins/a/a.zip | 0 .../modules/programs/calibre/plugins/b/b.zip | 0 tests/modules/programs/calibre/settings.nix | 14 ++++++ 6 files changed, 60 insertions(+) create mode 100644 modules/programs/calibre.nix create mode 100644 tests/modules/programs/calibre/default.nix create mode 100644 tests/modules/programs/calibre/plugins/a/a.zip create mode 100644 tests/modules/programs/calibre/plugins/b/b.zip create mode 100644 tests/modules/programs/calibre/settings.nix diff --git a/modules/programs/calibre.nix b/modules/programs/calibre.nix new file mode 100644 index 00000000..c28a9a8e --- /dev/null +++ b/modules/programs/calibre.nix @@ -0,0 +1,44 @@ +{ + lib, + pkgs, + config, + ... +}: +let + inherit (lib) + types + mkIf + mkEnableOption + mkPackageOption + mkOption + ; + + cfg = config.programs.calibre; +in +{ + meta.maintainers = with lib.hm.maintainers; [ aguirre-matteo ]; + options.programs.calibre = { + enable = mkEnableOption "calibre"; + package = mkPackageOption pkgs "calibre" { nullable = true; }; + plugins = mkOption { + type = with types; listOf path; + default = [ ]; + description = "List of plugins to install for calibre"; + }; + }; + + config = mkIf cfg.enable { + home.packages = mkIf (cfg.package != null) [ cfg.package ]; + xdg.configFile = mkIf (cfg.plugins != [ ]) ( + let + symlinkedPlugins = pkgs.symlinkJoin { + name = "calibre-plugins"; + paths = cfg.plugins; + }; + in + lib.mapAttrs' ( + k: _: lib.nameValuePair "calibre/plugins/${k}" { source = (symlinkedPlugins + "/${k}"); } + ) (builtins.readDir symlinkedPlugins) + ); + }; +} diff --git a/tests/darwinScrublist.nix b/tests/darwinScrublist.nix index 3979629b..ec8cafd3 100644 --- a/tests/darwinScrublist.nix +++ b/tests/darwinScrublist.nix @@ -27,6 +27,7 @@ let "broot" "browserpass" "btop" + "calibre" "carapace" "cava" "claude-code" diff --git a/tests/modules/programs/calibre/default.nix b/tests/modules/programs/calibre/default.nix new file mode 100644 index 00000000..1b21316c --- /dev/null +++ b/tests/modules/programs/calibre/default.nix @@ -0,0 +1 @@ +{ calibre-settings = ./settings.nix; } diff --git a/tests/modules/programs/calibre/plugins/a/a.zip b/tests/modules/programs/calibre/plugins/a/a.zip new file mode 100644 index 00000000..e69de29b diff --git a/tests/modules/programs/calibre/plugins/b/b.zip b/tests/modules/programs/calibre/plugins/b/b.zip new file mode 100644 index 00000000..e69de29b diff --git a/tests/modules/programs/calibre/settings.nix b/tests/modules/programs/calibre/settings.nix new file mode 100644 index 00000000..1aae893c --- /dev/null +++ b/tests/modules/programs/calibre/settings.nix @@ -0,0 +1,14 @@ +{ + programs.calibre = { + enable = true; + plugins = [ + ./plugins/a + ./plugins/b + ]; + }; + + nmt.script = '' + assertFileExists home-files/.config/calibre/plugins/a.zip + assertFileExists home-files/.config/calibre/plugins/b.zip + ''; +} From 68f7b341793702ec22811e1d0faaca26bf913797 Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Wed, 3 Dec 2025 10:16:17 -0300 Subject: [PATCH 21/80] news: add calibre entry --- modules/misc/news/2025/12/2025-12-03_10-14-53.nix | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 modules/misc/news/2025/12/2025-12-03_10-14-53.nix diff --git a/modules/misc/news/2025/12/2025-12-03_10-14-53.nix b/modules/misc/news/2025/12/2025-12-03_10-14-53.nix new file mode 100644 index 00000000..7d3119ac --- /dev/null +++ b/modules/misc/news/2025/12/2025-12-03_10-14-53.nix @@ -0,0 +1,12 @@ +{ + time = "2025-12-03T13:14:53+00:00"; + condition = true; + message = '' + A new module is available: `programs.calibre` + + Calibre is a powerful and easy to use e-book manager. Users say it’s outstanding + and a must-have. It’ll allow you to do nearly everything and it takes things a + step beyond normal e-book software. It’s also completely free and open source + and great for both casual users and computer experts. + ''; +} From a8b6296a1ebbc5065fa78562246c9384c7c4677d Mon Sep 17 00:00:00 2001 From: Jess Date: Fri, 5 Dec 2025 17:20:48 +1300 Subject: [PATCH 22/80] tests/restic: fix `timedatectl` failure Systemd [added a check](https://github.com/systemd/systemd/commit/6a12c90ca39b45087a84c7faa014f24cd6dc330c) which broke this test. So use `date` instead of `timedatectl`. --- tests/integration/standalone/restic.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/standalone/restic.nix b/tests/integration/standalone/restic.nix index aa45acfc..7298facb 100644 --- a/tests/integration/standalone/restic.nix +++ b/tests/integration/standalone/restic.nix @@ -216,7 +216,7 @@ in def make_backup(time): global snapshot_count - machine.succeed(f"timedatectl set-time '{time}'") + machine.succeed(f"date --set='{time}'") systemctl_succeed_as_alice("start restic-backups-prune-me.service") snapshot_count += 1 actual = \ From 6bbc48804b13a92d2aeb2cd3821e4f5cf698224e Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Thu, 4 Dec 2025 16:34:30 -0300 Subject: [PATCH 23/80] screen: add module --- modules/programs/screen.nix | 55 ++++++++++++++++++++++ tests/modules/programs/screen/default.nix | 1 + tests/modules/programs/screen/screenrc | 14 ++++++ tests/modules/programs/screen/settings.nix | 26 ++++++++++ 4 files changed, 96 insertions(+) create mode 100644 modules/programs/screen.nix create mode 100644 tests/modules/programs/screen/default.nix create mode 100644 tests/modules/programs/screen/screenrc create mode 100644 tests/modules/programs/screen/settings.nix diff --git a/modules/programs/screen.nix b/modules/programs/screen.nix new file mode 100644 index 00000000..e3f28924 --- /dev/null +++ b/modules/programs/screen.nix @@ -0,0 +1,55 @@ +{ + lib, + pkgs, + config, + ... +}: +let + inherit (lib) + types + mkIf + mkEnableOption + mkPackageOption + mkOption + ; + + cfg = config.programs.screen; +in +{ + meta.maintainers = with lib.hm.maintainers; [ aguirre-matteo ]; + options.programs.screen = { + enable = mkEnableOption "screen"; + package = mkPackageOption pkgs "screen" { nullable = true; }; + screenrc = mkOption { + type = with types; nullOr (either path lines); + default = null; + example = '' + screen -t rtorrent rtorrent + screen -t irssi irssi + screen -t centerim centerim + screen -t ncmpc ncmpc -c + screen -t bash4 + screen -t bash5 + screen -t bash6 + screen -t bash7 + screen -t bash8 + screen -t bash9 + altscreen on + term screen-256color + bind ',' prev + bind '.' next + ''; + description = '' + Config file for GNU Screen. All the details can be found here: + . + ''; + }; + }; + + config = mkIf cfg.enable { + home.packages = mkIf (cfg.package != null) [ cfg.package ]; + home.file.".screenrc" = mkIf (cfg.screenrc != null) { + source = if lib.isPath cfg.screenrc then cfg.screenrc else pkgs.writeText "screenrc" cfg.screenrc; + }; + }; +} diff --git a/tests/modules/programs/screen/default.nix b/tests/modules/programs/screen/default.nix new file mode 100644 index 00000000..574fee86 --- /dev/null +++ b/tests/modules/programs/screen/default.nix @@ -0,0 +1 @@ +{ screen-settings = ./settings.nix; } diff --git a/tests/modules/programs/screen/screenrc b/tests/modules/programs/screen/screenrc new file mode 100644 index 00000000..fb33699d --- /dev/null +++ b/tests/modules/programs/screen/screenrc @@ -0,0 +1,14 @@ +screen -t rtorrent rtorrent +screen -t irssi irssi +screen -t centerim centerim +screen -t ncmpc ncmpc -c +screen -t bash4 +screen -t bash5 +screen -t bash6 +screen -t bash7 +screen -t bash8 +screen -t bash9 +altscreen on +term screen-256color +bind ',' prev +bind '.' next diff --git a/tests/modules/programs/screen/settings.nix b/tests/modules/programs/screen/settings.nix new file mode 100644 index 00000000..45be7ba6 --- /dev/null +++ b/tests/modules/programs/screen/settings.nix @@ -0,0 +1,26 @@ +{ + programs.screen = { + enable = true; + screenrc = '' + screen -t rtorrent rtorrent + screen -t irssi irssi + screen -t centerim centerim + screen -t ncmpc ncmpc -c + screen -t bash4 + screen -t bash5 + screen -t bash6 + screen -t bash7 + screen -t bash8 + screen -t bash9 + altscreen on + term screen-256color + bind ',' prev + bind '.' next + ''; + }; + + nmt.script = '' + assertFileExists home-files/.screenrc + assertFileContent home-files/.screenrc ${./screenrc} + ''; +} From 35545f71dd7760c4b9ef717ec853b2269b15386a Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Thu, 4 Dec 2025 16:36:15 -0300 Subject: [PATCH 24/80] news: add screen entry --- modules/misc/news/2025/12/2025-12-04_16-34-38.nix | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 modules/misc/news/2025/12/2025-12-04_16-34-38.nix diff --git a/modules/misc/news/2025/12/2025-12-04_16-34-38.nix b/modules/misc/news/2025/12/2025-12-04_16-34-38.nix new file mode 100644 index 00000000..5c42bb34 --- /dev/null +++ b/modules/misc/news/2025/12/2025-12-04_16-34-38.nix @@ -0,0 +1,12 @@ +{ + time = "2025-12-04T19:34:38+00:00"; + condition = true; + message = '' + A new module is available: `programs.screen` + + GNU Screen is a terminal multiplexer, a software application that can + be used to multiplex several virtual consoles, allowing a user to access + multiple separate login sessions inside a single terminal window, or detach + and reattach sessions from a terminal. + ''; +} From f16bfa59e3fa1df211ac993c6cffbc762a4d4f04 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 4 Dec 2025 23:23:57 -0600 Subject: [PATCH 25/80] home-environment: fix undefined access eval `uid` isn't guaranteed to be defined, have fallback logic in place to account for that. Even if we require it eventually, we can't show a proper assertion/warning without being able to eval. Signed-off-by: Austin Horstman --- nixos/common.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/common.nix b/nixos/common.nix index 68eaa3aa..e9e27206 100644 --- a/nixos/common.nix +++ b/nixos/common.nix @@ -36,7 +36,7 @@ let modules = [ ( - { name, ... }: + { name, options, ... }: { imports = import ../modules/modules.nix { @@ -53,7 +53,7 @@ let home.username = config.users.users.${name}.name; home.homeDirectory = config.users.users.${name}.home; - home.uid = mkIf (config.users.users.${name}.uid != null) config.users.users.${name}.uid; + home.uid = mkIf (options.users.users.${name}.uid.isDefined or false) config.users.users.${name}.uid; # Forward `nix.enable` from the OS configuration. The # conditional is to check whether nix-darwin is new enough From 6bcb2395ab9ed05616052262da526cd7c5b4b123 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 4 Dec 2025 23:29:10 -0600 Subject: [PATCH 26/80] home-environment: style cleanup Signed-off-by: Austin Horstman --- nixos/common.nix | 83 +++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/nixos/common.nix b/nixos/common.nix index e9e27206..be243538 100644 --- a/nixos/common.nix +++ b/nixos/common.nix @@ -2,7 +2,6 @@ # For OS-specific configuration, please edit nixos/default.nix or nix-darwin/default.nix instead. { - options, config, lib, pkgs, @@ -48,23 +47,29 @@ let ++ cfg.sharedModules; config = { - submoduleSupport.enable = true; - submoduleSupport.externalPackageInstall = cfg.useUserPackages; + submoduleSupport = { + enable = true; + externalPackageInstall = cfg.useUserPackages; + }; - home.username = config.users.users.${name}.name; - home.homeDirectory = config.users.users.${name}.home; - home.uid = mkIf (options.users.users.${name}.uid.isDefined or false) config.users.users.${name}.uid; + home = { + username = config.users.users.${name}.name; + homeDirectory = config.users.users.${name}.home; + uid = mkIf (options.users.users.${name}.uid.isDefined or false) config.users.users.${name}.uid; + }; - # Forward `nix.enable` from the OS configuration. The - # conditional is to check whether nix-darwin is new enough - # to have the `nix.enable` option; it was previously a - # `mkRemovedOptionModule` error, which we can crudely detect - # by `visible` being set to `false`. - nix.enable = mkIf (options.nix.enable.visible or true) config.nix.enable; + nix = { + # Forward `nix.enable` from the OS configuration. The + # conditional is to check whether nix-darwin is new enough + # to have the `nix.enable` option; it was previously a + # `mkRemovedOptionModule` error, which we can crudely detect + # by `visible` being set to `false`. + enable = mkIf (options.nix.enable.visible or true) config.nix.enable; - # Make activation script use same version of Nix as system as a whole. - # This avoids problems with Nix not being in PATH. - nix.package = config.nix.package; + # Make activation script use same version of Nix as system as a whole. + # This avoids problems with Nix not being in PATH. + inherit (config.nix) package; + }; }; } ) @@ -161,30 +166,28 @@ in }; }; - config = ( - lib.mkMerge [ - # Fix potential recursion when configuring home-manager users based on values in users.users #594 - (mkIf (cfg.useUserPackages && cfg.users != { }) { - users.users = (lib.mapAttrs (_username: usercfg: { packages = [ usercfg.home.path ]; }) cfg.users); - environment.pathsToLink = [ "/etc/profile.d" ]; - }) - (mkIf (cfg.users != { }) { - warnings = lib.flatten ( - flip lib.mapAttrsToList cfg.users ( - user: config: flip map config.warnings (warning: "${user} profile: ${warning}") - ) - ); + config = lib.mkMerge [ + # Fix potential recursion when configuring home-manager users based on values in users.users #594 + (mkIf (cfg.useUserPackages && cfg.users != { }) { + users.users = lib.mapAttrs (_username: usercfg: { packages = [ usercfg.home.path ]; }) cfg.users; + environment.pathsToLink = [ "/etc/profile.d" ]; + }) + (mkIf (cfg.users != { }) { + warnings = lib.flatten ( + flip lib.mapAttrsToList cfg.users ( + user: config: flip map config.warnings (warning: "${user} profile: ${warning}") + ) + ); - assertions = lib.flatten ( - flip lib.mapAttrsToList cfg.users ( - user: config: - flip map config.assertions (assertion: { - inherit (assertion) assertion; - message = "${user} profile: ${assertion.message}"; - }) - ) - ); - }) - ] - ); + assertions = lib.flatten ( + flip lib.mapAttrsToList cfg.users ( + user: config: + flip map config.assertions (assertion: { + inherit (assertion) assertion; + message = "${user} profile: ${assertion.message}"; + }) + ) + ); + }) + ]; } From 1a99a515a1d32a67bac668b626629b5fd797ff8d Mon Sep 17 00:00:00 2001 From: David Date: Fri, 5 Dec 2025 16:59:02 +0100 Subject: [PATCH 27/80] nixos/common: guard nix.package access when nix.enable is false When nix-darwin has `nix.enable = false` (e.g., for Determinate Nix users), accessing `config.nix.package` throws an error: error: nix.package: accessed when `nix.enable` is off This regression was introduced in commit a521eab when adding the `home.uid` option. The code restructuring changed `inherit (config.nix) package` to be evaluated unconditionally, whereas PR #6383 had previously ensured this worked correctly. The fix wraps the package assignment in `mkIf config.nix.enable` to only access `config.nix.package` when nix management is enabled in the host OS configuration. Fixes: #8303 --- nixos/common.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nixos/common.nix b/nixos/common.nix index be243538..3edecffb 100644 --- a/nixos/common.nix +++ b/nixos/common.nix @@ -68,7 +68,9 @@ let # Make activation script use same version of Nix as system as a whole. # This avoids problems with Nix not being in PATH. - inherit (config.nix) package; + # Only set package when nix is enabled to avoid errors when + # nix-darwin has nix.enable = false (e.g., Determinate Nix users). + package = mkIf config.nix.enable config.nix.package; }; }; } From 519828bf1c97f8bc2ed2d3b79214067047d3c67d Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 5 Dec 2025 10:35:50 -0600 Subject: [PATCH 28/80] claude-code: refactor wrapper to use append-flags Alternative approach suggested in https://github.com/nix-community/home-manager/pull/8290#discussion_r2593114782 Signed-off-by: Austin Horstman --- modules/programs/claude-code.nix | 23 ++++++++++--------- .../programs/claude-code/expected-mcp-wrapper | 5 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/programs/claude-code.nix b/modules/programs/claude-code.nix index f4ac4306..f1ef03fc 100644 --- a/modules/programs/claude-code.nix +++ b/modules/programs/claude-code.nix @@ -345,23 +345,24 @@ in programs.claude-code.finalPackage = let - mcpConfigFile = jsonFormat.generate "claude-code-mcp-config.json" { - inherit (cfg) mcpServers; - }; + makeWrapperArgs = lib.flatten ( + lib.filter (x: x != [ ]) [ + (lib.optional (cfg.mcpServers != { }) [ + "--append-flags" + "--mcp-config ${jsonFormat.generate "claude-code-mcp-config.json" { inherit (cfg) mcpServers; }}" + ]) + ] + ); - needsWrapper = cfg.mcpServers != { }; - - wrapperScript = pkgs.writeShellScriptBin "claude" '' - exec "${lib.getExe cfg.package}" "$@" --mcp-config "${mcpConfigFile}" - ''; + hasWrapperArgs = makeWrapperArgs != [ ]; in - if needsWrapper then + if hasWrapperArgs then pkgs.symlinkJoin { name = "claude-code"; paths = [ cfg.package ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = '' - rm $out/bin/claude - ln -s ${wrapperScript}/bin/claude $out/bin/claude + wrapProgram $out/bin/claude ${lib.escapeShellArgs makeWrapperArgs} ''; inherit (cfg.package) meta; } diff --git a/tests/modules/programs/claude-code/expected-mcp-wrapper b/tests/modules/programs/claude-code/expected-mcp-wrapper index 312d51ef..7a582173 100644 --- a/tests/modules/programs/claude-code/expected-mcp-wrapper +++ b/tests/modules/programs/claude-code/expected-mcp-wrapper @@ -1,3 +1,2 @@ -#!/nix/store/00000000000000000000000000000000-bash/bin/bash -exec "/nix/store/00000000000000000000000000000000-claude-code/bin/claude-code" "$@" --mcp-config "/nix/store/00000000000000000000000000000000-claude-code-mcp-config.json" - +#! /nix/store/00000000000000000000000000000000-bash/bin/bash -e +exec -a "$0" "/nix/store/00000000000000000000000000000000-claude-code/bin/.claude-wrapped" "$@" --mcp-config /nix/store/00000000000000000000000000000000-claude-code-mcp-config.json From ccd22c13b2200263fb59342a34bf7119a31aa363 Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Fri, 5 Dec 2025 09:49:15 -0300 Subject: [PATCH 29/80] restic: change cache directory to $XDG_CACHE_HOME/restic --- modules/services/restic.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/services/restic.nix b/modules/services/restic.nix index 1fc6e9f7..69c3dbd8 100644 --- a/modules/services/restic.nix +++ b/modules/services/restic.nix @@ -475,7 +475,7 @@ in CacheDirectoryMode = "0700"; PrivateTmp = true; - Environment = mkEnvironment backup ++ [ "RESTIC_CACHE_DIR=%C" ]; + Environment = mkEnvironment backup ++ [ "RESTIC_CACHE_DIR=%C/${serviceName}" ]; ExecStart = lib.optional doBackup backupCmd @@ -591,7 +591,7 @@ in lib.concatLines ]} - RESTIC_CACHE_DIR=$HOME/.cache/${serviceName} + RESTIC_CACHE_DIR=${config.xdg.cacheHome}/${serviceName} PATH=${ lib.pipe environment [ From 2e02e22e28ee1541171708cbe8f6a294cac8f43b Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Thu, 4 Dec 2025 22:49:50 -0300 Subject: [PATCH 30/80] hyprlauncher: add module --- modules/services/hyprlauncher.nix | 86 +++++++++++++++++++ .../modules/services/hyprlauncher/default.nix | 5 ++ .../services/hyprlauncher/hyprlauncher.conf | 16 ++++ .../services/hyprlauncher/settings.nix | 20 +++++ 4 files changed, 127 insertions(+) create mode 100644 modules/services/hyprlauncher.nix create mode 100644 tests/modules/services/hyprlauncher/default.nix create mode 100644 tests/modules/services/hyprlauncher/hyprlauncher.conf create mode 100644 tests/modules/services/hyprlauncher/settings.nix diff --git a/modules/services/hyprlauncher.nix b/modules/services/hyprlauncher.nix new file mode 100644 index 00000000..1fcc6b53 --- /dev/null +++ b/modules/services/hyprlauncher.nix @@ -0,0 +1,86 @@ +{ + lib, + pkgs, + config, + ... +}: +let + inherit (lib) + mkIf + mkEnableOption + mkPackageOption + mkOption + ; + + cfg = config.services.hyprlauncher; +in +{ + meta.maintainers = with lib.hm.maintainers; [ aguirre-matteo ]; + + options.services.hyprlauncher = { + enable = mkEnableOption "hyprlauncher"; + package = mkPackageOption pkgs "hyprlauncher" { nullable = true; }; + settings = mkOption { + type = + with lib.types; + let + valueType = + nullOr (oneOf [ + bool + int + float + str + path + (attrsOf valueType) + (listOf valueType) + ]) + // { + description = "Hyprland configuration value"; + }; + in + valueType; + default = { }; + example = { + general.grab_focus = true; + cache.enabled = true; + ui.window_size = "400 260"; + finders = { + math_prefix = "="; + desktop_icons = true; + }; + }; + description = '' + Configuration settings for hyprlauncher. All the available options can be found here: + + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + (lib.hm.assertions.assertPlatform "services.hyprlauncher" pkgs lib.platforms.linux) + ]; + + home.packages = mkIf (cfg.package != null) [ cfg.package ]; + xdg.configFile."hypr/hyprlauncher.conf" = mkIf (cfg.settings != { }) { + text = lib.hm.generators.toHyprconf { attrs = cfg.settings; }; + }; + systemd.user.services.hyprlauncher = mkIf (cfg.package != null) { + Install.WantedBy = [ config.wayland.systemd.target ]; + Unit = { + Description = "hyprlauncher"; + After = [ config.wayland.systemd.target ]; + PartOf = [ config.wayland.systemd.target ]; + X-Restart-Triggers = lib.mkIf (cfg.settings != { }) [ + "${config.xdg.configFile."hypr/hyprlauncher.conf".source}" + ]; + }; + + Service = { + ExecStart = "${lib.getExe cfg.package} -d"; + Restart = "always"; + RestartSec = "10"; + }; + }; + }; +} diff --git a/tests/modules/services/hyprlauncher/default.nix b/tests/modules/services/hyprlauncher/default.nix new file mode 100644 index 00000000..8b221b45 --- /dev/null +++ b/tests/modules/services/hyprlauncher/default.nix @@ -0,0 +1,5 @@ +{ lib, pkgs, ... }: + +lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { + hyprlauncher-settings = ./settings.nix; +} diff --git a/tests/modules/services/hyprlauncher/hyprlauncher.conf b/tests/modules/services/hyprlauncher/hyprlauncher.conf new file mode 100644 index 00000000..6d6239e1 --- /dev/null +++ b/tests/modules/services/hyprlauncher/hyprlauncher.conf @@ -0,0 +1,16 @@ +cache { + enabled=true +} + +finders { + desktop_icons=true + math_prefix== +} + +general { + grab_focus=true +} + +ui { + window_size=400 260 +} diff --git a/tests/modules/services/hyprlauncher/settings.nix b/tests/modules/services/hyprlauncher/settings.nix new file mode 100644 index 00000000..92db74d8 --- /dev/null +++ b/tests/modules/services/hyprlauncher/settings.nix @@ -0,0 +1,20 @@ +{ + services.hyprlauncher = { + enable = true; + settings = { + general.grab_focus = true; + cache.enabled = true; + ui.window_size = "400 260"; + finders = { + math_prefix = "="; + desktop_icons = true; + }; + }; + }; + + nmt.script = '' + assertFileExists home-files/.config/hypr/hyprlauncher.conf + assertFileContent home-files/.config/hypr/hyprlauncher.conf \ + ${./hyprlauncher.conf} + ''; +} From 571c5eed1d24232a0515a0d7d8db337dddad50e9 Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Thu, 4 Dec 2025 22:51:32 -0300 Subject: [PATCH 31/80] news: add hyprlauncher entry --- modules/misc/news/2025/12/2025-12-04_22-50-03.nix | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 modules/misc/news/2025/12/2025-12-04_22-50-03.nix diff --git a/modules/misc/news/2025/12/2025-12-04_22-50-03.nix b/modules/misc/news/2025/12/2025-12-04_22-50-03.nix new file mode 100644 index 00000000..170b57eb --- /dev/null +++ b/modules/misc/news/2025/12/2025-12-04_22-50-03.nix @@ -0,0 +1,12 @@ +{ pkgs, ... }: + +{ + time = "2025-12-05T01:50:03+00:00"; + condition = pkgs.stdenv.hostPlatform.isLinux; + message = '' + A new module is available: `programs.hyprlauncher` + + Hyprlauncher is a multipurpose and versatile launcher/picker + for hyprland. It’s fast, simple, and provides various modules. + ''; +} From 36817384a6583478b8c03d269a7ab9339a7c5dfb Mon Sep 17 00:00:00 2001 From: Hanwen Guo Date: Wed, 3 Dec 2025 12:48:35 -0700 Subject: [PATCH 32/80] programs/git: package nullable Some platforms (e.g. macOS) provide their customized version of git with exclusive features, and user might want to use that instead. --- modules/programs/git.nix | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/programs/git.nix b/modules/programs/git.nix index 3d7bc0d2..b28380f6 100644 --- a/modules/programs/git.nix +++ b/modules/programs/git.nix @@ -41,6 +41,7 @@ in enable = mkEnableOption "Git"; package = lib.mkPackageOption pkgs "git" { + nullable = true; example = "pkgs.gitFull"; extraDescription = '' Use {var}`pkgs.gitFull` @@ -328,7 +329,7 @@ in config = mkIf cfg.enable ( lib.mkMerge [ { - home.packages = [ cfg.package ]; + home.packages = lib.optionals (cfg.package != null) [ cfg.package ]; assertions = [ { @@ -516,7 +517,7 @@ in Type = "oneshot"; ExecStart = let - exe = lib.getExe cfg.package; + exe = if cfg.package != null then lib.getExe cfg.package else "git"; in '' "${exe}" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%i @@ -553,7 +554,7 @@ in launchd.agents = let baseArguments = [ - "${lib.getExe cfg.package}" + "${if cfg.package != null then lib.getExe cfg.package else "git"}" "for-each-repo" "--keep-going" "--config=maintenance.repo" From 012cfcc44ac920b53d424e6f7ac70436c5fcc58c Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 5 Dec 2025 23:05:16 -0600 Subject: [PATCH 33/80] diff-highlight: add git package assertion Git package can be made nullable now, but module is written with expectation of its availability. Signed-off-by: Austin Horstman --- modules/programs/diff-highlight.nix | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/programs/diff-highlight.nix b/modules/programs/diff-highlight.nix index 8bbe178b..1c7ec048 100644 --- a/modules/programs/diff-highlight.nix +++ b/modules/programs/diff-highlight.nix @@ -68,8 +68,15 @@ in in lib.mkMerge [ (mkIf cfg.enable { - # Auto-enable git integration if programs.git.diff-highlight.enable was set to true - programs.diff-highlight.enableGitIntegration = lib.mkIf oldOptionEnabled (lib.mkOverride 1490 true); + assertions = [ + { + assertion = !cfg.enableGitIntegration || config.programs.git.package != null; + message = '' + programs.diff-highlight.enableGitIntegration requires programs.git.package to be set. + Please set programs.git.package to a valid git package. + ''; + } + ]; warnings = lib.optional @@ -77,9 +84,12 @@ in cfg.enableGitIntegration && options.programs.diff-highlight.enableGitIntegration.highestPrio == 1490 ) "`programs.diff-highlight.enableGitIntegration` automatic enablement is deprecated. Please explicitly set `programs.diff-highlight.enableGitIntegration = true`."; + + # Auto-enable git integration if programs.git.diff-highlight.enable was set to true + programs.diff-highlight.enableGitIntegration = lib.mkIf oldOptionEnabled (lib.mkOverride 1490 true); }) - (mkIf (cfg.enable && cfg.enableGitIntegration) { + (mkIf (cfg.enable && cfg.enableGitIntegration && config.programs.git.package != null) { programs.git = { enable = lib.mkDefault true; iniContent = From 27a6182347ccae90a88231ae0dc5dfa7d15815bb Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 5 Dec 2025 23:05:47 -0600 Subject: [PATCH 34/80] tests/diff-highlight: add null assertion test Verify eval when setting git package to null. Signed-off-by: Austin Horstman --- .../programs/diff-highlight/default.nix | 1 + ...ff-highlight-git-package-null-assertion.nix | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/modules/programs/diff-highlight/diff-highlight-git-package-null-assertion.nix diff --git a/tests/modules/programs/diff-highlight/default.nix b/tests/modules/programs/diff-highlight/default.nix index 5bdbe52d..8764b8c5 100644 --- a/tests/modules/programs/diff-highlight/default.nix +++ b/tests/modules/programs/diff-highlight/default.nix @@ -2,4 +2,5 @@ diff-highlight-basic = ./diff-highlight-basic.nix; diff-highlight-with-git-integration = ./diff-highlight-with-git-integration.nix; diff-highlight-migration = ./diff-highlight-migration.nix; + diff-highlight-git-package-null-assertion = ./diff-highlight-git-package-null-assertion.nix; } diff --git a/tests/modules/programs/diff-highlight/diff-highlight-git-package-null-assertion.nix b/tests/modules/programs/diff-highlight/diff-highlight-git-package-null-assertion.nix new file mode 100644 index 00000000..52e795bc --- /dev/null +++ b/tests/modules/programs/diff-highlight/diff-highlight-git-package-null-assertion.nix @@ -0,0 +1,18 @@ +{ + programs.diff-highlight = { + enable = true; + enableGitIntegration = true; + }; + + programs.git = { + enable = true; + package = null; + }; + + test.asserts.assertions.expected = [ + '' + programs.diff-highlight.enableGitIntegration requires programs.git.package to be set. + Please set programs.git.package to a valid git package. + '' + ]; +} From 082822b8a6c89f98e4e4a6873d898817e2762ac5 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Sun, 7 Dec 2025 04:22:42 +0000 Subject: [PATCH 35/80] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/2d293cbfa5a793b4c50d17c05ef9e385b90edf6c?narHash=sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4%3D' (2025-11-30) → 'github:NixOS/nixpkgs/f61125a668a320878494449750330ca58b78c557?narHash=sha256-BmPWzogsG2GsXZtlT%2BMTcAWeDK5hkbGRZTeZNW42fwA%3D' (2025-12-05) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index a7457cc9..65e47984 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1764517877, - "narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=", + "lastModified": 1764950072, + "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c", + "rev": "f61125a668a320878494449750330ca58b78c557", "type": "github" }, "original": { From f9d45d664ed06a11861d0ba29e34f390c07bf62e Mon Sep 17 00:00:00 2001 From: Viktor Titov Date: Mon, 8 Dec 2025 10:17:30 +0500 Subject: [PATCH 36/80] qt: added qt{5,6}ctSettings options (#8271) Added qtctSettings option to qt module to make it possible to configure qt(5/6)ct declaratively. --- modules/misc/qt.nix | 46 ++++++++++++++++++++- tests/modules/misc/qt/default.nix | 3 ++ tests/modules/misc/qt/qt-qt5ct-settings.nix | 13 ++++++ tests/modules/misc/qt/qt-qt6ct-settings.nix | 13 ++++++ tests/modules/misc/qt/qt-qtct-settings.nix | 16 +++++++ 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 tests/modules/misc/qt/qt-qt5ct-settings.nix create mode 100644 tests/modules/misc/qt/qt-qt6ct-settings.nix create mode 100644 tests/modules/misc/qt/qt-qtct-settings.nix diff --git a/modules/misc/qt.nix b/modules/misc/qt.nix index 0fe00d77..6bee4217 100644 --- a/modules/misc/qt.nix +++ b/modules/misc/qt.nix @@ -8,6 +8,10 @@ let cfg = config.qt; + qtctFormat = pkgs.formats.ini { + listToValue = values: lib.concatStringsSep ", " values; + }; + # Map platform names to their packages. platformPackages = with pkgs; { gnome = [ @@ -286,7 +290,34 @@ in ''; }; }; - }; + } + // (lib.genAttrs' [ "qt5ct" "qt6ct" ] ( + name: + lib.nameValuePair "${name}Settings" ( + lib.mkOption { + type = lib.types.nullOr qtctFormat.type; + default = null; + example = lib.literalExpression '' + { + Appearance = { + style = "kvantum"; + icon_theme = "Papirus-Dark"; + standar_dialogs = "xdgdesktopportal"; + }; + Fonts = { + fixed = "\"DejaVuSansM Nerd Font Mono,12\""; + general = "\"DejaVu Sans,12\""; + }; + } + ''; + description = '' + Qtct configuration. Writes settings to `${name}/${name}.conf` + file. Lists will be translated to comma-separated strings. + Fonts must be quoted (see example). + ''; + } + ) + )); }; config = @@ -397,5 +428,18 @@ in ] ++ lib.optionals (platformTheme.name != null) [ "QT_QPA_PLATFORMTHEME" ] ++ lib.optionals (cfg.style.name != null) [ "QT_STYLE_OVERRIDE" ]; + + xdg.configFile = + lib.pipe + [ "qt5ct" "qt6ct" ] + [ + (lib.filter (qtct: cfg."${qtct}Settings" != null)) + (lib.flip lib.genAttrs' ( + qtct: + lib.nameValuePair "${qtct}/${qtct}.conf" { + source = qtctFormat.generate "${qtct}-config" cfg."${qtct}Settings"; + } + )) + ]; }; } diff --git a/tests/modules/misc/qt/default.nix b/tests/modules/misc/qt/default.nix index 88ba03f4..bfdfea91 100644 --- a/tests/modules/misc/qt/default.nix +++ b/tests/modules/misc/qt/default.nix @@ -4,4 +4,7 @@ qt-platform-theme-gtk3 = ./qt-platform-theme-gtk3.nix; qt-platform-theme-gnome = ./qt-platform-theme-gnome.nix; qt-platform-theme-kde6-migration = ./qt-platform-theme-kde6-migration.nix; + qt-qt5ct-settings = ./qt-qt5ct-settings.nix; + qt-qt6ct-settings = ./qt-qt6ct-settings.nix; + qt-qtct-settings = ./qt-qtct-settings.nix; } diff --git a/tests/modules/misc/qt/qt-qt5ct-settings.nix b/tests/modules/misc/qt/qt-qt5ct-settings.nix new file mode 100644 index 00000000..152cdbf6 --- /dev/null +++ b/tests/modules/misc/qt/qt-qt5ct-settings.nix @@ -0,0 +1,13 @@ +{ + qt = { + enable = true; + qt5ctSettings = { + test_section.test_option = "test"; + }; + }; + + nmt.script = '' + assertFileExists "home-files/.config/qt5ct/qt5ct.conf" + assertPathNotExists "home-files/.config/qt6ct/qt6ct.conf" + ''; +} diff --git a/tests/modules/misc/qt/qt-qt6ct-settings.nix b/tests/modules/misc/qt/qt-qt6ct-settings.nix new file mode 100644 index 00000000..d363d881 --- /dev/null +++ b/tests/modules/misc/qt/qt-qt6ct-settings.nix @@ -0,0 +1,13 @@ +{ + qt = { + enable = true; + qt6ctSettings = { + test_section.test_option = "test"; + }; + }; + + nmt.script = '' + assertFileExists "home-files/.config/qt6ct/qt6ct.conf" + assertPathNotExists "home-files/.config/qt5ct/qt5ct.conf" + ''; +} diff --git a/tests/modules/misc/qt/qt-qtct-settings.nix b/tests/modules/misc/qt/qt-qtct-settings.nix new file mode 100644 index 00000000..e5805009 --- /dev/null +++ b/tests/modules/misc/qt/qt-qtct-settings.nix @@ -0,0 +1,16 @@ +{ + qt = { + enable = true; + qt5ctSettings = { + test_section.test_option = "test"; + }; + qt6ctSettings = { + test_section.test_option = "test"; + }; + }; + + nmt.script = '' + assertFileExists "home-files/.config/qt5ct/qt5ct.conf" + assertFileExists "home-files/.config/qt6ct/qt6ct.conf" + ''; +} From 6bdf2a68e1d55c96ab655d4f64bae0b3fe578fcc Mon Sep 17 00:00:00 2001 From: Mirko Lenz Date: Sat, 6 Dec 2025 09:51:31 +0100 Subject: [PATCH 37/80] ty: add module --- .../misc/news/2025/12/2025-12-06_11-05-43.nix | 10 ++++ modules/programs/ty.nix | 51 +++++++++++++++++++ tests/modules/programs/ty/default.nix | 4 ++ .../modules/programs/ty/example-settings.nix | 25 +++++++++ tests/modules/programs/ty/no-settings.nix | 17 +++++++ 5 files changed, 107 insertions(+) create mode 100644 modules/misc/news/2025/12/2025-12-06_11-05-43.nix create mode 100644 modules/programs/ty.nix create mode 100644 tests/modules/programs/ty/default.nix create mode 100644 tests/modules/programs/ty/example-settings.nix create mode 100644 tests/modules/programs/ty/no-settings.nix diff --git a/modules/misc/news/2025/12/2025-12-06_11-05-43.nix b/modules/misc/news/2025/12/2025-12-06_11-05-43.nix new file mode 100644 index 00000000..91388980 --- /dev/null +++ b/modules/misc/news/2025/12/2025-12-06_11-05-43.nix @@ -0,0 +1,10 @@ +{ + time = "2025-12-06T10:05:43+00:00"; + condition = true; + message = '' + A new module is available: `programs.ty` + + It allows to manage the configuration and package + of the Python language server `ty` by Astral. + ''; +} diff --git a/modules/programs/ty.nix b/modules/programs/ty.nix new file mode 100644 index 00000000..b278ef7c --- /dev/null +++ b/modules/programs/ty.nix @@ -0,0 +1,51 @@ +{ + pkgs, + config, + lib, + ... +}: +let + inherit (lib) + mkEnableOption + mkPackageOption + mkOption + literalExpression + ; + + tomlFormat = pkgs.formats.toml { }; + cfg = config.programs.ty; +in +{ + meta.maintainers = with lib.maintainers; [ mirkolenz ]; + + options.programs.ty = { + enable = mkEnableOption "ty"; + + package = mkPackageOption pkgs "ty" { nullable = true; }; + + settings = mkOption { + type = tomlFormat.type; + default = { }; + example = literalExpression '' + { + rules.index-out-of-bounds = "ignore"; + } + ''; + description = '' + Configuration written to + {file}`$XDG_CONFIG_HOME/ty/ty.toml`. + See + and + for more information. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + + xdg.configFile."ty/ty.toml" = lib.mkIf (cfg.settings != { }) { + source = tomlFormat.generate "ty-config.toml" cfg.settings; + }; + }; +} diff --git a/tests/modules/programs/ty/default.nix b/tests/modules/programs/ty/default.nix new file mode 100644 index 00000000..be700b67 --- /dev/null +++ b/tests/modules/programs/ty/default.nix @@ -0,0 +1,4 @@ +{ + ty-example-settings = ./example-settings.nix; + ty-no-settings = ./no-settings.nix; +} diff --git a/tests/modules/programs/ty/example-settings.nix b/tests/modules/programs/ty/example-settings.nix new file mode 100644 index 00000000..4d460ead --- /dev/null +++ b/tests/modules/programs/ty/example-settings.nix @@ -0,0 +1,25 @@ +{ pkgs, ... }: + +{ + programs.ty = { + enable = true; + settings = { + rules.index-out-of-bounds = "ignore"; + }; + }; + + test.stubs.ty = { }; + + nmt.script = + let + expectedConfigPath = "home-files/.config/ty/ty.toml"; + expectedConfigContent = pkgs.writeText "ty.config-custom.expected" '' + [rules] + index-out-of-bounds = "ignore" + ''; + in + '' + assertFileExists "${expectedConfigPath}" + assertFileContent "${expectedConfigPath}" "${expectedConfigContent}" + ''; +} diff --git a/tests/modules/programs/ty/no-settings.nix b/tests/modules/programs/ty/no-settings.nix new file mode 100644 index 00000000..23bc04bf --- /dev/null +++ b/tests/modules/programs/ty/no-settings.nix @@ -0,0 +1,17 @@ +{ pkgs, ... }: + +{ + programs.ty = { + enable = true; + }; + + test.stubs.ty = { }; + + nmt.script = + let + expectedConfigPath = "home-files/.config/ty/ty.toml"; + in + '' + assertPathNotExists "${expectedConfigPath}" + ''; +} From a7887340779d5e70ef68012f3823568ee59eaa30 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 7 Dec 2025 23:40:35 -0600 Subject: [PATCH 38/80] swayidle: fix events default Changed from list to attrset, but default never updated. Signed-off-by: Austin Horstman --- modules/services/swayidle.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/services/swayidle.nix b/modules/services/swayidle.nix index 5af0b467..3b96da01 100644 --- a/modules/services/swayidle.nix +++ b/modules/services/swayidle.nix @@ -108,7 +108,7 @@ in ) ) ) (submodule eventsModule); - default = [ ]; + default = { }; example = literalExpression '' { "before-sleep" = "''${pkgs.swaylock}/bin/swaylock -fF"; From caa47b637d877124ac891a64abc14de09fce1675 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 09:20:44 +0000 Subject: [PATCH 39/80] maintainers: update all-maintainers.nix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automated update of the master maintainers list combining: - Home Manager specific maintainers from modules/lib/maintainers.nix - Nixpkgs maintainers referenced in Home Manager modules **Added:** 1 maintainers **Removed:** 0 maintainers **Total:** 281 → 282 maintainers **✅ Added:** xavwe Generated by: lib/python/generate-all-maintainers.py --- all-maintainers.nix | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/all-maintainers.nix b/all-maintainers.nix index 10ddd58e..df645916 100644 --- a/all-maintainers.nix +++ b/all-maintainers.nix @@ -2311,6 +2311,13 @@ name = "Florian Peter"; source = "nixpkgs"; }; + xavwe = { + email = "git@xavwe.dev"; + github = "xavwe"; + githubId = 125409009; + name = "Xaver Wenhart"; + source = "nixpkgs"; + }; xlambein = { email = "xlambein@gmail.com"; github = "xlambein"; From e5b1f87841810fc24772bf4389f9793702000c9b Mon Sep 17 00:00:00 2001 From: June Stepp Date: Sat, 6 Dec 2025 14:33:41 -0600 Subject: [PATCH 40/80] anki: enable tests on Darwin Anki is no longer marked as broken in nixpkgs. --- tests/modules/programs/anki/default.nix | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/modules/programs/anki/default.nix b/tests/modules/programs/anki/default.nix index a4fd8d9a..baccadfa 100644 --- a/tests/modules/programs/anki/default.nix +++ b/tests/modules/programs/anki/default.nix @@ -1,7 +1,4 @@ -{ lib, pkgs, ... }: - -# Anki is currently marked as broken on Darwin (2025/06/23) -lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { +{ anki-minimal-config = ./minimal-config.nix; anki-full-config = ./full-config.nix; } From 13cc1efd78b943b98c08d74c9060a5b59bf86921 Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Tue, 9 Dec 2025 16:06:37 +0800 Subject: [PATCH 41/80] snixembed: add waybar incompatbility warning If snixembed is enabled and you try to use the waybar tray the two tools conflict with eachother and often waybar's tray will not show any icons. This adds a warning about it, as the problem can be difficult to diagnose. --- modules/services/snixembed.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/services/snixembed.nix b/modules/services/snixembed.nix index e9b1572e..4f55b200 100644 --- a/modules/services/snixembed.nix +++ b/modules/services/snixembed.nix @@ -7,6 +7,7 @@ let cfg = config.services.snixembed; + waybarCfg = config.programs.waybar; in { meta.maintainers = [ lib.maintainers.DamienCassou ]; @@ -32,6 +33,10 @@ in assertions = [ (lib.hm.assertions.assertPlatform "services.snixembed" pkgs lib.platforms.linux) ]; + warnings = lib.optional waybarCfg.enable '' + snixembed and waybar should not be enabled at the same time. + You may experience inconsistent tray behavior as a result. + ''; systemd.user.services.snixembed = { Install.WantedBy = [ "graphical-session.target" ]; From af7c726e8b0daf9b657027310d223b1fa19b2923 Mon Sep 17 00:00:00 2001 From: Kyure_A <49436968+Kyure-A@users.noreply.github.com> Date: Thu, 11 Dec 2025 05:17:15 +0900 Subject: [PATCH 42/80] sheldon: replace initExtra with initContent --- modules/programs/sheldon.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/sheldon.nix b/modules/programs/sheldon.nix index f1055514..806cd566 100644 --- a/modules/programs/sheldon.nix +++ b/modules/programs/sheldon.nix @@ -52,7 +52,7 @@ in eval "$(sheldon source)" ''; - programs.zsh.initExtra = mkIf cfg.enableZshIntegration '' + programs.zsh.initContent = mkIf cfg.enableZshIntegration '' eval "$(sheldon source)" ''; From 7b34e428f31ce52baabda53e52bf14002e1c0625 Mon Sep 17 00:00:00 2001 From: Nick DeGroot Date: Wed, 10 Dec 2025 12:55:09 -0800 Subject: [PATCH 43/80] chromium: fix `null` package --- modules/programs/chromium.nix | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/programs/chromium.nix b/modules/programs/chromium.nix index 88b15f68..5344b60a 100644 --- a/modules/programs/chromium.nix +++ b/modules/programs/chromium.nix @@ -40,7 +40,7 @@ let finalPackage = mkOption { inherit visible; - type = types.package; + type = types.nullOr types.package; readOnly = true; description = '' Resulting customized ${name} package @@ -220,15 +220,22 @@ let }; in + lib.mkIf cfg.enable { - programs.${browser}.finalPackage = lib.mkIf (cfg.package != null) ( + assertions = [ + { + assertion = !(cfg.package == null && cfg.commandLineArgs != [ ]); + message = "Cannot set `commandLineArgs` when `package` is null for ${browser}."; + } + ]; + + programs.${browser}.finalPackage = if cfg.commandLineArgs != [ ] then cfg.package.override { commandLineArgs = lib.concatStringsSep " " cfg.commandLineArgs; } else - cfg.package - ); + cfg.package; home.packages = lib.mkIf (cfg.finalPackage != null) [ cfg.finalPackage From dd18018d060cfcce3837ba44c920c83e33096390 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 04:21:54 +0000 Subject: [PATCH 44/80] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/f61125a668a320878494449750330ca58b78c557?narHash=sha256-BmPWzogsG2GsXZtlT%2BMTcAWeDK5hkbGRZTeZNW42fwA%3D' (2025-12-05) → 'github:NixOS/nixpkgs/addf7cf5f383a3101ecfba091b98d0a1263dc9b8?narHash=sha256-hM20uyap1a0M9d344I692r%2Bik4gTMyj60cQWO%2BhAYP8%3D' (2025-12-08) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 65e47984..8a30674d 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1764950072, - "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", + "lastModified": 1765186076, + "narHash": "sha256-hM20uyap1a0M9d344I692r+ik4gTMyj60cQWO+hAYP8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f61125a668a320878494449750330ca58b78c557", + "rev": "addf7cf5f383a3101ecfba091b98d0a1263dc9b8", "type": "github" }, "original": { From e52be4fb78ef9901fc663606f65a673b948bdc13 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 11 Dec 2025 12:37:24 -0600 Subject: [PATCH 45/80] tldr-update: perchun -> PerchunPak Signed-off-by: Austin Horstman --- modules/services/tldr-update.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/services/tldr-update.nix b/modules/services/tldr-update.nix index 4c4e73db..d5fb96a6 100644 --- a/modules/services/tldr-update.nix +++ b/modules/services/tldr-update.nix @@ -8,7 +8,7 @@ let cfg = config.services.tldr-update; in { - meta.maintainers = [ lib.maintainers.perchun ]; + meta.maintainers = [ lib.maintainers.PerchunPak ]; options.services.tldr-update = { enable = lib.mkEnableOption '' From 574f6d3526c98d6f2df020a22298349a7014dda7 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 11 Dec 2025 12:37:31 -0600 Subject: [PATCH 46/80] hyprpanel: perchun -> PerchunPak Signed-off-by: Austin Horstman --- modules/programs/hyprpanel/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/hyprpanel/default.nix b/modules/programs/hyprpanel/default.nix index 3c558dc7..f44b8f72 100644 --- a/modules/programs/hyprpanel/default.nix +++ b/modules/programs/hyprpanel/default.nix @@ -9,7 +9,7 @@ let jsonFormat = pkgs.formats.json { }; in { - meta.maintainers = [ lib.maintainers.perchun ]; + meta.maintainers = [ lib.maintainers.PerchunPak ]; imports = [ (lib.mkRemovedOptionModule [ "programs" "hyprpanel" "dontAssertNotificationDaemons " ] '' From 9b5ac85d79d1136e51834f6c0d6987107ea28181 Mon Sep 17 00:00:00 2001 From: Mirko Lenz Date: Sat, 6 Dec 2025 10:07:08 +0100 Subject: [PATCH 47/80] npm: add module --- .../misc/news/2025/12/2025-12-06_11-03-01.nix | 10 +++ modules/programs/npm.nix | 74 +++++++++++++++++++ tests/modules/programs/npm/default.nix | 4 + .../modules/programs/npm/example-settings.nix | 36 +++++++++ tests/modules/programs/npm/no-settings.nix | 17 +++++ 5 files changed, 141 insertions(+) create mode 100644 modules/misc/news/2025/12/2025-12-06_11-03-01.nix create mode 100644 modules/programs/npm.nix create mode 100644 tests/modules/programs/npm/default.nix create mode 100644 tests/modules/programs/npm/example-settings.nix create mode 100644 tests/modules/programs/npm/no-settings.nix diff --git a/modules/misc/news/2025/12/2025-12-06_11-03-01.nix b/modules/misc/news/2025/12/2025-12-06_11-03-01.nix new file mode 100644 index 00000000..79cd2651 --- /dev/null +++ b/modules/misc/news/2025/12/2025-12-06_11-03-01.nix @@ -0,0 +1,10 @@ +{ + time = "2025-12-06T10:03:01+00:00"; + condition = true; + message = '' + A new module is available: `programs.npm` + + It allows you manage your npm user configuration (`.npmrc`) + and install a specific version of the package. + ''; +} diff --git a/modules/programs/npm.nix b/modules/programs/npm.nix new file mode 100644 index 00000000..015b80b8 --- /dev/null +++ b/modules/programs/npm.nix @@ -0,0 +1,74 @@ +# https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/programs/npm.nix +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.programs.npm; + + xdgConfigHome = lib.removePrefix config.home.homeDirectory config.xdg.configHome; + configFile = if config.home.preferXdgDirectories then "${xdgConfigHome}/npm/npmrc" else ".npmrc"; + + iniFormat = pkgs.formats.ini { + listsAsDuplicateKeys = true; + }; + + toNpmrc = + let + mkLine = lib.generators.mkKeyValueDefault { } "="; + mkLines = k: v: if lib.isList v then map (x: mkLine "${k}[]" x) v else [ (mkLine k v) ]; + in + attrs: lib.concatLines (lib.concatLists (lib.mapAttrsToList mkLines attrs)); +in +{ + meta.maintainers = with lib.maintainers; [ mirkolenz ]; + + options = { + programs.npm = { + enable = lib.mkEnableOption "{command}`npm` user config"; + + package = lib.mkPackageOption pkgs [ "nodejs" ] { + example = "nodejs_24"; + nullable = true; + }; + + settings = lib.mkOption { + type = lib.types.attrsOf iniFormat.lib.types.atom; + description = '' + The user-specific npm configuration. + See and + + for more information. + ''; + default = { + prefix = "\${HOME}/.npm"; + }; + example = lib.literalExpression '' + { + color = true; + include = [ + "dev" + "prod" + ]; + init-license = "MIT"; + prefix = "''${HOME}/.npm"; + } + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + home = { + packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + file.${configFile} = lib.mkIf (cfg.settings != { }) { + text = toNpmrc cfg.settings; + }; + sessionVariables = lib.mkIf (cfg.settings != { }) { + NPM_CONFIG_USERCONFIG = "${config.home.homeDirectory}/${configFile}"; + }; + }; + }; +} diff --git a/tests/modules/programs/npm/default.nix b/tests/modules/programs/npm/default.nix new file mode 100644 index 00000000..d66921ec --- /dev/null +++ b/tests/modules/programs/npm/default.nix @@ -0,0 +1,4 @@ +{ + npm-example-settings = ./example-settings.nix; + npm-no-settings = ./no-settings.nix; +} diff --git a/tests/modules/programs/npm/example-settings.nix b/tests/modules/programs/npm/example-settings.nix new file mode 100644 index 00000000..b8ce0858 --- /dev/null +++ b/tests/modules/programs/npm/example-settings.nix @@ -0,0 +1,36 @@ +{ pkgs, ... }: + +{ + programs.npm = { + enable = true; + settings = { + color = true; + include = [ + "dev" + "prod" + ]; + init-license = "MIT"; + prefix = "\${HOME}/.npm"; + }; + }; + + test.stubs.nodejs = { }; + + nmt.script = + let + configPath = "home-files/.npmrc"; + expectedConfig = pkgs.writeText "npmrc-expected" '' + color=true + include[]=dev + include[]=prod + init-license=MIT + prefix=''${HOME}/.npm + ''; + in + '' + assertFileExists "${configPath}" + assertFileContent "${configPath}" "${expectedConfig}" + assertFileContains home-path/etc/profile.d/hm-session-vars.sh \ + 'export NPM_CONFIG_USERCONFIG="/home/hm-user/.npmrc"' + ''; +} diff --git a/tests/modules/programs/npm/no-settings.nix b/tests/modules/programs/npm/no-settings.nix new file mode 100644 index 00000000..21b0b677 --- /dev/null +++ b/tests/modules/programs/npm/no-settings.nix @@ -0,0 +1,17 @@ +{ ... }: + +{ + programs.npm = { + enable = true; + settings = { }; + }; + + test.stubs.nodejs = { }; + + nmt.script = '' + assertPathNotExists home-files/.npmrc + assertFileExists home-path/etc/profile.d/hm-session-vars.sh + assertFileNotRegex home-path/etc/profile.d/hm-session-vars.sh \ + "export NPM_CONFIG_USERCONFIG=" + ''; +} From 0cf525a5be5d4250b2804dc2d104bdcc406052cb Mon Sep 17 00:00:00 2001 From: Jacob Abel Date: Thu, 11 Dec 2025 14:04:13 -0500 Subject: [PATCH 48/80] option defaultEditor: Set {env}`VISUAL` on true (#8322) The defaultEditor option sets {env}`EDITOR` however strictly speaking {env}`EDITOR` is intended for editors that are fully compatible with teletype terminals (ex: `ed` or `vi`'s `ex` mode). The {env}`VISUAL variable is intended for modern "visual mode" terminal editors (ex: `vi` or `emacs`). Technically speaking editors that are assigned to {env}`EDITOR` should be configured to operate in teletype compatible mode (see `vi -e` and `vim -e`). We don't do this currently because for most users this would be unintuitive behavior when a script or program mistakenly checks {env}`EDITOR` instead of first checking {env}`VISUAL`. In the future it may be worthwhile to introduce an additional option to these modules to configure {env}`EDITOR` in a strictly conforming manner (i.e. using the teletype/`ex` mode flags or unsetting {env}`EDITOR` entirely). Related Issue: #8314 Signed-off-by: Jacob Abel --- modules/programs/helix.nix | 8 ++++++-- modules/programs/kakoune.nix | 8 ++++++-- modules/programs/neovim.nix | 8 ++++++-- modules/programs/vim.nix | 8 ++++++-- modules/services/emacs.nix | 18 ++++++++++++------ 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/modules/programs/helix.nix b/modules/programs/helix.nix index 2e2f78c9..74b029c0 100644 --- a/modules/programs/helix.nix +++ b/modules/programs/helix.nix @@ -49,7 +49,8 @@ in default = false; description = '' Whether to configure {command}`hx` as the default - editor using the {env}`EDITOR` environment variable. + editor using the {env}`EDITOR` and {env}`VISUAL` + environment variables. ''; }; @@ -225,7 +226,10 @@ in else [ cfg.package ]; - home.sessionVariables = mkIf cfg.defaultEditor { EDITOR = "hx"; }; + home.sessionVariables = mkIf cfg.defaultEditor { + EDITOR = "hx"; + VISUAL = "hx"; + }; xdg.configFile = let diff --git a/modules/programs/kakoune.nix b/modules/programs/kakoune.nix index becc7519..ca8e8a58 100644 --- a/modules/programs/kakoune.nix +++ b/modules/programs/kakoune.nix @@ -705,7 +705,8 @@ in default = false; description = '' Whether to configure {command}`kak` as the default - editor using the {env}`EDITOR` environment variable. + editor using the {env}`EDITOR` and {env}`VISUAL` + environment variables. ''; }; @@ -755,7 +756,10 @@ in programs.kakoune.finalPackage = lib.mkIf (cfg.package != null) kakouneWithPlugins; home.packages = lib.mkIf (cfg.finalPackage != null) [ cfg.finalPackage ]; - home.sessionVariables = mkIf cfg.defaultEditor { EDITOR = "kak"; }; + home.sessionVariables = mkIf cfg.defaultEditor { + EDITOR = "kak"; + VISUAL = "kak"; + }; xdg.configFile = lib.mkMerge [ { "kak/kakrc".source = configFile; } (mkIf (cfg.colorSchemePackage != null) { diff --git a/modules/programs/neovim.nix b/modules/programs/neovim.nix index a234a133..271bc150 100644 --- a/modules/programs/neovim.nix +++ b/modules/programs/neovim.nix @@ -289,7 +289,8 @@ in default = false; description = '' Whether to configure {command}`nvim` as the default - editor using the {env}`EDITOR` environment variable. + editor using the {env}`EDITOR` and {env}`VISUAL` + environment variables. ''; }; @@ -453,7 +454,10 @@ in home.packages = [ cfg.finalPackage ]; - home.sessionVariables = mkIf cfg.defaultEditor { EDITOR = "nvim"; }; + home.sessionVariables = mkIf cfg.defaultEditor { + EDITOR = "nvim"; + VISUAL = "nvim"; + }; home.shellAliases = mkIf cfg.vimdiffAlias { vimdiff = "nvim -d"; }; diff --git a/modules/programs/vim.nix b/modules/programs/vim.nix index 6773c878..5f919dfa 100644 --- a/modules/programs/vim.nix +++ b/modules/programs/vim.nix @@ -162,7 +162,8 @@ in default = false; description = '' Whether to configure {command}`vim` as the default - editor using the {env}`EDITOR` environment variable. + editor using the {env}`EDITOR` and {env}`VISUAL` + environment variables. ''; }; }; @@ -213,7 +214,10 @@ in home.packages = [ cfg.package ]; - home.sessionVariables = lib.mkIf cfg.defaultEditor { EDITOR = "vim"; }; + home.sessionVariables = lib.mkIf cfg.defaultEditor { + EDITOR = "vim"; + VISUAL = "vim"; + }; programs.vim = { package = vim; diff --git a/modules/services/emacs.nix b/modules/services/emacs.nix index 117c3b72..a7327199 100644 --- a/modules/services/emacs.nix +++ b/modules/services/emacs.nix @@ -113,7 +113,8 @@ in example = !default; description = '' Whether to configure {command}`emacsclient` as the default - editor using the {env}`EDITOR` environment variable. + editor using the {env}`EDITOR` and {env}`VISUAL` + environment variables. ''; }; }; @@ -121,11 +122,16 @@ in config = mkIf cfg.enable ( lib.mkMerge [ { - home.sessionVariables = mkIf cfg.defaultEditor { - EDITOR = lib.getBin ( - pkgs.writeShellScript "editor" ''exec ${lib.getBin cfg.package}/bin/emacsclient "''${@:---create-frame}"'' - ); - }; + home.sessionVariables = + let + editorBin = lib.getBin ( + pkgs.writeShellScript "editor" ''exec ${lib.getBin cfg.package}/bin/emacsclient "''${@:---create-frame}"'' + ); + in + mkIf cfg.defaultEditor { + EDITOR = editorBin; + VISUAL = editorBin; + }; } (mkIf pkgs.stdenv.isLinux { From 39cb677ed9e908e90478aa9fe5f3383dfc1a63f3 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 11 Dec 2025 13:10:21 -0600 Subject: [PATCH 49/80] news: add defaultEditor entry Let users know about new variable being set. Signed-off-by: Austin Horstman --- .../misc/news/2025/12/2025-12-11_13-04-30.nix | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 modules/misc/news/2025/12/2025-12-11_13-04-30.nix diff --git a/modules/misc/news/2025/12/2025-12-11_13-04-30.nix b/modules/misc/news/2025/12/2025-12-11_13-04-30.nix new file mode 100644 index 00000000..ddafb6dd --- /dev/null +++ b/modules/misc/news/2025/12/2025-12-11_13-04-30.nix @@ -0,0 +1,32 @@ +{ config, ... }: + +{ + time = "2025-12-11T19:04:30+00:00"; + condition = + let + helixEnabled = config.programs.helix.enable && config.programs.helix.defaultEditor; + kakouneEnabled = config.programs.kakoune.enable && config.programs.kakoune.defaultEditor; + neovimEnabled = config.programs.neovim.enable && config.programs.neovim.defaultEditor; + vimEnabled = config.programs.vim.enable && config.programs.vim.defaultEditor; + emacsEnabled = config.services.emacs.enable && config.services.emacs.defaultEditor; + in + helixEnabled || kakouneEnabled || neovimEnabled || vimEnabled || emacsEnabled; + message = '' + The 'defaultEditor' option now sets both {env}`EDITOR` and {env}`VISUAL` + environment variables. + + Previously, only {env}`EDITOR` was set. The {env}`VISUAL` variable is now + also configured to point to the same editor, which is the expected behavior + for modern terminal editors. + + This change affects the following modules: + - programs.helix + - programs.kakoune + - programs.neovim + - programs.vim + - services.emacs + + No action is required. This change should improve compatibility with tools + that check {env}`VISUAL` before {env}`EDITOR`. + ''; +} From 4767a9c7190cd0b774d8aeb67a46715c84ea29f4 Mon Sep 17 00:00:00 2001 From: Olmo Kramer Date: Sat, 22 Jun 2024 10:37:42 +0200 Subject: [PATCH 50/80] herbstluftwm: Make herbstclient alias optional The `herbstclient` alias in the generated `autostart` made it impossible to use bash functions. This makes the `herbstclient` alias optional by adding an extra `herbstclientAlias` option on the herbstluftwm configuration. The new option defaults to `false` as to not confuse newcomers to the herbstluftwm module, which is not a breaking change because it was only an optimization. --- .../misc/news/2025/12/2025-12-12_19-20-28.nix | 12 +++++ .../services/window-managers/herbstluftwm.nix | 32 ++++++++++-- .../modules/services/herbstluftwm/default.nix | 1 + .../herbstluftwm-alias-disabled-autostart | 47 ++++++++++++++++++ .../herbstluftwm-alias-disabled.nix | 49 +++++++++++++++++++ .../herbstluftwm-no-tags-autostart | 2 + .../herbstluftwm-simple-config-autostart | 2 + 7 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 modules/misc/news/2025/12/2025-12-12_19-20-28.nix create mode 100644 tests/modules/services/herbstluftwm/herbstluftwm-alias-disabled-autostart create mode 100644 tests/modules/services/herbstluftwm/herbstluftwm-alias-disabled.nix diff --git a/modules/misc/news/2025/12/2025-12-12_19-20-28.nix b/modules/misc/news/2025/12/2025-12-12_19-20-28.nix new file mode 100644 index 00000000..6aea4d88 --- /dev/null +++ b/modules/misc/news/2025/12/2025-12-12_19-20-28.nix @@ -0,0 +1,12 @@ +{ config, ... }: + +{ + time = "2025-12-12T19:20:28+00:00"; + condition = config.xsession.windowManager.herbstluftwm.enable; + message = '' + It is now possible to disable the `herbstclient` alias in the autostart + script by setting `xsession.windowManagers.herbsluftwm.enableAlias = false`. + This makes it possible to use the `herbstclient` command in bash functions, + though may cause flickering while the autostart script runs. + ''; +} diff --git a/modules/services/window-managers/herbstluftwm.nix b/modules/services/window-managers/herbstluftwm.nix index c004564b..3ec19304 100644 --- a/modules/services/window-managers/herbstluftwm.nix +++ b/modules/services/window-managers/herbstluftwm.nix @@ -119,6 +119,24 @@ in {file}`$XDG_CONFIG_HOME/herbstluftwm/autostart`. ''; }; + + enableAlias = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Set an alias for the {command}`herbstclient` command in the + {file}`autostart` script that only stores its arguments and executes + them all at once at the end of the {file}`autostart` script. + + This reduces the amount of flickering you get while all options are + being applied and improves the performance. + + On the other hand, this makes it more difficult to write bash functions + that call {command}`herbstclient`. You can work around this by calling + {command}`command herbstclient` in your functions to still get some of + the benefits of enabling this alias. + ''; + }; }; config = lib.mkIf cfg.enable { @@ -131,11 +149,13 @@ in xsession.windowManager.command = "${cfg.package}/bin/herbstluftwm --locked"; xdg.configFile."herbstluftwm/autostart".source = pkgs.writeShellScript "herbstluftwm-autostart" '' - shopt -s expand_aliases + ${lib.optionalString cfg.enableAlias '' + shopt -s expand_aliases - # shellcheck disable=SC2142 - alias herbstclient='set -- "$@" ";"' - set -- + # shellcheck disable=SC2142 + alias herbstclient='set -- "$@" ";"' + set -- + ''} herbstclient emit_hook reload @@ -169,7 +189,9 @@ in herbstclient unlock - ${cfg.package}/bin/herbstclient chain ";" "$@" + ${lib.optionalString cfg.enableAlias '' + ${cfg.package}/bin/herbstclient chain ";" "$@" + ''} ''; }; } diff --git a/tests/modules/services/herbstluftwm/default.nix b/tests/modules/services/herbstluftwm/default.nix index 52d34497..0bad877e 100644 --- a/tests/modules/services/herbstluftwm/default.nix +++ b/tests/modules/services/herbstluftwm/default.nix @@ -3,4 +3,5 @@ lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { herbstluftwm-simple-config = ./herbstluftwm-simple-config.nix; herbstluftwm-no-tags = ./herbstluftwm-no-tags.nix; + herbstluftwm-alias-disabled = ./herbstluftwm-alias-disabled.nix; } diff --git a/tests/modules/services/herbstluftwm/herbstluftwm-alias-disabled-autostart b/tests/modules/services/herbstluftwm/herbstluftwm-alias-disabled-autostart new file mode 100644 index 00000000..7368c7a7 --- /dev/null +++ b/tests/modules/services/herbstluftwm/herbstluftwm-alias-disabled-autostart @@ -0,0 +1,47 @@ +#!/nix/store/00000000000000000000000000000000-bash/bin/bash + + +herbstclient emit_hook reload + +# Reset everything. +herbstclient attr theme.tiling.reset 1 +herbstclient attr theme.floating.reset 1 +herbstclient keyunbind --all +herbstclient mouseunbind --all +herbstclient unrule --all + +herbstclient set always_show_frame true +herbstclient set default_frame_layout max +herbstclient set frame_bg_active_color '#000000' +herbstclient set frame_gap 12 +herbstclient set frame_padding -12 + +for tag in 1 'with space' 'wə1rd#ch@rs'\'''; do + herbstclient add "$tag" +done + +if @herbstluftwm@/bin/herbstclient object_tree tags.by-name.default &>/dev/null; then + herbstclient use 1 + herbstclient merge_tag default 1 +fi + + +herbstclient keybind Mod4-1 use 1 +herbstclient keybind Mod4-2 use 2 +herbstclient keybind Mod4-Alt-Tab cycle -1 +herbstclient keybind Mod4-Tab cycle 1 + +herbstclient mousebind Mod4-B1 move +herbstclient mousebind Mod4-B3 resize + +herbstclient rule focus=on +herbstclient rule windowtype~'_NET_WM_WINDOW_TYPE_(DIALOG|UTILITY|SPLASH)' focus=on pseudotile=on +herbstclient rule class~'[Pp]inentry' instance=pinentry focus=on floating=on floatplacement=center keys_inactive='.*' + +herbstclient use 1 + + +herbstclient unlock + + + diff --git a/tests/modules/services/herbstluftwm/herbstluftwm-alias-disabled.nix b/tests/modules/services/herbstluftwm/herbstluftwm-alias-disabled.nix new file mode 100644 index 00000000..0e9f8f96 --- /dev/null +++ b/tests/modules/services/herbstluftwm/herbstluftwm-alias-disabled.nix @@ -0,0 +1,49 @@ +{ lib, pkgs, ... }: + +{ + xsession.windowManager.herbstluftwm = { + enable = true; + settings = { + always_show_frame = true; + default_frame_layout = "max"; + frame_bg_active_color = "#000000"; + frame_gap = 12; + frame_padding = -12; + }; + keybinds = { + "Mod4-1" = "use 1"; + "Mod4-2" = "use 2"; + "Mod4-Tab" = "cycle 1"; + "Mod4-Alt-Tab" = "cycle -1"; + }; + mousebinds = { + "Mod4-B1" = "move"; + "Mod4-B3" = "resize"; + }; + rules = [ + "focus=on" + "windowtype~'_NET_WM_WINDOW_TYPE_(DIALOG|UTILITY|SPLASH)' focus=on pseudotile=on" + "class~'[Pp]inentry' instance=pinentry focus=on floating=on floatplacement=center keys_inactive='.*'" + ]; + tags = [ + "1" + "with space" + "wə1rd#ch@rs'" + ]; + extraConfig = '' + herbstclient use 1 + ''; + enableAlias = false; + }; + + test.stubs.herbstluftwm = { }; + + nmt.script = '' + autostart=home-files/.config/herbstluftwm/autostart + assertFileExists "$autostart" + assertFileIsExecutable "$autostart" + + normalizedAutostart=$(normalizeStorePaths "$autostart") + assertFileContent "$normalizedAutostart" ${./herbstluftwm-alias-disabled-autostart} + ''; +} diff --git a/tests/modules/services/herbstluftwm/herbstluftwm-no-tags-autostart b/tests/modules/services/herbstluftwm/herbstluftwm-no-tags-autostart index 9e8cd78a..08c35d13 100644 --- a/tests/modules/services/herbstluftwm/herbstluftwm-no-tags-autostart +++ b/tests/modules/services/herbstluftwm/herbstluftwm-no-tags-autostart @@ -5,6 +5,7 @@ shopt -s expand_aliases alias herbstclient='set -- "$@" ";"' set -- + herbstclient emit_hook reload # Reset everything. @@ -30,3 +31,4 @@ herbstclient unlock @herbstluftwm@/bin/herbstclient chain ";" "$@" + diff --git a/tests/modules/services/herbstluftwm/herbstluftwm-simple-config-autostart b/tests/modules/services/herbstluftwm/herbstluftwm-simple-config-autostart index 85ef45b1..0c608a3c 100644 --- a/tests/modules/services/herbstluftwm/herbstluftwm-simple-config-autostart +++ b/tests/modules/services/herbstluftwm/herbstluftwm-simple-config-autostart @@ -5,6 +5,7 @@ shopt -s expand_aliases alias herbstclient='set -- "$@" ";"' set -- + herbstclient emit_hook reload # Reset everything. @@ -49,3 +50,4 @@ herbstclient unlock @herbstluftwm@/bin/herbstclient chain ";" "$@" + From 07d79726f172d15b796cd81433acdde1c7577aa0 Mon Sep 17 00:00:00 2001 From: Benedikt Rips Date: Sun, 23 Nov 2025 15:17:49 +0100 Subject: [PATCH 51/80] less: enable options to be specified multiple times --- modules/programs/less.nix | 13 ++++++++----- .../programs/less/custom-options-and-config.nix | 7 +++++-- tests/modules/programs/less/custom-options.nix | 7 +++++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/modules/programs/less.nix b/modules/programs/less.nix index 1a8c5fb7..c9296928 100644 --- a/modules/programs/less.nix +++ b/modules/programs/less.nix @@ -37,11 +37,14 @@ in options = lib.mkOption { type = with lib.types; - attrsOf (oneOf [ - bool - int - str - ]); + let + scalar = oneOf [ + bool + int + str + ]; + in + attrsOf (either scalar (listOf scalar)); default = { }; description = "GNU-style options to be set via {env}`$LESS`."; example = { diff --git a/tests/modules/programs/less/custom-options-and-config.nix b/tests/modules/programs/less/custom-options-and-config.nix index 421a55cd..787c8473 100644 --- a/tests/modules/programs/less/custom-options-and-config.nix +++ b/tests/modules/programs/less/custom-options-and-config.nix @@ -12,7 +12,10 @@ in options = { RAW-CONTROL-CHARS = true; quiet = true; - wheel-lines = 3; + wheel-lines = [ + 3 + 1 + ]; }; }; @@ -20,7 +23,7 @@ in assertFileExists home-files/.config/lesskey assertFileContent home-files/.config/lesskey ${builtins.toFile "less.expected" '' #env - LESS = --RAW-CONTROL-CHARS --quiet --wheel-lines 3 + LESS = --RAW-CONTROL-CHARS --quiet --wheel-lines 3 --wheel-lines 1 ${config}''} ''; diff --git a/tests/modules/programs/less/custom-options.nix b/tests/modules/programs/less/custom-options.nix index 2e97f7d6..81abfd96 100644 --- a/tests/modules/programs/less/custom-options.nix +++ b/tests/modules/programs/less/custom-options.nix @@ -4,7 +4,10 @@ options = { RAW-CONTROL-CHARS = true; quiet = true; - wheel-lines = 3; + wheel-lines = [ + 3 + 1 + ]; }; }; @@ -12,7 +15,7 @@ assertFileExists home-files/.config/lesskey assertFileContent home-files/.config/lesskey ${builtins.toFile "lesskey.expected" '' #env - LESS = --RAW-CONTROL-CHARS --quiet --wheel-lines 3 + LESS = --RAW-CONTROL-CHARS --quiet --wheel-lines 3 --wheel-lines 1 ''} ''; } From d787ec69c3216ea33be1c0424fe65cb23aa8fb31 Mon Sep 17 00:00:00 2001 From: Benedikt Rips Date: Sun, 23 Nov 2025 15:42:16 +0100 Subject: [PATCH 52/80] less: list options in correct order --- modules/programs/less.nix | 11 +++---- .../programs/less/correct-option-order.nix | 30 +++++++++++++++++++ tests/modules/programs/less/default.nix | 1 + 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 tests/modules/programs/less/correct-option-order.nix diff --git a/modules/programs/less.nix b/modules/programs/less.nix index c9296928..60a67e24 100644 --- a/modules/programs/less.nix +++ b/modules/programs/less.nix @@ -43,10 +43,11 @@ in int str ]; + attrs = attrsOf (either scalar (listOf scalar)); in - attrsOf (either scalar (listOf scalar)); - default = { }; - description = "GNU-style options to be set via {env}`$LESS`."; + coercedTo attrs (lib.cli.toGNUCommandLine { }) (listOf str); + default = [ ]; + description = "Options to be set via {env}`$LESS`."; example = { RAW-CONTROL-CHARS = true; quiet = true; @@ -61,10 +62,10 @@ in xdg.configFile."lesskey" = lib.mkIf (cfg.config != "") { text = cfg.config; }; - programs.less.config = lib.mkIf (cfg.options != { }) ( + programs.less.config = lib.mkIf (cfg.options != [ ]) ( lib.mkBefore '' #env - LESS = ${lib.cli.toGNUCommandLineShell { } cfg.options} + LESS = ${lib.concatStringsSep " " cfg.options} '' ); }; diff --git a/tests/modules/programs/less/correct-option-order.nix b/tests/modules/programs/less/correct-option-order.nix new file mode 100644 index 00000000..6376898d --- /dev/null +++ b/tests/modules/programs/less/correct-option-order.nix @@ -0,0 +1,30 @@ +{ lib, ... }: + +{ + programs.less = { + enable = true; + options = lib.mkMerge [ + { + quiet = true; + use-color = true; + } + (lib.mkAfter { + color = [ + "HkK" # header: gray + "Mkb" # marks: blue + ]; + }) + (lib.mkOrder 2000 { + prompt = "s%f"; + }) + ]; + }; + + nmt.script = '' + assertFileExists home-files/.config/lesskey + assertFileContent home-files/.config/lesskey ${builtins.toFile "lesskey.expected" '' + #env + LESS = --quiet --use-color --color HkK --color Mkb --prompt s%f + ''} + ''; +} diff --git a/tests/modules/programs/less/default.nix b/tests/modules/programs/less/default.nix index 675c1a6c..08bd2e07 100644 --- a/tests/modules/programs/less/default.nix +++ b/tests/modules/programs/less/default.nix @@ -1,4 +1,5 @@ { + less-correct-option-order = ./correct-option-order.nix; less-custom-config = ./custom-config.nix; less-custom-options = ./custom-options.nix; less-custom-options-and-config = ./custom-options-and-config.nix; From 58bf3ecb2d0bba7bdf363fc8a6c4d49b4d509d03 Mon Sep 17 00:00:00 2001 From: will Date: Tue, 30 Sep 2025 20:31:20 +1000 Subject: [PATCH 53/80] modules/services/colima: init --- .../misc/news/2025/10/2025-10-15_10-44-58.nix | 9 + modules/services/colima.nix | 266 ++++++++++++++++++ .../colima/darwin/colima-custom-settings.nix | 39 +++ .../colima/darwin/colima-default-config.nix | 61 ++++ .../darwin/custom-settings-expected.yaml | 4 + .../services/colima/darwin/default.nix | 4 + .../colima/darwin/expected-agent.plist | 30 ++ tests/modules/services/colima/default.nix | 3 + .../colima/linux/colima-default-config.nix | 50 ++++ .../modules/services/colima/linux/default.nix | 3 + .../colima/linux/expected-service.service | 19 ++ 11 files changed, 488 insertions(+) create mode 100644 modules/misc/news/2025/10/2025-10-15_10-44-58.nix create mode 100644 modules/services/colima.nix create mode 100644 tests/modules/services/colima/darwin/colima-custom-settings.nix create mode 100644 tests/modules/services/colima/darwin/colima-default-config.nix create mode 100644 tests/modules/services/colima/darwin/custom-settings-expected.yaml create mode 100644 tests/modules/services/colima/darwin/default.nix create mode 100644 tests/modules/services/colima/darwin/expected-agent.plist create mode 100644 tests/modules/services/colima/default.nix create mode 100644 tests/modules/services/colima/linux/colima-default-config.nix create mode 100644 tests/modules/services/colima/linux/default.nix create mode 100644 tests/modules/services/colima/linux/expected-service.service diff --git a/modules/misc/news/2025/10/2025-10-15_10-44-58.nix b/modules/misc/news/2025/10/2025-10-15_10-44-58.nix new file mode 100644 index 00000000..94440572 --- /dev/null +++ b/modules/misc/news/2025/10/2025-10-15_10-44-58.nix @@ -0,0 +1,9 @@ +{ + time = "2025-10-14T23:44:58+00:00"; + condition = true; + message = '' + A new module is available: `services.colima` + + Colima is a tool for orchestrating container runtimes under a linux VM. + ''; +} diff --git a/modules/services/colima.nix b/modules/services/colima.nix new file mode 100644 index 00000000..da5f83f8 --- /dev/null +++ b/modules/services/colima.nix @@ -0,0 +1,266 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.services.colima; + yamlFormat = pkgs.formats.yaml { }; +in +{ + meta.maintainers = [ + lib.hm.maintainers.will-lol + ]; + + options.services.colima = { + enable = lib.mkEnableOption "Colima, a container runtime"; + + package = lib.mkPackageOption pkgs "colima" { }; + dockerPackage = lib.mkPackageOption pkgs "docker" { + extraDescription = "Used by colima to activate profiles. Not needed if no profile is set to isActive."; + }; + perlPackage = lib.mkPackageOption pkgs "perl" { + extraDescription = "Used by colima during image download for the shasum command."; + }; + sshPackage = lib.mkPackageOption pkgs "openssh" { + extraDescription = "Used by colima to manage the vm."; + }; + coreutilsPackage = lib.mkPackageOption pkgs "coreutils" { + extraDescription = "Used in various ways by colima."; + }; + curlPackage = lib.mkPackageOption pkgs "curl" { + extraDescription = "Used by colima to donwload images."; + }; + bashPackage = lib.mkPackageOption pkgs "bashNonInteractive" { + extraDescription = "Used by colima's internal scripts."; + }; + + profiles = lib.mkOption { + default = { + default = { + isActive = true; + isService = true; + }; + }; + description = '' + Profiles allow multiple colima configurations. The default profile is active by default. + If you have used colima before, you may need to delete existing configuration using `colima delete` or use a different profile. + + Note that removing a configured profile will not delete the corresponding Colima instance. + You will need to manually run `colima delete ` to remove the instance and release resources. + ''; + example = '' + { + default = { + isActive = true; + isService = true; + }; + rosetta = { + isService = true; + settings.rosetta = true; + }; + powerful = { + settings.cpu = 8; + }; + }; + ''; + type = lib.types.attrsOf ( + lib.types.submodule ( + { name, ... }: + { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + readOnly = true; + description = "The profile's name."; + }; + + isService = lib.mkOption { + type = lib.types.bool; + default = false; + example = true; + description = '' + Whether this profile will run as a service. + ''; + }; + + isActive = lib.mkOption { + type = lib.types.bool; + default = false; + example = true; + description = '' + Whether to set this profile as: + - active docker context + - active kubernetes context + - active incus remote + Exactly one or zero profiles should have this option set. + ''; + }; + + logFile = lib.mkOption { + type = lib.types.path; + default = "${config.home.homeDirectory}/.local/state/colima-${name}.log"; + defaultText = lib.literalExpression "\${config.home.homeDirectory}/.local/state/colima-\${name}.log"; + description = "Combined stdout and stderr log file for the Colima service."; + }; + + settings = lib.mkOption { + inherit (yamlFormat) type; + default = { }; + description = "Colima configuration settings, see or run `colima template`."; + example = '' + { + cpu = 2; + disk = 100; + memory = 2; + arch = "host"; + runtime = "docker"; + hostname = null; + kubernetes = { + enabled = false; + version = "v1.33.3+k3s1"; + k3sArgs = [ "--disable=traefik" ]; + port = 0; + }; + autoActivate = true; + network = { + address = false; + mode = "shared"; + interface = "en0"; + preferredRoute = false; + dns = [ ]; + dnsHosts = { + "host.docker.internal" = "host.lima.internal"; + }; + hostAddresses = false; + }; + forwardAgent = false; + docker = { }; + vmType = "qemu"; + portForwarder = "ssh"; + rosetta = false; + binfmt = true; + nestedVirtualization = false; + mountType = "sshfs"; + mountInotify = false; + cpuType = "host"; + provision = [ ]; + sshConfig = true; + sshPort = 0; + mounts = [ ]; + diskImage = ""; + rootDisk = 20; + env = { }; + } + ''; + }; + }; + } + ) + ); + }; + }; + + config = lib.mkIf cfg.enable ({ + assertions = [ + { + assertion = (lib.count (p: p.isActive) (lib.attrValues cfg.profiles)) <= 1; + message = "Only one Colima profile can be active at a time."; + } + ]; + + home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + + home.file = lib.mkMerge ( + lib.mapAttrsToList (profileName: profile: { + ".colima/${profileName}/colima.yaml" = { + source = yamlFormat.generate "colima.yaml" profile.settings; + }; + }) (lib.filterAttrs (name: profile: profile.settings != { }) cfg.profiles) + ); + + programs.docker-cli.settings.currentContext = + let + activeProfile = lib.findFirst (p: p.isActive) null (lib.attrValues cfg.profiles); + in + lib.mkIf (activeProfile != null) ( + if activeProfile.name != "default" then "colima-${activeProfile.name}" else "colima" + ); + + launchd.agents = lib.mkIf pkgs.stdenv.isDarwin ( + lib.mapAttrs' ( + name: profile: + lib.nameValuePair "colima-${name}" { + enable = true; + config = { + ProgramArguments = [ + "${lib.getExe cfg.package}" + "start" + name + "-f" + "--activate=${if profile.isActive then "true" else "false"}" + "--save-config=false" + ]; + KeepAlive = true; + RunAtLoad = true; + EnvironmentVariables.PATH = lib.makeBinPath [ + cfg.package + cfg.perlPackage + cfg.dockerPackage + cfg.sshPackage + cfg.coreutilsPackage + cfg.curlPackage + cfg.bashPackage + pkgs.darwin.DarwinTools + ]; + StandardOutPath = profile.logFile; + StandardErrorPath = profile.logFile; + }; + } + ) (lib.filterAttrs (_: p: p.isService) cfg.profiles) + ); + + systemd.user.services = lib.mkIf pkgs.stdenv.isLinux ( + lib.mapAttrs' ( + name: profile: + lib.nameValuePair "colima-${name}" { + Unit = { + Description = "Colima container runtime (${name} profile)"; + After = [ "network-online.target" ]; + Wants = [ "network-online.target" ]; + }; + Service = { + ExecStart = '' + ${lib.getExe cfg.package} start ${name} \ + -f \ + --activate=${if profile.isActive then "true" else "false"} \ + --save-config=false + ''; + Restart = "always"; + RestartSec = 2; + Environment = [ + "PATH=${ + lib.makeBinPath [ + cfg.package + cfg.perlPackage + cfg.dockerPackage + cfg.sshPackage + cfg.coreutilsPackage + cfg.curlPackage + cfg.bashPackage + ] + }" + ]; + StandardOutput = "append:${profile.logFile}"; + StandardError = "append:${profile.logFile}"; + }; + Install = { + WantedBy = [ "default.target" ]; + }; + } + ) (lib.filterAttrs (_: p: p.isService) cfg.profiles) + ); + }); +} diff --git a/tests/modules/services/colima/darwin/colima-custom-settings.nix b/tests/modules/services/colima/darwin/colima-custom-settings.nix new file mode 100644 index 00000000..3c908c0a --- /dev/null +++ b/tests/modules/services/colima/darwin/colima-custom-settings.nix @@ -0,0 +1,39 @@ +{ + config, + lib, + pkgs, + ... +}: + +{ + services.colima = { + enable = true; + package = config.lib.test.mkStubPackage { + name = "colima"; + outPath = "@colima@"; + }; + dockerPackage = config.lib.test.mkStubPackage { + name = "docker"; + outPath = "@docker@"; + }; + perlPackage = config.lib.test.mkStubPackage { + name = "perl"; + outPath = "@perl@"; + }; + sshPackage = config.lib.test.mkStubPackage { + name = "openssh"; + outPath = "@openssh@"; + }; + + profiles.default.settings = { + cpu = 4; + memory = 8; + kubernetes.enabled = true; + }; + }; + + nmt.script = '' + assertFileExists home-files/.colima/default/colima.yaml + assertFileContent home-files/.colima/default/colima.yaml ${./custom-settings-expected.yaml} + ''; +} diff --git a/tests/modules/services/colima/darwin/colima-default-config.nix b/tests/modules/services/colima/darwin/colima-default-config.nix new file mode 100644 index 00000000..fbc29de8 --- /dev/null +++ b/tests/modules/services/colima/darwin/colima-default-config.nix @@ -0,0 +1,61 @@ +{ + config, + lib, + pkgs, + ... +}: + +{ + nixpkgs.overlays = [ + (self: super: { + darwin = super.darwin // { + DarwinTools = config.lib.test.mkStubPackage { + name = "DarwinTools"; + outPath = "@DarwinTools@"; + }; + }; + }) + ]; + + services.colima = { + enable = true; + package = config.lib.test.mkStubPackage { + name = "colima"; + outPath = "@colima@"; + }; + dockerPackage = config.lib.test.mkStubPackage { + name = "docker"; + outPath = "@docker@"; + }; + perlPackage = config.lib.test.mkStubPackage { + name = "perl"; + outPath = "@perl@"; + }; + sshPackage = config.lib.test.mkStubPackage { + name = "openssh"; + outPath = "@openssh@"; + }; + coreutilsPackage = config.lib.test.mkStubPackage { + name = "coreutils"; + outPath = "@coreutils@"; + }; + curlPackage = config.lib.test.mkStubPackage { + name = "curl"; + outPath = "@curl@"; + }; + bashPackage = config.lib.test.mkStubPackage { + name = "bashNonInteractive"; + outPath = "@bashNonInteractive@"; + }; + }; + + nmt.script = '' + assertPathNotExists home-files/.colima/default/colima.yaml + + serviceFile=LaunchAgents/org.nix-community.home.colima-default.plist + + assertFileExists "$serviceFile" + + assertFileContent "$serviceFile" ${./expected-agent.plist} + ''; +} diff --git a/tests/modules/services/colima/darwin/custom-settings-expected.yaml b/tests/modules/services/colima/darwin/custom-settings-expected.yaml new file mode 100644 index 00000000..4a166598 --- /dev/null +++ b/tests/modules/services/colima/darwin/custom-settings-expected.yaml @@ -0,0 +1,4 @@ +cpu: 4 +kubernetes: + enabled: true +memory: 8 diff --git a/tests/modules/services/colima/darwin/default.nix b/tests/modules/services/colima/darwin/default.nix new file mode 100644 index 00000000..1ce901bb --- /dev/null +++ b/tests/modules/services/colima/darwin/default.nix @@ -0,0 +1,4 @@ +{ + "colima-default-config" = ./colima-default-config.nix; + "colima-custom-settings" = ./colima-custom-settings.nix; +} diff --git a/tests/modules/services/colima/darwin/expected-agent.plist b/tests/modules/services/colima/darwin/expected-agent.plist new file mode 100644 index 00000000..e4e43477 --- /dev/null +++ b/tests/modules/services/colima/darwin/expected-agent.plist @@ -0,0 +1,30 @@ + + + + + EnvironmentVariables + + PATH + @colima@/bin:@perl@/bin:@docker@/bin:@openssh@/bin:@coreutils@/bin:@curl@/bin:@bashNonInteractive@/bin:@DarwinTools@/bin + + KeepAlive + + Label + org.nix-community.home.colima-default + ProgramArguments + + @colima@/bin/colima + start + default + -f + --activate=true + --save-config=false + + RunAtLoad + + StandardErrorPath + /home/hm-user/.local/state/colima-default.log + StandardOutPath + /home/hm-user/.local/state/colima-default.log + + \ No newline at end of file diff --git a/tests/modules/services/colima/default.nix b/tests/modules/services/colima/default.nix new file mode 100644 index 00000000..4b1206a2 --- /dev/null +++ b/tests/modules/services/colima/default.nix @@ -0,0 +1,3 @@ +{ lib, pkgs, ... }: +(lib.optionalAttrs pkgs.stdenv.hostPlatform.isDarwin (import ./darwin/default.nix)) +// (lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux (import ./linux/default.nix)) diff --git a/tests/modules/services/colima/linux/colima-default-config.nix b/tests/modules/services/colima/linux/colima-default-config.nix new file mode 100644 index 00000000..940f997f --- /dev/null +++ b/tests/modules/services/colima/linux/colima-default-config.nix @@ -0,0 +1,50 @@ +{ + config, + lib, + pkgs, + ... +}: + +{ + services.colima = { + enable = true; + package = config.lib.test.mkStubPackage { + name = "colima"; + outPath = "@colima@"; + }; + dockerPackage = config.lib.test.mkStubPackage { + name = "docker"; + outPath = "@docker@"; + }; + perlPackage = config.lib.test.mkStubPackage { + name = "perl"; + outPath = "@perl@"; + }; + sshPackage = config.lib.test.mkStubPackage { + name = "openssh"; + outPath = "@openssh@"; + }; + coreutilsPackage = config.lib.test.mkStubPackage { + name = "coreutils"; + outPath = "@coreutils@"; + }; + curlPackage = config.lib.test.mkStubPackage { + name = "curl"; + outPath = "@curl@"; + }; + bashPackage = config.lib.test.mkStubPackage { + name = "bashNonInteractive"; + outPath = "@bashNonInteractive@"; + }; + }; + + nmt.script = '' + assertPathNotExists home-files/.colima/default/colima.yaml + + assertFileExists home-files/.config/systemd/user/colima-default.service + + assertFileContent \ + home-files/.config/systemd/user/colima-default.service \ + ${./expected-service.service} + ''; +} diff --git a/tests/modules/services/colima/linux/default.nix b/tests/modules/services/colima/linux/default.nix new file mode 100644 index 00000000..ee233831 --- /dev/null +++ b/tests/modules/services/colima/linux/default.nix @@ -0,0 +1,3 @@ +{ + "colima-default-config" = ./colima-default-config.nix; +} diff --git a/tests/modules/services/colima/linux/expected-service.service b/tests/modules/services/colima/linux/expected-service.service new file mode 100644 index 00000000..82dacb73 --- /dev/null +++ b/tests/modules/services/colima/linux/expected-service.service @@ -0,0 +1,19 @@ +[Install] +WantedBy=default.target + +[Service] +Environment=PATH=@colima@/bin:@perl@/bin:@docker@/bin:@openssh@/bin:@coreutils@/bin:@curl@/bin:@bashNonInteractive@/bin +ExecStart=@colima@/bin/colima start default \ + -f \ + --activate=true \ + --save-config=false + +Restart=always +RestartSec=2 +StandardError=append:/home/hm-user/.local/state/colima-default.log +StandardOutput=append:/home/hm-user/.local/state/colima-default.log + +[Unit] +After=network-online.target +Description=Colima container runtime (default profile) +Wants=network-online.target From 8315c1544f383b791a3115c9959d1f27920e8320 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 09:24:13 +0000 Subject: [PATCH 54/80] maintainers: update all-maintainers.nix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automated update of the master maintainers list combining: - Home Manager specific maintainers from modules/lib/maintainers.nix - Nixpkgs maintainers referenced in Home Manager modules **Added:** 0 maintainers **Removed:** 0 maintainers **Total:** 282 → 282 maintainers Generated by: lib/python/generate-all-maintainers.py --- all-maintainers.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/all-maintainers.nix b/all-maintainers.nix index df645916..48f96da4 100644 --- a/all-maintainers.nix +++ b/all-maintainers.nix @@ -361,6 +361,11 @@ email = "nixpkgs@perchun.it"; github = "PerchunPak"; githubId = 68118654; + keys = [ + { + fingerprint = "BBB5 1142 959D 8549 A3D2 F6C5 313F 67D1 EAB7 70F9"; + } + ]; name = "Perchun Pak"; source = "nixpkgs"; }; From 6cdf765eede13e27771b4066a49fe24e31498f94 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Sun, 14 Dec 2025 04:25:13 +0000 Subject: [PATCH 55/80] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/addf7cf5f383a3101ecfba091b98d0a1263dc9b8?narHash=sha256-hM20uyap1a0M9d344I692r%2Bik4gTMyj60cQWO%2BhAYP8%3D' (2025-12-08) → 'github:NixOS/nixpkgs/2fbfb1d73d239d2402a8fe03963e37aab15abe8b?narHash=sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0%3D' (2025-12-11) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 8a30674d..9726a9a0 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1765186076, - "narHash": "sha256-hM20uyap1a0M9d344I692r+ik4gTMyj60cQWO+hAYP8=", + "lastModified": 1765472234, + "narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "addf7cf5f383a3101ecfba091b98d0a1263dc9b8", + "rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b", "type": "github" }, "original": { From 3a92ffa1924bd8180982d316845c7fd082143f65 Mon Sep 17 00:00:00 2001 From: DaRacci Date: Sun, 14 Dec 2025 21:58:05 +1100 Subject: [PATCH 56/80] home-manager: expose all attributes returned by evalModules This change brings the homeConfigurations output inline with how the nixosConfiguration output presents its attributes. The primary purpose is for exposing the graph attribute however there is no downside to exposing the rest along with it to prevent needing to add each desired attribute individually in the future if more are added. --- modules/default.nix | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/default.nix b/modules/default.nix index 9bc43df1..10e6e7a3 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -57,9 +57,8 @@ let let module = moduleChecks rawModule; in - { - inherit (module) options config; - + module + // { activationPackage = module.config.home.activationPackage; # For backwards compatibility. Please use activationPackage instead. From f575cb24f6a949e047e3e7244314969b9582e4bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 01:01:13 +0000 Subject: [PATCH 57/80] ci: bump korthout/backport-action from 3 to 4 Bumps [korthout/backport-action](https://github.com/korthout/backport-action) from 3 to 4. - [Release notes](https://github.com/korthout/backport-action/releases) - [Commits](https://github.com/korthout/backport-action/compare/v3...v4) --- updated-dependencies: - dependency-name: korthout/backport-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index f6287a69..514ba1f3 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -37,7 +37,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs id: backport - uses: korthout/backport-action@v3 + uses: korthout/backport-action@v4 with: # See https://github.com/korthout/backport-action#inputs github_token: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }} From 09de9577d47d8bffb11c449b6a3d24e32ac16c99 Mon Sep 17 00:00:00 2001 From: Anton Mosich Date: Tue, 16 Dec 2025 05:40:45 +0100 Subject: [PATCH 58/80] pimsync: add Install.WantedBy for default.target (#8344) --- modules/services/pimsync.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/services/pimsync.nix b/modules/services/pimsync.nix index 99eac8d1..b2cda644 100644 --- a/modules/services/pimsync.nix +++ b/modules/services/pimsync.nix @@ -43,6 +43,7 @@ in Description = "pimsync calendar and contacts synchronization"; PartOf = [ "network-online.target" ]; }; + Install.WantedBy = [ "default.target" ]; Service = { # TODO: make use of the readiness notification Type = "simple"; From 22202ff0d86c176079791dbee1e90c473ef5c209 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 04:21:45 +0000 Subject: [PATCH 59/80] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/2fbfb1d73d239d2402a8fe03963e37aab15abe8b?narHash=sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0%3D' (2025-12-11) → 'github:NixOS/nixpkgs/1306659b587dc277866c7b69eb97e5f07864d8c4?narHash=sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4%3D' (2025-12-15) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 9726a9a0..7009e57c 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1765472234, - "narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=", + "lastModified": 1765779637, + "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b", + "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", "type": "github" }, "original": { From 89c9508bbe9b40d36b3dc206c2483ef176f15173 Mon Sep 17 00:00:00 2001 From: Benedikt Rips Date: Wed, 19 Nov 2025 23:31:39 +0100 Subject: [PATCH 60/80] ssh-agent: set $SSH_AUTH_SOCK in non-interactive shells Since PR #8099, the module sets `$SSH_AUTH_SOCK` through shells' options for interactive shell initialization instead of `home.sessionVariablesExtra`. The replacement was not faithful, however, since `home.sessionVariablesExtra` is sourced also in non-interactive shells. With this commit, the shells' profile options (where `home.sessionVariablesExtra` is sourced) are used to set `$SSH_AUTH_SOCK`. Fixes #8129. --- modules/services/ssh-agent.nix | 12 +++++------- .../services/ssh-agent/darwin/bash-integration.nix | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/modules/services/ssh-agent.nix b/modules/services/ssh-agent.nix index 35714e2b..8bb68a40 100644 --- a/modules/services/ssh-agent.nix +++ b/modules/services/ssh-agent.nix @@ -84,13 +84,11 @@ in ''; in { - bash.initExtra = lib.mkIf cfg.enableBashIntegration bashIntegration; - - zsh.initContent = lib.mkIf cfg.enableZshIntegration bashIntegration; - - fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration fishIntegration; - - nushell.extraConfig = lib.mkIf cfg.enableNushellIntegration nushellIntegration; + # $SSH_AUTH_SOCK has to be set early since other tools rely on it + bash.profileExtra = lib.mkIf cfg.enableBashIntegration (lib.mkOrder 900 bashIntegration); + fish.shellInit = lib.mkIf cfg.enableFishIntegration (lib.mkOrder 900 fishIntegration); + nushell.extraConfig = lib.mkIf cfg.enableNushellIntegration (lib.mkOrder 900 nushellIntegration); + zsh.envExtra = lib.mkIf cfg.enableZshIntegration (lib.mkOrder 900 bashIntegration); }; } diff --git a/tests/modules/services/ssh-agent/darwin/bash-integration.nix b/tests/modules/services/ssh-agent/darwin/bash-integration.nix index b4fb9109..2341462a 100644 --- a/tests/modules/services/ssh-agent/darwin/bash-integration.nix +++ b/tests/modules/services/ssh-agent/darwin/bash-integration.nix @@ -8,7 +8,7 @@ nmt.script = '' assertFileContains \ - home-files/.bashrc \ + home-files/.profile \ 'export SSH_AUTH_SOCK=$(@getconf-system_cmds@/bin/getconf DARWIN_USER_TEMP_DIR)/ssh-agent' ''; } From 9bf54edf10f033d18c8ea3028cf45f9f8d3571d8 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 19 Dec 2025 12:39:47 -0600 Subject: [PATCH 61/80] claude-code: add rules support Support the `rules` directory for modular memory files. Signed-off-by: Austin Horstman --- modules/programs/claude-code.nix | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/modules/programs/claude-code.nix b/modules/programs/claude-code.nix index f1ef03fc..8d5a4036 100644 --- a/modules/programs/claude-code.nix +++ b/modules/programs/claude-code.nix @@ -197,6 +197,47 @@ in }; }; + rules = lib.mkOption { + type = lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path); + default = { }; + description = '' + Modular rule files for Claude Code. + The attribute name becomes the rule filename, and the value is either: + - Inline content as a string + - A path to a file containing the rule content + Rules are stored in .claude/rules/ directory. + All markdown files in .claude/rules/ are automatically loaded as project memory. + ''; + example = lib.literalExpression '' + { + code-style = ''' + # Code Style Guidelines + + - Use consistent formatting + - Follow language conventions + '''; + testing = ''' + # Testing Conventions + + - Write tests for all new features + - Maintain test coverage above 80% + '''; + security = ./rules/security.md; + } + ''; + }; + + rulesDir = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = '' + Path to a directory containing rule files for Claude Code. + Rule files from this directory will be symlinked to .claude/rules/. + All markdown files in this directory are automatically loaded as project memory. + ''; + example = lib.literalExpression "./rules"; + }; + agentsDir = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; @@ -325,6 +366,10 @@ in assertion = !(cfg.memory.text != null && cfg.memory.source != null); message = "Cannot specify both `programs.claude-code.memory.text` and `programs.claude-code.memory.source`"; } + { + assertion = !(cfg.rules != { } && cfg.rulesDir != null); + message = "Cannot specify both `programs.claude-code.rules` and `programs.claude-code.rulesDir`"; + } { assertion = !(cfg.agents != { } && cfg.agentsDir != null); message = "Cannot specify both `programs.claude-code.agents` and `programs.claude-code.agentsDir`"; @@ -386,6 +431,11 @@ in if cfg.memory.text != null then { text = cfg.memory.text; } else { source = cfg.memory.source; } ); + ".claude/rules" = lib.mkIf (cfg.rulesDir != null) { + source = cfg.rulesDir; + recursive = true; + }; + ".claude/agents" = lib.mkIf (cfg.agentsDir != null) { source = cfg.agentsDir; recursive = true; @@ -406,6 +456,12 @@ in recursive = true; }; } + // lib.mapAttrs' ( + name: content: + lib.nameValuePair ".claude/rules/${name}.md" ( + if lib.isPath content then { source = content; } else { text = content; } + ) + ) cfg.rules // lib.mapAttrs' ( name: content: lib.nameValuePair ".claude/agents/${name}.md" ( From bb35f07cc95a73aacbaf1f7f46bb8a3f40f265b5 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 19 Dec 2025 12:39:53 -0600 Subject: [PATCH 62/80] tests/claude-code: add rules tests Signed-off-by: Austin Horstman --- .../modules/programs/claude-code/default.nix | 2 ++ .../programs/claude-code/rules-dir.nix | 14 +++++++++++++ .../programs/claude-code/rules-path.nix | 20 +++++++++++++++++++ .../programs/claude-code/rules/test-rule.md | 9 +++++++++ .../modules/programs/claude-code/test-rule.md | 9 +++++++++ 5 files changed, 54 insertions(+) create mode 100644 tests/modules/programs/claude-code/rules-dir.nix create mode 100644 tests/modules/programs/claude-code/rules-path.nix create mode 100644 tests/modules/programs/claude-code/rules/test-rule.md create mode 100644 tests/modules/programs/claude-code/test-rule.md diff --git a/tests/modules/programs/claude-code/default.nix b/tests/modules/programs/claude-code/default.nix index 7ff4a997..315a1975 100644 --- a/tests/modules/programs/claude-code/default.nix +++ b/tests/modules/programs/claude-code/default.nix @@ -5,6 +5,8 @@ claude-code-assertion = ./assertion.nix; claude-code-memory-management = ./memory-management.nix; claude-code-memory-from-source = ./memory-from-source.nix; + claude-code-rules-dir = ./rules-dir.nix; + claude-code-rules-path = ./rules-path.nix; claude-code-agents-dir = ./agents-dir.nix; claude-code-commands-dir = ./commands-dir.nix; claude-code-hooks-dir = ./hooks-dir.nix; diff --git a/tests/modules/programs/claude-code/rules-dir.nix b/tests/modules/programs/claude-code/rules-dir.nix new file mode 100644 index 00000000..0716b85b --- /dev/null +++ b/tests/modules/programs/claude-code/rules-dir.nix @@ -0,0 +1,14 @@ +{ + programs.claude-code = { + enable = true; + rulesDir = ./rules; + }; + + nmt.script = '' + assertFileExists home-files/.claude/rules/test-rule.md + assertLinkExists home-files/.claude/rules/test-rule.md + assertFileContent \ + home-files/.claude/rules/test-rule.md \ + ${./rules/test-rule.md} + ''; +} diff --git a/tests/modules/programs/claude-code/rules-path.nix b/tests/modules/programs/claude-code/rules-path.nix new file mode 100644 index 00000000..474280bd --- /dev/null +++ b/tests/modules/programs/claude-code/rules-path.nix @@ -0,0 +1,20 @@ +{ + programs.claude-code = { + enable = true; + rules = { + test-rule = ./test-rule.md; + inline-rule = '' + # Inline Rule + + This is an inline rule for testing. + ''; + }; + }; + + nmt.script = '' + assertFileExists home-files/.claude/rules/test-rule.md + assertFileContent home-files/.claude/rules/test-rule.md \ + ${./test-rule.md} + assertFileExists home-files/.claude/rules/inline-rule.md + ''; +} diff --git a/tests/modules/programs/claude-code/rules/test-rule.md b/tests/modules/programs/claude-code/rules/test-rule.md new file mode 100644 index 00000000..fc873169 --- /dev/null +++ b/tests/modules/programs/claude-code/rules/test-rule.md @@ -0,0 +1,9 @@ +# Test Rule from Directory + +This is a test rule loaded from a directory. +Used to verify rulesDir support functionality. + +## Best Practices + +- Write clean code +- Test thoroughly diff --git a/tests/modules/programs/claude-code/test-rule.md b/tests/modules/programs/claude-code/test-rule.md new file mode 100644 index 00000000..273fad52 --- /dev/null +++ b/tests/modules/programs/claude-code/test-rule.md @@ -0,0 +1,9 @@ +# Test Rule + +This is a test rule loaded from a file path. +Used to verify path support functionality for rules. + +## Guidelines + +- Follow test conventions +- Maintain code quality From c848303f1e0e58deafc2c6dc20c3b8caa3f7725c Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 19 Dec 2025 11:10:31 -0600 Subject: [PATCH 63/80] gemini-cli: don't force GEMINI_MODEL Default to null so user's can opt in to using this variable that will override configurations. Signed-off-by: Austin Horstman --- modules/programs/gemini-cli.nix | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/programs/gemini-cli.nix b/modules/programs/gemini-cli.nix index accfdabf..5e21543d 100644 --- a/modules/programs/gemini-cli.nix +++ b/modules/programs/gemini-cli.nix @@ -81,12 +81,12 @@ in }; defaultModel = lib.mkOption { - type = lib.types.str; - default = "gemini-2.5-pro"; + type = lib.types.nullOr lib.types.str; + default = null; example = "gemini-2.5-flash"; description = '' The default model to use for the CLI. - Will be set as $GEMINI_MODEL. + Will be set as $GEMINI_MODEL when configured. ''; }; @@ -138,7 +138,9 @@ in file.".gemini/settings.json" = lib.mkIf (cfg.settings != { }) { source = jsonFormat.generate "gemini-cli-settings.json" cfg.settings; }; - sessionVariables.GEMINI_MODEL = cfg.defaultModel; + sessionVariables = lib.mkIf (cfg.defaultModel != null) { + GEMINI_MODEL = cfg.defaultModel; + }; }; } { From 28b3622b80998db334ebf6bc87164f971c204a2f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 19 Dec 2025 11:12:36 -0600 Subject: [PATCH 64/80] tests/gemini-cli: verify defaultModel behavior Signed-off-by: Austin Horstman --- tests/modules/programs/gemini-cli/context.nix | 4 ++++ tests/modules/programs/gemini-cli/settings.nix | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/tests/modules/programs/gemini-cli/context.nix b/tests/modules/programs/gemini-cli/context.nix index f4b547ac..6fbac0ea 100644 --- a/tests/modules/programs/gemini-cli/context.nix +++ b/tests/modules/programs/gemini-cli/context.nix @@ -41,5 +41,9 @@ assertFileExists home-files/.gemini/CONTEXT.md assertFileContent home-files/.gemini/CONTEXT.md \ ${./context-additional.md} + + assertFileExists home-path/etc/profile.d/hm-session-vars.sh + assertFileNotRegex home-path/etc/profile.d/hm-session-vars.sh \ + "GEMINI_MODEL" ''; } diff --git a/tests/modules/programs/gemini-cli/settings.nix b/tests/modules/programs/gemini-cli/settings.nix index f78a14f6..80a4344a 100644 --- a/tests/modules/programs/gemini-cli/settings.nix +++ b/tests/modules/programs/gemini-cli/settings.nix @@ -1,6 +1,7 @@ { programs.gemini-cli = { enable = true; + defaultModel = "gemini-2.5-flash"; settings = { theme = "Default"; vimMode = true; @@ -28,5 +29,9 @@ ${./changelog.toml} assertFileContent home-files/.gemini/commands/git/fix.toml \ ${./fix.toml} + + assertFileExists home-path/etc/profile.d/hm-session-vars.sh + assertFileContains home-path/etc/profile.d/hm-session-vars.sh \ + 'export GEMINI_MODEL="gemini-2.5-flash"' ''; } From c764a377a0f0383e9e6a4addcafdb144823909d6 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 19 Dec 2025 14:43:08 -0600 Subject: [PATCH 65/80] gemini-cli: fix settings example Supposed to represent the settigns to pass in, not the generated json. Signed-off-by: Austin Horstman --- modules/programs/gemini-cli.nix | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/modules/programs/gemini-cli.nix b/modules/programs/gemini-cli.nix index 5e21543d..b9b0ca5e 100644 --- a/modules/programs/gemini-cli.nix +++ b/modules/programs/gemini-cli.nix @@ -21,14 +21,19 @@ in settings = lib.mkOption { inherit (jsonFormat) type; default = { }; - example = lib.literalExpression '' - { - "theme": "Default", - "vimMode": true, - "preferredEditor": "nvim", - "autoAccept": true - } - ''; + example = { + ui.theme = "Default"; + general = { + vimMode = true; + preferredEditor = "nvim"; + previewFeatures = true; + }; + ide.enabled = true; + privacy.usageStatisticsEnabled = false; + tools.autoAccept = false; + context.loadMemoryFromIncludeDirectories = true; + security.auth.selectedType = "oauth-personal"; + }; description = "JSON config for gemini-cli"; }; From ab01ea24b2c38bbd0b57015016e59e93cc8ed2e1 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 19 Dec 2025 23:18:30 -0600 Subject: [PATCH 66/80] neovim: add missing wrapper args Add support for extraName, autowrapRuntimeDeps, waylandSupport, and withPerl options in the neovim module. These options are passed to the neovim wrapper to allow for more granular configuration. Signed-off-by: Austin Horstman --- modules/programs/neovim.nix | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/modules/programs/neovim.nix b/modules/programs/neovim.nix index 271bc150..c7520c06 100644 --- a/modules/programs/neovim.nix +++ b/modules/programs/neovim.nix @@ -155,6 +155,15 @@ in ''; }; + withPerl = mkOption { + type = types.bool; + default = false; + description = '' + Enable perl provider. Set to `true` to + use Perl plugins. + ''; + }; + withRuby = mkOption { type = types.nullOr types.bool; default = true; @@ -224,6 +233,31 @@ in ''; }; + extraName = mkOption { + type = types.str; + default = ""; + description = '' + Extra name appended to the wrapper package name. + ''; + }; + + autowrapRuntimeDeps = mkOption { + type = types.bool; + default = true; + description = '' + Whether to automatically wrap the binary with the runtime dependencies of the plugins. + ''; + }; + + waylandSupport = mkOption { + type = types.bool; + default = pkgs.stdenv.isLinux; + defaultText = literalExpression "pkgs.stdenv.isLinux"; + description = '' + Whether to enable Wayland clipboard support. + ''; + }; + extraWrapperArgs = mkOption { type = with types; listOf str; default = [ ]; @@ -418,6 +452,7 @@ in extraPython3Packages withPython3 withRuby + withPerl viAlias vimAlias ; @@ -429,6 +464,11 @@ in wrappedNeovim' = pkgs.wrapNeovimUnstable cfg.package ( neovimConfig // { + inherit (cfg) + extraName + autowrapRuntimeDeps + waylandSupport + ; wrapperArgs = (lib.escapeShellArgs (neovimConfig.wrapperArgs ++ cfg.extraWrapperArgs)) + " " From 0a583021eab3418571296ba2ca7663960af21ead Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 19 Dec 2025 23:31:47 -0600 Subject: [PATCH 67/80] tests/neovim: add wrapper args test Signed-off-by: Austin Horstman --- tests/modules/programs/neovim/default.nix | 1 + .../modules/programs/neovim/wrapper-args.nix | 83 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 tests/modules/programs/neovim/wrapper-args.nix diff --git a/tests/modules/programs/neovim/default.nix b/tests/modules/programs/neovim/default.nix index d3224666..0fa15979 100644 --- a/tests/modules/programs/neovim/default.nix +++ b/tests/modules/programs/neovim/default.nix @@ -2,6 +2,7 @@ neovim-plugin-config = ./plugin-config.nix; neovim-coc-config = ./coc-config.nix; neovim-runtime = ./runtime.nix; + neovim-wrapper-args = ./wrapper-args.nix; # waiting for a nixpkgs patch neovim-no-init = ./no-init.nix; diff --git a/tests/modules/programs/neovim/wrapper-args.nix b/tests/modules/programs/neovim/wrapper-args.nix new file mode 100644 index 00000000..6e79d529 --- /dev/null +++ b/tests/modules/programs/neovim/wrapper-args.nix @@ -0,0 +1,83 @@ +{ + lib, + pkgs, + ... +}: + +let + inherit (pkgs.stdenv.hostPlatform) isLinux; + + dummyDep = pkgs.runCommand "dummy-dep" { } '' + mkdir -p $out/bin + echo "echo dummy" > $out/bin/dummy-dep-bin + chmod +x $out/bin/dummy-dep-bin + ''; + + dummyPlugin = pkgs.vimUtils.buildVimPlugin { + pname = "dummy-plugin"; + version = "1.0"; + src = pkgs.writeTextDir "plugin/dummy.vim" "\" dummy"; + runtimeDeps = [ dummyDep ]; + }; +in +{ + imports = [ ./stubs.nix ]; + tests.stubs.wl-clipboard = { }; + + programs.neovim = { + enable = true; + extraName = "-my-suffix"; + withPerl = true; + withPython3 = true; + withRuby = true; + withNodeJs = true; + autowrapRuntimeDeps = true; + waylandSupport = isLinux; + plugins = [ dummyPlugin ]; + }; + + nmt.script = '' + nvimBin="home-path/bin/nvim" + + assertBinaryContains() { + local file="$TESTED/$1" + if [[ $1 == /* ]]; then file="$1"; fi + + if ! grep -a -qF -- "$2" "$file"; then + fail "Expected binary file '$1' to contain '$2' but it did not." + fi + } + + # Ensure the main binary exists + assertFileExists "$nvimBin" + + # 1. extraName: Check if the suffix is in the rplugin manifest path within the wrapper + assertBinaryContains "$nvimBin" "-my-suffix/rplugin.vim" + + # 2. withPerl: Check if nvim-perl binary exists and host prog is set + assertFileExists "home-path/bin/nvim-perl" + assertBinaryContains "$nvimBin" "perl_host_prog=" + + # 3. withPython3: Check if nvim-python3 binary exists and host prog is set + assertFileExists "home-path/bin/nvim-python3" + assertBinaryContains "$nvimBin" "python3_host_prog=" + + # 4. withRuby: Check if nvim-ruby binary exists, GEM_HOME and host prog are set + assertFileExists "home-path/bin/nvim-ruby" + assertBinaryContains "$nvimBin" "GEM_HOME=" + assertBinaryContains "$nvimBin" "ruby_host_prog=" + + # 5. withNodeJs: Check if nvim-node binary exists and host prog is set + assertFileExists "home-path/bin/nvim-node" + assertBinaryContains "$nvimBin" "node_host_prog=" + + # 6. waylandSupport: Check for wl-clipboard path in wrapper's PATH modification + # We check for the store path of wl-clipboard in the current pkgs + ${lib.optionalString isLinux '' + assertBinaryContains "$nvimBin" "wl-clipboard-" + ''} + + # 7. autowrapRuntimeDeps: Check for dummyDep path in wrapper's PATH modification + assertBinaryContains "$nvimBin" "${dummyDep}/bin" + ''; +} From bdb807dc28011d1676a0b88faf76fc1bcbc19d44 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 20 Dec 2025 00:24:07 -0600 Subject: [PATCH 68/80] neovim: modernize Signed-off-by: Austin Horstman --- modules/programs/neovim.nix | 417 +++++++++++++++++++----------------- 1 file changed, 222 insertions(+), 195 deletions(-) diff --git a/modules/programs/neovim.nix b/modules/programs/neovim.nix index c7520c06..0498c2bd 100644 --- a/modules/programs/neovim.nix +++ b/modules/programs/neovim.nix @@ -7,93 +7,30 @@ let inherit (lib) + concatMapStringsSep literalExpression mkEnableOption mkIf mkOption + mkPackageOption mkRemovedOptionModule + optionals types ; cfg = config.programs.neovim; - fileType = - (import ../lib/file-type.nix { - inherit (config.home) homeDirectory; - inherit lib pkgs; - }).fileType; + inherit + ( + (import ../lib/file-type.nix { + inherit (config.home) homeDirectory; + inherit lib pkgs; + }) + ) + fileType + ; jsonFormat = pkgs.formats.json { }; - - pluginWithConfigType = types.submodule { - options = { - config = mkOption { - type = types.nullOr types.lines; - description = "Script to configure this plugin. The scripting language should match type."; - default = null; - }; - - type = mkOption { - type = types.either (types.enum [ - "lua" - "viml" - "teal" - "fennel" - ]) types.str; - description = "Language used in config. Configurations are aggregated per-language."; - default = "viml"; - }; - - optional = mkEnableOption "optional" // { - description = "Don't load by default (load with :packadd)"; - }; - - plugin = lib.mkPackageOption pkgs.vimPlugins "plugin" { - default = null; - example = "pkgs.vimPlugins.nvim-treesitter"; - pkgsText = "pkgs.vimPlugins"; - }; - - runtime = mkOption { - default = { }; - # passing actual "${xdg.configHome}/nvim" as basePath was a bit tricky - # due to how fileType.target is implemented - type = fileType "programs.neovim.plugins._.runtime" "{var}`xdg.configHome/nvim`" "nvim"; - example = literalExpression '' - { "ftplugin/c.vim".text = "setlocal omnifunc=v:lua.vim.lsp.omnifunc"; } - ''; - description = '' - Set of files that have to be linked in nvim config folder. - ''; - }; - }; - }; - - allPlugins = - cfg.plugins - ++ lib.optional cfg.coc.enable { - type = "viml"; - plugin = cfg.coc.package; - config = cfg.coc.pluginConfig; - optional = false; - }; - - luaPackages = cfg.finalPackage.unwrapped.lua.pkgs; - resolvedExtraLuaPackages = cfg.extraLuaPackages luaPackages; - - extraMakeWrapperArgs = lib.optionalString ( - cfg.extraPackages != [ ] - ) ''--suffix PATH : "${lib.makeBinPath cfg.extraPackages}"''; - extraMakeWrapperLuaCArgs = - lib.optionalString (resolvedExtraLuaPackages != [ ]) - ''--suffix LUA_CPATH ";" "${ - lib.concatMapStringsSep ";" luaPackages.getLuaCPath resolvedExtraLuaPackages - }"''; - extraMakeWrapperLuaArgs = - lib.optionalString (resolvedExtraLuaPackages != [ ]) - ''--suffix LUA_PATH ";" "${ - lib.concatMapStringsSep ";" luaPackages.getLuaPath resolvedExtraLuaPackages - }"''; in { meta.maintainers = with lib.maintainers; [ khaneliman ]; @@ -118,10 +55,20 @@ in configure.customRC -> programs.neovim.extraConfig '') ]; + options = { programs.neovim = { enable = mkEnableOption "Neovim"; + package = mkPackageOption pkgs "neovim" { default = "neovim-unwrapped"; }; + + finalPackage = mkOption { + type = types.package; + readOnly = true; + description = "Resulting customized neovim package."; + }; + + # Aliases viAlias = mkOption { type = types.bool; default = false; @@ -146,6 +93,17 @@ in ''; }; + defaultEditor = mkOption { + type = types.bool; + default = false; + description = '' + Whether to configure {command}`nvim` as the default + editor using the {env}`EDITOR` and {env}`VISUAL` + environment variables. + ''; + }; + + # Providers & Runtimes withNodeJs = mkOption { type = types.bool; default = false; @@ -164,14 +122,6 @@ in ''; }; - withRuby = mkOption { - type = types.nullOr types.bool; - default = true; - description = '' - Enable ruby provider. - ''; - }; - withPython3 = mkOption { type = types.bool; default = true; @@ -181,6 +131,14 @@ in ''; }; + withRuby = mkOption { + type = types.nullOr types.bool; + default = true; + description = '' + Enable ruby provider. + ''; + }; + extraPython3Packages = mkOption { # In case we get a plain list, we need to turn it into a function, # as expected by the function in nixpkgs. @@ -207,10 +165,10 @@ in ''; }; - # We get the Lua package from the final package and use its - # Lua packageset to evaluate the function that this option was set to. - # This ensures that we always use the same Lua version as the Neovim package. extraLuaPackages = mkOption { + # We get the Lua package from the final package and use its + # Lua packageset to evaluate the function that this option was set to. + # This ensures that we always use the same Lua version as the Neovim package. type = let fromType = types.listOf types.package; @@ -233,6 +191,7 @@ in ''; }; + # Wrapper Configuration extraName = mkOption { type = types.str; default = ""; @@ -259,7 +218,7 @@ in }; extraWrapperArgs = mkOption { - type = with types; listOf str; + type = types.listOf types.str; default = [ ]; example = literalExpression '' [ @@ -280,54 +239,14 @@ in ''; }; - generatedConfigViml = mkOption { - type = types.lines; - visible = true; - readOnly = true; - description = '' - Generated vimscript config. - ''; - }; - - generatedConfigs = mkOption { - type = types.attrsOf types.lines; - visible = true; - readOnly = true; - example = literalExpression '' - { - viml = ''' - " Generated by home-manager - map , - '''; - - lua = ''' - -- Generated by home-manager - vim.opt.background = "dark" - '''; - }''; - description = '' - Generated configurations with as key their language (set via type). - ''; - }; - - package = lib.mkPackageOption pkgs "neovim" { default = "neovim-unwrapped"; }; - - finalPackage = mkOption { - type = types.package; - readOnly = true; - description = "Resulting customized neovim package."; - }; - - defaultEditor = mkOption { - type = types.bool; - default = false; - description = '' - Whether to configure {command}`nvim` as the default - editor using the {env}`EDITOR` and {env}`VISUAL` - environment variables. - ''; + extraPackages = mkOption { + type = types.listOf types.package; + default = [ ]; + example = literalExpression "[ pkgs.shfmt ]"; + description = "Extra packages available to nvim."; }; + # Configuration & Plugins extraConfig = mkOption { type = types.lines; default = ""; @@ -350,37 +269,78 @@ in ''; }; - extraPackages = mkOption { - type = with types; listOf package; - default = [ ]; - example = literalExpression "[ pkgs.shfmt ]"; - description = "Extra packages available to nvim."; - }; + plugins = + let + pluginWithConfigType = types.submodule { + options = { + config = mkOption { + type = types.nullOr types.lines; + description = "Script to configure this plugin. The scripting language should match type."; + default = null; + }; - plugins = mkOption { - type = with types; listOf (either package pluginWithConfigType); - default = [ ]; - example = literalExpression '' - with pkgs.vimPlugins; [ - yankring - vim-nix - { plugin = vim-startify; - config = "let g:startify_change_to_vcs_root = 0"; - } - ] - ''; - description = '' - List of vim plugins to install optionally associated with - configuration to be placed in init.vim. + type = mkOption { + type = types.either (types.enum [ + "lua" + "viml" + "teal" + "fennel" + ]) types.str; + description = "Language used in config. Configurations are aggregated per-language."; + default = "viml"; + }; - This option is mutually exclusive with {var}`configure`. - ''; - }; + optional = mkEnableOption "optional" // { + description = "Don't load by default (load with :packadd)"; + }; + + plugin = mkPackageOption pkgs.vimPlugins "plugin" { + default = null; + example = "pkgs.vimPlugins.nvim-treesitter"; + pkgsText = "pkgs.vimPlugins"; + }; + + runtime = mkOption { + default = { }; + # passing actual "${xdg.configHome}/nvim" as basePath was a bit tricky + # due to how fileType.target is implemented + type = fileType "programs.neovim.plugins._.runtime" "{var}`xdg.configHome/nvim`" "nvim"; + example = literalExpression '' + { "ftplugin/c.vim".text = "setlocal omnifunc=v:lua.vim.lsp.omnifunc"; } + ''; + description = '' + Set of files that have to be linked in nvim config folder. + ''; + }; + }; + }; + + in + mkOption { + type = types.listOf (types.either types.package pluginWithConfigType); + default = [ ]; + example = literalExpression '' + with pkgs.vimPlugins; + [ + yankring + vim-nix + { plugin = vim-startify; + config = "let g:startify_change_to_vcs_root = 0"; + } + ] + ''; + description = '' + List of vim plugins to install optionally associated with + configuration to be placed in init.vim. + + This option is mutually exclusive with {var}`configure`. + ''; + }; coc = { enable = mkEnableOption "Coc"; - package = lib.mkPackageOption pkgs "coc-nvim" { + package = mkPackageOption pkgs "coc-nvim" { default = [ "vimPlugins" "coc-nvim" @@ -410,7 +370,7 @@ in filetypes = [ "haskell" "lhaskell" ]; }; }; - }; + } ''; description = '' Extra configuration lines to add to @@ -427,11 +387,52 @@ in description = "Script to configure CoC. Must be viml."; }; }; + + # Generated / Read-Only + generatedConfigViml = mkOption { + type = types.lines; + visible = true; + readOnly = true; + description = '' + Generated vimscript config. + ''; + }; + + generatedConfigs = mkOption { + type = types.attrsOf types.lines; + visible = true; + readOnly = true; + example = literalExpression '' + { + viml = ''' + " Generated by home-manager + map , + '''; + + lua = ''' + -- Generated by home-manager + vim.opt.background = "dark" + '''; + } + ''; + description = '' + Generated configurations with as key their language (set via type). + ''; + }; }; }; - config = + config = mkIf cfg.enable ( let + allPlugins = + cfg.plugins + ++ lib.optional cfg.coc.enable { + type = "viml"; + plugin = cfg.coc.package; + config = cfg.coc.pluginConfig; + optional = false; + }; + defaultPlugin = { type = "viml"; plugin = null; @@ -447,6 +448,32 @@ in suppressNotVimlConfig = p: if p.type != "viml" then p // { config = null; } else p; + # Lua & Python Package Resolution + luaPackages = cfg.finalPackage.unwrapped.lua.pkgs; + resolvedExtraLuaPackages = cfg.extraLuaPackages luaPackages; + + # Wrapper Arguments Construction + extraMakeWrapperArgs = optionals (cfg.extraPackages != [ ]) [ + "--suffix" + "PATH" + ":" + (lib.makeBinPath cfg.extraPackages) + ]; + + extraMakeWrapperLuaCArgs = optionals (resolvedExtraLuaPackages != [ ]) [ + "--suffix" + "LUA_CPATH" + ";" + (concatMapStringsSep ";" luaPackages.getLuaCPath resolvedExtraLuaPackages) + ]; + + extraMakeWrapperLuaArgs = optionals (resolvedExtraLuaPackages != [ ]) [ + "--suffix" + "LUA_PATH" + ";" + (concatMapStringsSep ";" luaPackages.getLuaPath resolvedExtraLuaPackages) + ]; + neovimConfig = pkgs.neovimUtils.makeNeovimConfig { inherit (cfg) extraPython3Packages @@ -468,58 +495,59 @@ in extraName autowrapRuntimeDeps waylandSupport + withNodeJs ; wrapperArgs = - (lib.escapeShellArgs (neovimConfig.wrapperArgs ++ cfg.extraWrapperArgs)) - + " " - + extraMakeWrapperArgs - + " " - + extraMakeWrapperLuaCArgs - + " " - + extraMakeWrapperLuaArgs; + neovimConfig.wrapperArgs + ++ cfg.extraWrapperArgs + ++ extraMakeWrapperArgs + ++ extraMakeWrapperLuaCArgs + ++ extraMakeWrapperLuaArgs; wrapRc = false; } ); in - mkIf cfg.enable { + { + programs.neovim = { + generatedConfigViml = neovimConfig.neovimRcContent; - programs.neovim.generatedConfigViml = neovimConfig.neovimRcContent; + generatedConfigs = + let + grouped = lib.lists.groupBy (x: x.type) pluginsNormalized; + configsOnly = lib.foldl (acc: p: if p.config != null then acc ++ [ p.config ] else acc) [ ]; + in + lib.mapAttrs (name: vals: concatMapStringsSep "\n" (configsOnly vals)) grouped; - programs.neovim.generatedConfigs = - let - grouped = lib.lists.groupBy (x: x.type) pluginsNormalized; - configsOnly = lib.foldl (acc: p: if p.config != null then acc ++ [ p.config ] else acc) [ ]; - in - lib.mapAttrs (name: vals: lib.concatStringsSep "\n" (configsOnly vals)) grouped; - - home.packages = [ cfg.finalPackage ]; - - home.sessionVariables = mkIf cfg.defaultEditor { - EDITOR = "nvim"; - VISUAL = "nvim"; + finalPackage = wrappedNeovim'; }; - home.shellAliases = mkIf cfg.vimdiffAlias { vimdiff = "nvim -d"; }; + home = { + packages = [ cfg.finalPackage ]; + + sessionVariables = mkIf cfg.defaultEditor { + EDITOR = "nvim"; + VISUAL = "nvim"; + }; + + shellAliases = mkIf cfg.vimdiffAlias { vimdiff = "nvim -d"; }; + }; xdg.configFile = let hasLuaConfig = lib.hasAttr "lua" config.programs.neovim.generatedConfigs; + luaRcContent = + lib.optionalString ( + wrappedNeovim'.initRc != "" + ) "vim.cmd [[source ${pkgs.writeText "nvim-init-home-manager.vim" wrappedNeovim'.initRc}]]\n" + + config.programs.neovim.extraLuaConfig + + lib.optionalString hasLuaConfig config.programs.neovim.generatedConfigs.lua; in lib.mkMerge ( # writes runtime (map (x: x.runtime) pluginsNormalized) ++ [ { - "nvim/init.lua" = - let - luaRcContent = - lib.optionalString ( - wrappedNeovim'.initRc != "" - ) "vim.cmd [[source ${pkgs.writeText "nvim-init-home-manager.vim" wrappedNeovim'.initRc}]]\n" - + config.programs.neovim.extraLuaConfig - + lib.optionalString hasLuaConfig config.programs.neovim.generatedConfigs.lua; - in - mkIf (luaRcContent != "") { text = luaRcContent; }; + "nvim/init.lua" = mkIf (luaRcContent != "") { text = luaRcContent; }; "nvim/coc-settings.json" = mkIf cfg.coc.enable { source = jsonFormat.generate "coc-settings.json" cfg.coc.settings; @@ -527,7 +555,6 @@ in } ] ); - - programs.neovim.finalPackage = wrappedNeovim'; - }; + } + ); } From a7d6bba3585ffb8bf9c9a58edd43631a2d2932eb Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 20 Dec 2025 00:34:25 -0600 Subject: [PATCH 69/80] neovim: remove old removal options 3-4 years old, no need to keep around. Signed-off-by: Austin Horstman --- modules/programs/neovim.nix | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/modules/programs/neovim.nix b/modules/programs/neovim.nix index 0498c2bd..1f93940b 100644 --- a/modules/programs/neovim.nix +++ b/modules/programs/neovim.nix @@ -13,7 +13,6 @@ let mkIf mkOption mkPackageOption - mkRemovedOptionModule optionals types ; @@ -35,27 +34,6 @@ in { meta.maintainers = with lib.maintainers; [ khaneliman ]; - imports = [ - (mkRemovedOptionModule [ - "programs" - "neovim" - "withPython" - ] "Python2 support has been removed from neovim.") - (mkRemovedOptionModule [ - "programs" - "neovim" - "extraPythonPackages" - ] "Python2 support has been removed from neovim.") - (mkRemovedOptionModule [ "programs" "neovim" "configure" ] '' - programs.neovim.configure is deprecated. - Other programs.neovim options can override its settings or ignore them. - Please use the other options at your disposal: - configure.packages.*.opt -> programs.neovim.plugins = [ { plugin = ...; optional = true; }] - configure.packages.*.start -> programs.neovim.plugins = [ { plugin = ...; }] - configure.customRC -> programs.neovim.extraConfig - '') - ]; - options = { programs.neovim = { enable = mkEnableOption "Neovim"; From 4dc3c91c50d676180cc5e45b8bff0b3ced052ced Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 20 Dec 2025 00:39:10 -0600 Subject: [PATCH 70/80] neovim: remove deprecated type coercion for extraXPackages 3 years old, no need to keep around. Signed-off-by: Austin Horstman --- modules/programs/neovim.nix | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/modules/programs/neovim.nix b/modules/programs/neovim.nix index 1f93940b..6cc8c056 100644 --- a/modules/programs/neovim.nix +++ b/modules/programs/neovim.nix @@ -118,20 +118,7 @@ in }; extraPython3Packages = mkOption { - # In case we get a plain list, we need to turn it into a function, - # as expected by the function in nixpkgs. - # The only way to do so is to call `const`, which will ignore its input. - type = - let - fromType = types.listOf types.package; - in - types.coercedTo fromType (lib.flip lib.warn lib.const '' - Assigning a plain list to extraPython3Packages is deprecated. - Please assign a function taking a package set as argument, so - extraPython3Packages = [ pkgs.python3Packages.xxx ]; - should become - extraPython3Packages = ps: [ ps.xxx ]; - '') (types.functionTo fromType); + type = types.functionTo (types.listOf types.package); default = _: [ ]; defaultText = literalExpression "ps: [ ]"; example = literalExpression "pyPkgs: with pyPkgs; [ python-language-server ]"; @@ -144,20 +131,7 @@ in }; extraLuaPackages = mkOption { - # We get the Lua package from the final package and use its - # Lua packageset to evaluate the function that this option was set to. - # This ensures that we always use the same Lua version as the Neovim package. - type = - let - fromType = types.listOf types.package; - in - types.coercedTo fromType (lib.flip lib.warn lib.const '' - Assigning a plain list to extraLuaPackages is deprecated. - Please assign a function taking a package set as argument, so - extraLuaPackages = [ pkgs.lua51Packages.xxx ]; - should become - extraLuaPackages = ps: [ ps.xxx ]; - '') (types.functionTo fromType); + type = types.functionTo (types.listOf types.package); default = _: [ ]; defaultText = literalExpression "ps: [ ]"; example = literalExpression "luaPkgs: with luaPkgs; [ luautf8 ]"; From 7b73a6e98f3c28552ea6acba645bf20d6102cad0 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 20 Dec 2025 19:10:38 -0600 Subject: [PATCH 71/80] neovim: fix eval Signed-off-by: Austin Horstman --- modules/programs/neovim.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/programs/neovim.nix b/modules/programs/neovim.nix index 6cc8c056..bc3bbac6 100644 --- a/modules/programs/neovim.nix +++ b/modules/programs/neovim.nix @@ -465,10 +465,10 @@ in generatedConfigs = let - grouped = lib.lists.groupBy (x: x.type) pluginsNormalized; + grouped = builtins.groupBy (x: x.type) pluginsNormalized; configsOnly = lib.foldl (acc: p: if p.config != null then acc ++ [ p.config ] else acc) [ ]; in - lib.mapAttrs (name: vals: concatMapStringsSep "\n" (configsOnly vals)) grouped; + lib.mapAttrs (_name: vals: lib.concatStringsSep "\n" (configsOnly vals)) grouped; finalPackage = wrappedNeovim'; }; From 61fcc9de76b88e55578eb5d79fc80f2b236df707 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 20 Dec 2025 19:42:05 -0600 Subject: [PATCH 72/80] tests/neovim: add test to catch config eval Signed-off-by: Austin Horstman --- .../modules/programs/neovim/plugin-config.nix | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/modules/programs/neovim/plugin-config.nix b/tests/modules/programs/neovim/plugin-config.nix index 466cede4..5b198ded 100644 --- a/tests/modules/programs/neovim/plugin-config.nix +++ b/tests/modules/programs/neovim/plugin-config.nix @@ -34,15 +34,25 @@ lib.mkIf config.test.enableBig { _module.args.pkgs = lib.mkForce realPkgs; - nmt.script = '' - vimout=$(mktemp) - echo "redir >> /dev/stdout | echo g:hmExtraConfig | echo g:hmPlugins | redir END" \ - | ${pkgs.neovim}/bin/nvim -es -u "$TESTED/home-files/.config/nvim/init.lua" \ - > "$vimout" || true - assertFileContains "$vimout" "HM_EXTRA_CONFIG" - assertFileContains "$vimout" "HM_PLUGINS_CONFIG" + nmt.script = + let + # Force evaluation of generatedConfigs. + luaConfig = config.programs.neovim.generatedConfigs.lua; + vimlConfig = config.programs.neovim.generatedConfigs.viml; + in + '' + vimout=$(mktemp) + echo "redir >> /dev/stdout | echo g:hmExtraConfig | echo g:hmPlugins | redir END" \ + | ${pkgs.neovim}/bin/nvim -es -u "$TESTED/home-files/.config/nvim/init.lua" \ + > "$vimout" || true + assertFileContains "$vimout" "HM_EXTRA_CONFIG" + assertFileContains "$vimout" "HM_PLUGINS_CONFIG" - initLua="$TESTED/home-files/.config/nvim/init.lua" - assertFileContent $(normalizeStorePaths "$initLua") ${./plugin-config.expected} - ''; + initLua="$TESTED/home-files/.config/nvim/init.lua" + assertFileContent $(normalizeStorePaths "$initLua") ${./plugin-config.expected} + + # Verify generatedConfigs evaluated properly (issue #8371) + echo "Lua config length: ${toString (builtins.stringLength luaConfig)}" + echo "Viml config length: ${toString (builtins.stringLength vimlConfig)}" + ''; } From 3fe66908e090a521d1ec5978e84f3c2012820628 Mon Sep 17 00:00:00 2001 From: aleksana Date: Mon, 22 Dec 2025 10:27:38 +0800 Subject: [PATCH 73/80] television: fix keybindings --- modules/programs/television.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/programs/television.nix b/modules/programs/television.nix index cec1c963..9286ce84 100644 --- a/modules/programs/television.nix +++ b/modules/programs/television.nix @@ -106,13 +106,13 @@ in ]; programs.bash.initExtra = lib.mkIf cfg.enableBashIntegration '' - eval "$(${lib.getExe cfg.package} init bash)" + source ${cfg.package}/share/television/completion.bash ''; programs.zsh.initContent = lib.mkIf cfg.enableZshIntegration '' - eval "$(${lib.getExe cfg.package} init zsh)" + source ${cfg.package}/share/television/completion.zsh ''; programs.fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration '' - ${lib.getExe cfg.package} init fish | source + source ${cfg.package}/share/television/completion.fish ''; }; } From 57a02fd7d98beb62e47bbe6d26d4180e75a54f82 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 04:26:19 +0000 Subject: [PATCH 74/80] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/1306659b587dc277866c7b69eb97e5f07864d8c4?narHash=sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4%3D' (2025-12-15) → 'github:NixOS/nixpkgs/c6245e83d836d0433170a16eb185cefe0572f8b8?narHash=sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc%3D' (2025-12-18) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 7009e57c..b7dc8c95 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1765779637, - "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", + "lastModified": 1766070988, + "narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", + "rev": "c6245e83d836d0433170a16eb185cefe0572f8b8", "type": "github" }, "original": { From 527ad07e6625302b648ed3b28c34b62a79bd103e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 22 Dec 2025 01:08:47 -0600 Subject: [PATCH 75/80] tests/radicle: stub radicle-node Failing build is blocking CI, test only verifies generated config. Signed-off-by: Austin Horstman --- .../programs/radicle/basic-configuration.nix | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/modules/programs/radicle/basic-configuration.nix b/tests/modules/programs/radicle/basic-configuration.nix index 378cb844..b283e660 100644 --- a/tests/modules/programs/radicle/basic-configuration.nix +++ b/tests/modules/programs/radicle/basic-configuration.nix @@ -1,9 +1,19 @@ -{ config, pkgs, ... }: - { config = { programs.radicle.enable = true; + test.stubs.radicle-node = { + buildScript = '' + mkdir -p "$out/bin" + cat > "$out/bin/rad" << 'EOF' + #!/bin/sh + # Stub rad command that does nothing + exit 0 + EOF + chmod +x "$out/bin/rad" + ''; + }; + nmt.script = '' assertFileContent \ home-files/.radicle/config.json \ From 9c790e687e2e7645899cb21dec2a416710aa1064 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 09:20:36 +0000 Subject: [PATCH 76/80] maintainers: update all-maintainers.nix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automated update of the master maintainers list combining: - Home Manager specific maintainers from modules/lib/maintainers.nix - Nixpkgs maintainers referenced in Home Manager modules **Added:** 0 maintainers **Removed:** 0 maintainers **Total:** 282 → 282 maintainers Generated by: lib/python/generate-all-maintainers.py --- all-maintainers.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/all-maintainers.nix b/all-maintainers.nix index 48f96da4..7bfbdbaa 100644 --- a/all-maintainers.nix +++ b/all-maintainers.nix @@ -2078,7 +2078,7 @@ source = "nixpkgs"; }; shikanime = { - email = "deva.shikanime@protonmail.com"; + email = "william.phetsinorath@shikanime.studio"; github = "shikanime"; githubId = 22115108; name = "William Phetsinorath"; From 624c7e80fbf9149e740386dde89010308fbea339 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 23 Dec 2025 16:25:41 -0600 Subject: [PATCH 77/80] release/23.05: add swaylock stateVersion change Signed-off-by: Austin Horstman --- docs/release-notes/rl-2305.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/release-notes/rl-2305.md b/docs/release-notes/rl-2305.md index 1358cf71..bdd99100 100644 --- a/docs/release-notes/rl-2305.md +++ b/docs/release-notes/rl-2305.md @@ -57,3 +57,9 @@ changes are only active if the `home.stateVersion` option is set to now default to `true` which is consistent with the default values for those options used by `i3` and `sway`. + +- The [](#opt-programs.swaylock.enable) option now defaults to `false` + and must be explicitly enabled. Previously, it would be implicitly + enabled when `programs.swaylock.settings` was non-empty. Users with + `home.stateVersion` set to earlier versions will continue to get the + old implicit behavior. From af3c24de76297856acc6839e1580e227f1717bf9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 23 Dec 2025 16:25:56 -0600 Subject: [PATCH 78/80] release/25.05: add git signing format change Signed-off-by: Austin Horstman --- docs/release-notes/rl-2505.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/rl-2505.md b/docs/release-notes/rl-2505.md index 977e853c..2ec0f1ad 100644 --- a/docs/release-notes/rl-2505.md +++ b/docs/release-notes/rl-2505.md @@ -27,4 +27,8 @@ The state version in this release includes the changes below. These changes are only active if the `home.stateVersion` option is set to \"25.05\" or later. -- No changes. +- The [](#opt-programs.git.signing.format) option no longer defaults to + `"openpgp"`. Users who use Git signing with GPG should explicitly set + this option to `"openpgp"` to maintain the previous behavior. Users + with `home.stateVersion` set to earlier versions will continue to get + the `"openpgp"` default for backwards compatibility. From 20728df08f6ecf69a99ee6f031c235bf393ea585 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 23 Dec 2025 16:26:14 -0600 Subject: [PATCH 79/80] release/25.11: add darwin copy apps change Signed-off-by: Austin Horstman --- docs/release-notes/rl-2511.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/release-notes/rl-2511.md b/docs/release-notes/rl-2511.md index 8f0c666f..83f4b396 100644 --- a/docs/release-notes/rl-2511.md +++ b/docs/release-notes/rl-2511.md @@ -80,3 +80,10 @@ changes are only active if the `home.stateVersion` option is set to `{ PASSWORD_STORE_DIR = $XDG_DATA_HOME/password-store; }` anymore by its default value. This will revert to the default behaviour of the program, namely `$HOME/.password-store` to be used as the store path. + +- On macOS, [](#opt-targets.darwin.copyApps.enable) is now enabled by + default instead of [](#opt-targets.darwin.linkApps.enable). This means + applications from `home.packages` will be copied to + `~/Applications/Home Manager Apps` rather than symlinked, making them + work properly with Spotlight. Users with `home.stateVersion` set to + earlier versions will continue to use `linkApps` by default. From 7eca7f7081036a7b740090994c9ec543927f89a7 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Wed, 24 Dec 2025 04:24:22 +0000 Subject: [PATCH 80/80] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/c6245e83d836d0433170a16eb185cefe0572f8b8?narHash=sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc%3D' (2025-12-18) → 'github:NixOS/nixpkgs/a6531044f6d0bef691ea18d4d4ce44d0daa6e816?narHash=sha256-3xY8CZ4rSnQ0NqGhMKAy5vgC%2B2IVK0NoVEzDoOh4DA4%3D' (2025-12-21) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index b7dc8c95..767b497b 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1766070988, - "narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=", + "lastModified": 1766309749, + "narHash": "sha256-3xY8CZ4rSnQ0NqGhMKAy5vgC+2IVK0NoVEzDoOh4DA4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c6245e83d836d0433170a16eb185cefe0572f8b8", + "rev": "a6531044f6d0bef691ea18d4d4ce44d0daa6e816", "type": "github" }, "original": {