From 36349274d710d37abdca49dd5b3612091738722a Mon Sep 17 00:00:00 2001 From: Dennis Schridde Date: Tue, 3 Feb 2026 15:03:45 +0100 Subject: [PATCH 01/83] home-manager: Prevent pipe failure when reading news On my system (Fedora 43 with Nix 2.31.3) `home-manager news` exits from `SIGPIPE` and never writes `~/.local/share/home-manager/news-read-ids`, resulting in news items to never be marked read. This is caused by piping `(import ./news.nix).news.all` through `jq` and `less` failing with an error as soon as `less` exits, which triggers `set -o pipefail` to exit the shell running `home-manager` itself. Avoiding the pipe into `$PAGER` avoids the problem. Closes: https://github.com/nix-community/home-manager/issues/8690 References: https://github.com/jqlang/jq/issues/1017 References: https://www.greenend.org.uk/rjk/tech/shellmistakes.html#pipeerrors --- home-manager/home-manager | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/home-manager/home-manager b/home-manager/home-manager index 569a5642..6f9f3907 100644 --- a/home-manager/home-manager +++ b/home-manager/home-manager @@ -998,9 +998,11 @@ function doShowNews() { return 1 esac + local formattedNewsFile="$WORK_DIR/news.txt" nix-instantiate --quiet --eval --json --expr "(import ${newsNixFile}).news.$newsAttr" \ | jq -r . \ - | ${PAGER:-less} + > "$formattedNewsFile" + ${PAGER:-less} "$formattedNewsFile" local allIds allIds="$(nix-instantiate --quiet --eval --expr "(import ${newsNixFile}).meta.ids")" From 83e4f9b4d20d2d6084d0392d0f51333992fb3c4c Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 09:37:49 +0000 Subject: [PATCH 02/83] 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:** magicquark Generated by: lib/python/generate-all-maintainers.py --- all-maintainers.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/all-maintainers.nix b/all-maintainers.nix index 7eaca3ff..b25b8a6b 100644 --- a/all-maintainers.nix +++ b/all-maintainers.nix @@ -1538,6 +1538,12 @@ name = "Lukas Nagel"; source = "home-manager"; }; + magicquark = { + github = "magicquark"; + githubId = 198001825; + name = "magicquark"; + source = "nixpkgs"; + }; mainrs = { email = "5113257+mainrs@users.noreply.github.com"; github = "mainrs"; From 4fda26500b4539e0a1e3afba9f0e1616bdad4f85 Mon Sep 17 00:00:00 2001 From: Johan Larsson Date: Fri, 9 Jan 2026 22:55:58 +0100 Subject: [PATCH 03/83] tomat: modify `After`, `PartOf`, `Environment` in service This allows the tomat service to properly execute hooks, which is a feature that requires a modifying the PATH and ensuring that the service is started after the graphical session. --- modules/services/tomat.nix | 6 ++++-- tests/modules/services/tomat/expected.service | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/services/tomat.nix b/modules/services/tomat.nix index 95189cca..8102af86 100644 --- a/modules/services/tomat.nix +++ b/modules/services/tomat.nix @@ -57,17 +57,19 @@ in systemd.user.services.tomat = { Unit = { Description = "Tomat Pomodoro server"; - After = [ "graphical.target" ]; + After = [ "graphical-session.target" ]; + PartOf = [ "graphical-session.target" ]; }; Service = { ExecStart = "${lib.getExe cfg.package} daemon run"; Restart = "always"; RestartSec = 5; + Environment = [ "PATH=${config.home.profileDirectory}/bin" ]; }; Install = { - WantedBy = [ "default.target" ]; + WantedBy = [ "graphical-session.target" ]; }; }; }; diff --git a/tests/modules/services/tomat/expected.service b/tests/modules/services/tomat/expected.service index dce396a2..8a036c8e 100644 --- a/tests/modules/services/tomat/expected.service +++ b/tests/modules/services/tomat/expected.service @@ -1,11 +1,13 @@ [Install] -WantedBy=default.target +WantedBy=graphical-session.target [Service] +Environment=PATH=/home/hm-user/.nix-profile/bin ExecStart=@tomat@/bin/tomat daemon run Restart=always RestartSec=5 [Unit] -After=graphical.target +After=graphical-session.target Description=Tomat Pomodoro server +PartOf=graphical-session.target From 689e4a34231faf19b6c6de4d3700d8f356964a1d Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 05:02:13 +0000 Subject: [PATCH 04/83] 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/88d3861acdd3d2f0e361767018218e51810df8a1?narHash=sha256-MJ27Cy2NtBEV5tsK%2BYraYr2g851f3Fl1LpNHDzDX15c%3D' (2026-01-21) → 'github:NixOS/nixpkgs/e6eae2ee2110f3d31110d5c222cd395303343b08?narHash=sha256-KHFT9UWOF2yRPlAnSXQJh6uVcgNcWlFqqiAZ7OVlHNc%3D' (2026-02-03) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 22a49923..c2a8a2cd 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1769018530, - "narHash": "sha256-MJ27Cy2NtBEV5tsK+YraYr2g851f3Fl1LpNHDzDX15c=", + "lastModified": 1770115704, + "narHash": "sha256-KHFT9UWOF2yRPlAnSXQJh6uVcgNcWlFqqiAZ7OVlHNc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "88d3861acdd3d2f0e361767018218e51810df8a1", + "rev": "e6eae2ee2110f3d31110d5c222cd395303343b08", "type": "github" }, "original": { From 7c47cafa90b7f5c1bac11b1c2f2b5cf8c339b02b Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Tue, 7 Oct 2025 22:31:23 -0600 Subject: [PATCH 05/83] =?UTF-8?q?xdg.userDirs:=20don=E2=80=99t=20require?= =?UTF-8?q?=20Linux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Everything here works on Darwin, etc. --- modules/misc/xdg-user-dirs.nix | 4 ---- tests/modules/misc/xdg/default.nix | 1 + tests/modules/misc/xdg/linux.nix | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/misc/xdg-user-dirs.nix b/modules/misc/xdg-user-dirs.nix index e3d3db54..aadf3bbe 100644 --- a/modules/misc/xdg-user-dirs.nix +++ b/modules/misc/xdg-user-dirs.nix @@ -126,10 +126,6 @@ in // cfg.extraConfig; in lib.mkIf cfg.enable { - assertions = [ - (lib.hm.assertions.assertPlatform "xdg.userDirs" pkgs lib.platforms.linux) - ]; - xdg.configFile."user-dirs.dirs".text = let # For some reason, these need to be wrapped with quotes to be valid. diff --git a/tests/modules/misc/xdg/default.nix b/tests/modules/misc/xdg/default.nix index 9212694f..6b80d45c 100644 --- a/tests/modules/misc/xdg/default.nix +++ b/tests/modules/misc/xdg/default.nix @@ -4,4 +4,5 @@ xdg-mime-disabled = ./mime-disabled.nix; xdg-autostart = ./autostart.nix; xdg-autostart-readonly = ./autostart-readonly.nix; + xdg-user-dirs-null = ./user-dirs-null.nix; } diff --git a/tests/modules/misc/xdg/linux.nix b/tests/modules/misc/xdg/linux.nix index b0a6bb04..ed177349 100644 --- a/tests/modules/misc/xdg/linux.nix +++ b/tests/modules/misc/xdg/linux.nix @@ -2,7 +2,6 @@ xdg-mime-apps-basics = ./mime-apps-basics.nix; xdg-system-dirs = ./system-dirs.nix; xdg-desktop-entries = ./desktop-entries.nix; - xdg-user-dirs-null = ./user-dirs-null.nix; xdg-portal = ./portal.nix; xdg-mime = ./mime.nix; xdg-mime-package = ./mime-packages.nix; From 83cc9d32e3115f17b9d4aaa80387d84e0efd194f Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Tue, 7 Oct 2025 22:35:38 -0600 Subject: [PATCH 06/83] xdg.userDirs: add a nullable `package` option --- modules/misc/xdg-user-dirs.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/misc/xdg-user-dirs.nix b/modules/misc/xdg-user-dirs.nix index aadf3bbe..200d4e76 100644 --- a/modules/misc/xdg-user-dirs.nix +++ b/modules/misc/xdg-user-dirs.nix @@ -36,6 +36,8 @@ in ''; }; + package = lib.mkPackageOption pkgs "xdg-user-dirs" { nullable = true; }; + # Well-known directory list from # https://gitlab.freedesktop.org/xdg/xdg-user-dirs/blob/master/man/user-dirs.dirs.xml @@ -135,6 +137,8 @@ in xdg.configFile."user-dirs.conf".text = "enabled=False"; + home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + home.sessionVariables = directories; home.activation.createXdgUserDirectories = lib.mkIf cfg.createDirectories ( From db9044b1198708c1e99e9008d6cee64b61cf29e5 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Tue, 7 Oct 2025 22:39:05 -0600 Subject: [PATCH 07/83] xdg.userDirs: add a `setSessionVariables` option It defaults to `true` to maintain the current behavior. This is conditionalized on 26.05, so in future `setSessionVariables` will default to `false`. --- modules/misc/xdg-user-dirs.nix | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/misc/xdg-user-dirs.nix b/modules/misc/xdg-user-dirs.nix index 200d4e76..2d8a8be3 100644 --- a/modules/misc/xdg-user-dirs.nix +++ b/modules/misc/xdg-user-dirs.nix @@ -110,6 +110,26 @@ in }; createDirectories = lib.mkEnableOption "automatic creation of the XDG user directories"; + + setSessionVariables = mkOption { + type = with types; bool; + default = lib.versionOlder config.home.stateVersion "26.05"; + defaultText = literalExpression '' + lib.versionOlder config.home.stateVersion "26.05" + ''; + description = '' + Whether to set the XDG user dir environment variables, like + `XDG_DESKTOP_DIR`. + + ::: {.note} + The recommended way to get these values is via the `xdg-user-dir` + command or by processing `$XDG_CONFIG_HOME/user-dirs.dirs` directly in + your application. + ::: + + This defaults to `true` for state version < 26.05 and `false` otherwise. + ''; + }; }; config = @@ -139,7 +159,7 @@ in home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; - home.sessionVariables = directories; + home.sessionVariables = lib.mkIf cfg.setSessionVariables directories; home.activation.createXdgUserDirectories = lib.mkIf cfg.createDirectories ( let From 9b62076484ba01e2c76141f6c8d6ecdb01cfa559 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Sat, 4 Oct 2025 17:37:58 -0600 Subject: [PATCH 08/83] xdg.userDirs: change the syntax for `extraConfig` E.g., use `MISC` instead of `XDG_MISC_DIR`, the same way `xdg-user-dirs-update` works. This is conditionalized on 26.05, so in future `XDG_MISC_DIR` will be disallowed. --- modules/misc/xdg-user-dirs.nix | 49 ++++++++++++++++------ tests/modules/misc/xdg/default.nix | 2 + tests/modules/misc/xdg/user-dirs-mixed.nix | 35 ++++++++++++++++ tests/modules/misc/xdg/user-dirs-short.nix | 30 +++++++++++++ 4 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 tests/modules/misc/xdg/user-dirs-mixed.nix create mode 100644 tests/modules/misc/xdg/user-dirs-short.nix diff --git a/modules/misc/xdg-user-dirs.nix b/modules/misc/xdg-user-dirs.nix index 2d8a8be3..b5050394 100644 --- a/modules/misc/xdg-user-dirs.nix +++ b/modules/misc/xdg-user-dirs.nix @@ -103,10 +103,33 @@ in defaultText = literalExpression "{ }"; example = literalExpression '' { - XDG_MISC_DIR = "''${config.home.homeDirectory}/Misc"; + MISC = "''${config.home.homeDirectory}/Misc"; } ''; - description = "Other user directories."; + apply = + if lib.versionOlder config.home.stateVersion "26.05" then + lib.mapAttrs' ( + k: + let + matches = lib.match "XDG_(.*)_DIR" k; + in + lib.nameValuePair ( + if matches == null then + k + else + let + name = lib.elemAt matches 0; + in + lib.warn "using keys like ‘${k}’ for xdg.userDirs.extraConfig is deprecated in favor of keys like ‘${name}’" name + ) + ) + else + lib.id; + description = '' + Other user directories. + + The key ‘MISC’ corresponds to the user-dirs entry ‘XDG_MISC_DIR’. + ''; }; createDirectories = lib.mkEnableOption "automatic creation of the XDG user directories"; @@ -136,22 +159,24 @@ in let directories = (lib.filterAttrs (n: v: !isNull v) { - XDG_DESKTOP_DIR = cfg.desktop; - XDG_DOCUMENTS_DIR = cfg.documents; - XDG_DOWNLOAD_DIR = cfg.download; - XDG_MUSIC_DIR = cfg.music; - XDG_PICTURES_DIR = cfg.pictures; - XDG_PUBLICSHARE_DIR = cfg.publicShare; - XDG_TEMPLATES_DIR = cfg.templates; - XDG_VIDEOS_DIR = cfg.videos; + DESKTOP = cfg.desktop; + DOCUMENTS = cfg.documents; + DOWNLOAD = cfg.download; + MUSIC = cfg.music; + PICTURES = cfg.pictures; + PUBLICSHARE = cfg.publicShare; + TEMPLATES = cfg.templates; + VIDEOS = cfg.videos; }) // cfg.extraConfig; + + bindings = lib.mapAttrs' (k: lib.nameValuePair "XDG_${k}_DIR") directories; in lib.mkIf cfg.enable { xdg.configFile."user-dirs.dirs".text = let # For some reason, these need to be wrapped with quotes to be valid. - wrapped = lib.mapAttrs (_: value: ''"${value}"'') directories; + wrapped = lib.mapAttrs (_: value: ''"${value}"'') bindings; in lib.generators.toKeyValue { } wrapped; @@ -159,7 +184,7 @@ in home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; - home.sessionVariables = lib.mkIf cfg.setSessionVariables directories; + home.sessionVariables = lib.mkIf cfg.setSessionVariables bindings; home.activation.createXdgUserDirectories = lib.mkIf cfg.createDirectories ( let diff --git a/tests/modules/misc/xdg/default.nix b/tests/modules/misc/xdg/default.nix index 6b80d45c..53ab57cd 100644 --- a/tests/modules/misc/xdg/default.nix +++ b/tests/modules/misc/xdg/default.nix @@ -4,5 +4,7 @@ xdg-mime-disabled = ./mime-disabled.nix; xdg-autostart = ./autostart.nix; xdg-autostart-readonly = ./autostart-readonly.nix; + xdg-user-dirs-mixed = ./user-dirs-mixed.nix; xdg-user-dirs-null = ./user-dirs-null.nix; + xdg-user-dirs-short = ./user-dirs-short.nix; } diff --git a/tests/modules/misc/xdg/user-dirs-mixed.nix b/tests/modules/misc/xdg/user-dirs-mixed.nix new file mode 100644 index 00000000..4e34e579 --- /dev/null +++ b/tests/modules/misc/xdg/user-dirs-mixed.nix @@ -0,0 +1,35 @@ +{ + config, + pkgs, + ... +}: + +{ + config = { + home.stateVersion = "25.11"; + + xdg.userDirs = { + enable = true; + extraConfig.PROJECTS = "${config.home.homeDirectory}/Projects"; + ## This will trigger a warning. + extraConfig.XDG_MISC_DIR = "${config.home.homeDirectory}/Misc"; + }; + + nmt.script = '' + configFile=home-files/.config/user-dirs.dirs + assertFileExists $configFile + assertFileContent $configFile ${pkgs.writeText "expected" '' + XDG_DESKTOP_DIR="/home/hm-user/Desktop" + XDG_DOCUMENTS_DIR="/home/hm-user/Documents" + XDG_DOWNLOAD_DIR="/home/hm-user/Downloads" + XDG_MISC_DIR="/home/hm-user/Misc" + XDG_MUSIC_DIR="/home/hm-user/Music" + XDG_PICTURES_DIR="/home/hm-user/Pictures" + XDG_PROJECTS_DIR="/home/hm-user/Projects" + XDG_PUBLICSHARE_DIR="/home/hm-user/Public" + XDG_TEMPLATES_DIR="/home/hm-user/Templates" + XDG_VIDEOS_DIR="/home/hm-user/Videos" + ''} + ''; + }; +} diff --git a/tests/modules/misc/xdg/user-dirs-short.nix b/tests/modules/misc/xdg/user-dirs-short.nix new file mode 100644 index 00000000..4dfe529c --- /dev/null +++ b/tests/modules/misc/xdg/user-dirs-short.nix @@ -0,0 +1,30 @@ +{ + config, + pkgs, + ... +}: + +{ + config = { + xdg.userDirs = { + enable = true; + extraConfig.PROJECTS = "${config.home.homeDirectory}/Projects"; + }; + + nmt.script = '' + configFile=home-files/.config/user-dirs.dirs + assertFileExists $configFile + assertFileContent $configFile ${pkgs.writeText "expected" '' + XDG_DESKTOP_DIR="/home/hm-user/Desktop" + XDG_DOCUMENTS_DIR="/home/hm-user/Documents" + XDG_DOWNLOAD_DIR="/home/hm-user/Downloads" + XDG_MUSIC_DIR="/home/hm-user/Music" + XDG_PICTURES_DIR="/home/hm-user/Pictures" + XDG_PROJECTS_DIR="/home/hm-user/Projects" + XDG_PUBLICSHARE_DIR="/home/hm-user/Public" + XDG_TEMPLATES_DIR="/home/hm-user/Templates" + XDG_VIDEOS_DIR="/home/hm-user/Videos" + ''} + ''; + }; +} From c8f9edda941c5aa8dfc8194cc95c2ae471d55cdb Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Thu, 15 Jan 2026 20:03:17 -0700 Subject: [PATCH 09/83] xdg.userDirs: add notes for `stateVersion` changes --- docs/release-notes/rl-2605.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/release-notes/rl-2605.md b/docs/release-notes/rl-2605.md index bcc6c7c7..4758869a 100644 --- a/docs/release-notes/rl-2605.md +++ b/docs/release-notes/rl-2605.md @@ -23,3 +23,12 @@ changes are only active if the `home.stateVersion` option is set to - The [](#opt-programs.zsh.dotDir) option now defaults to the XDG configuration directory (usually `~/.config/zsh`) when [](#opt-xdg.enable) is true. + +- The [](#opt-xdg.userDirs.setSessionVariables) option now defaults to `false` + instead of `true`. + +- The [](#opt-xdg.userDirs.extraConfig) option no longer accepts keys of the + form `XDG__DIR`, they should be now be just the name. For example, if + you had the key `XDG_DESKTOP_DIR` before, you should now use the key + `DESKTOP`. Home Manager 26.05 introduced a warning when the `XDG__DIR` + form is used. From 6c79e409a5605ac39e96f854d3d4bfd6e7b4f79a Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 4 Feb 2026 21:05:38 -0600 Subject: [PATCH 10/83] news: add xdg-user-dirs changes entry Inform users about the various changes to xdg-user-dirs. Signed-off-by: Austin Horstman --- .../misc/news/2026/02/2026-02-04_21-02-20.nix | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 modules/misc/news/2026/02/2026-02-04_21-02-20.nix diff --git a/modules/misc/news/2026/02/2026-02-04_21-02-20.nix b/modules/misc/news/2026/02/2026-02-04_21-02-20.nix new file mode 100644 index 00000000..a9c02aab --- /dev/null +++ b/modules/misc/news/2026/02/2026-02-04_21-02-20.nix @@ -0,0 +1,19 @@ +{ config, ... }: +{ + time = "2026-02-05T03:02:20+00:00"; + condition = config.xdg.userDirs.enable; + message = '' + The `xdg.userDirs` module now supports non-Linux platforms. + + The `xdg.userDirs.package` option is now available. Set it to `null` + to prevent Home Manager from installing `xdg-user-dirs`. + + The `xdg.userDirs.extraConfig` option no longer recommends keys of the + form `XDG__DIR`; use just `` instead (e.g. `DESKTOP`). + The old form is deprecated and will emit a warning. + + The `xdg.userDirs.setSessionVariables` option was added to control + whether XDG user directory environment variables like `XDG_DESKTOP_DIR` are + set. It now defaults to `false` for `home.stateVersion` 26.05 and later. + ''; +} From 715e6d7f891372a747a1ce5b785b9d8ed846d5f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ana=20=E6=B1=9F?= Date: Thu, 29 Jan 2026 23:20:09 +0100 Subject: [PATCH 11/83] programs.git: fix assertion message inconsistency --- modules/programs/git.nix | 52 ++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/modules/programs/git.nix b/modules/programs/git.nix index b28380f6..a9ef6887 100644 --- a/modules/programs/git.nix +++ b/modules/programs/git.nix @@ -332,21 +332,43 @@ in home.packages = lib.optionals (cfg.package != null) [ cfg.package ]; assertions = [ - { - assertion = - let - enabled = [ - (config.programs.delta.enable && config.programs.delta.enableGitIntegration) - (config.programs.diff-highlight.enable && config.programs.diff-highlight.enableGitIntegration) - (config.programs.diff-so-fancy.enable && config.programs.diff-so-fancy.enableGitIntegration) - (config.programs.difftastic.enable && config.programs.difftastic.git.enable) - (config.programs.patdiff.enable && config.programs.patdiff.enableGitIntegration) - (config.programs.riff.enable && config.programs.riff.enableGitIntegration) - ]; - in - lib.count lib.id enabled <= 1; - message = "Only one of 'programs.git.delta.enable' or 'programs.git.difftastic.enable' or 'programs.git.diff-so-fancy.enable' or 'programs.git.diff-highlight' or 'programs.git.patdiff' can be set to true at the same time."; - } + ( + let + configOf = + { + name, + gitIntegrationOption ? [ "enableGitIntegration" ], + }: + { + name = "programs.${name}.${lib.concatStringsSep "." gitIntegrationOption}"; + value = + config.programs.${name}.enable && lib.getAttrFromPath gitIntegrationOption config.programs.${name}; + }; + enabled = builtins.filter (x: x.value) ( + map configOf [ + { name = "delta"; } + { name = "diff-highlight"; } + { name = "diff-so-fancy"; } + { + name = "difftastic"; + gitIntegrationOption = [ + "git" + "enable" + ]; + } + { name = "patdiff"; } + { name = "riff"; } + ] + ); + in + { + assertion = lib.length enabled <= 1; + message = '' + Only one of the following options can be enabled at a time. + - ${lib.concatStringsSep "\n - " (map (x: "`${x.name}'") enabled)} + ''; + } + ) ]; xdg.configFile = { From cfb3b544f9fa7942556a9ad85064733242a8222b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ana=20=E6=B1=9F?= Date: Fri, 30 Jan 2026 13:46:43 +0100 Subject: [PATCH 12/83] programs.git: test git integration options assertion --- tests/modules/programs/git/default.nix | 1 + .../git/git-integration-assertion.nix | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/modules/programs/git/git-integration-assertion.nix diff --git a/tests/modules/programs/git/default.nix b/tests/modules/programs/git/default.nix index 49f58e69..5b1bf2b9 100644 --- a/tests/modules/programs/git/default.nix +++ b/tests/modules/programs/git/default.nix @@ -10,4 +10,5 @@ git-with-lfs = ./git-with-lfs.nix; git-with-maintenance = ./git-with-maintenance.nix; git-settings-deprecations = ./git-settings-deprecations.nix; + git-integration-assertion = ./git-integration-assertion.nix; } diff --git a/tests/modules/programs/git/git-integration-assertion.nix b/tests/modules/programs/git/git-integration-assertion.nix new file mode 100644 index 00000000..e66347e7 --- /dev/null +++ b/tests/modules/programs/git/git-integration-assertion.nix @@ -0,0 +1,23 @@ +let + enable = { + enable = true; + enableGitIntegration = true; + }; +in +{ + programs = { + delta = enable; + # FIXME(leana8959): these two aren't caught by the tests. + # diff-highlight = enable; + # diff-so-fancy = enable; + patdiff = enable; + }; + + test.asserts.assertions.expected = [ + '' + Only one of the following options can be enabled at a time. + - `programs.delta.enableGitIntegration' + - `programs.patdiff.enableGitIntegration' + '' + ]; +} From 6f64dee49143322e45db65f1e61584944ae72b67 Mon Sep 17 00:00:00 2001 From: mikaeladev <100416544+mikaeladev@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:48:14 +0000 Subject: [PATCH 13/83] maintainers: add mikaeladev --- modules/lib/maintainers.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/lib/maintainers.nix b/modules/lib/maintainers.nix index fae1f15f..a853024f 100644 --- a/modules/lib/maintainers.nix +++ b/modules/lib/maintainers.nix @@ -368,6 +368,12 @@ github = "mifom"; githubId = 23462908; }; + mikaeladev = { + name = "mikaeladev"; + email = "mikaeladev@users.noreply.github.com"; + github = "mikaeladev"; + githubId = 100416544; + }; mikilio = { name = "mikilio"; email = "official.mikilio+dev@gmail.com"; From 04e5203db66417d548ae1ff188a9f591836dfaa7 Mon Sep 17 00:00:00 2001 From: mikaeladev <100416544+mikaeladev@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:48:22 +0000 Subject: [PATCH 14/83] prismlauncher: add module --- .../misc/news/2026/01/2026-01-27_20-02-41.nix | 10 ++ modules/programs/prismlauncher.nix | 114 ++++++++++++++++++ .../programs/prismlauncher/default.nix | 3 + .../programs/prismlauncher/settings.nix | 65 ++++++++++ 4 files changed, 192 insertions(+) create mode 100644 modules/misc/news/2026/01/2026-01-27_20-02-41.nix create mode 100644 modules/programs/prismlauncher.nix create mode 100644 tests/modules/programs/prismlauncher/default.nix create mode 100644 tests/modules/programs/prismlauncher/settings.nix diff --git a/modules/misc/news/2026/01/2026-01-27_20-02-41.nix b/modules/misc/news/2026/01/2026-01-27_20-02-41.nix new file mode 100644 index 00000000..be050265 --- /dev/null +++ b/modules/misc/news/2026/01/2026-01-27_20-02-41.nix @@ -0,0 +1,10 @@ +{ + time = "2026-01-27T20:02:41+00:00"; + condition = true; + message = '' + A new module is available: 'programs.prismlauncher'. + + An open-source Minecraft launcher that can manage multiple instances, + accounts, and mods. Focused on user freedom and free redistributability. + ''; +} diff --git a/modules/programs/prismlauncher.nix b/modules/programs/prismlauncher.nix new file mode 100644 index 00000000..fc15c6a3 --- /dev/null +++ b/modules/programs/prismlauncher.nix @@ -0,0 +1,114 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + inherit (pkgs.stdenv.hostPlatform) isDarwin; + + inherit (lib) + escapeShellArg + getExe + listToAttrs + literalExpression + mkEnableOption + mkIf + mkMerge + mkOption + mkPackageOption + types + ; + + cfg = config.programs.prismlauncher; + + dataDir = + if (isDarwin && !config.xdg.enable) then + "Library/Application Support/PrismLauncher" + else + "${config.xdg.dataHome}/PrismLauncher"; + + iniFormat = pkgs.formats.ini { }; + + impureConfigMerger = filePath: staticSettingsFile: emptySettingsFile: '' + mkdir -p $(dirname '${escapeShellArg filePath}') + + if [ ! -e '${escapeShellArg filePath}' ]; then + cat '${escapeShellArg emptySettingsFile}' > '${escapeShellArg filePath}' + fi + + ${getExe pkgs.crudini} --merge --ini-options=nospace \ + '${escapeShellArg filePath}' < '${escapeShellArg staticSettingsFile}' + ''; +in + +{ + meta.maintainers = with lib.hm.maintainers; [ + mikaeladev + ]; + + options.programs.prismlauncher = { + enable = mkEnableOption "Prism Launcher"; + + package = mkPackageOption pkgs "prismlauncher" { nullable = true; }; + + extraPackages = mkOption { + type = types.listOf types.package; + default = [ ]; + description = '' + Additional theme packages to install to the user environment. + + Themes can be sourced from and should + install to `$out/share/PrismLauncher/{themes,iconthemes,catpacks}`. + ''; + }; + + icons = mkOption { + type = types.listOf types.path; + default = [ ]; + example = literalExpression "[ ./java.png ]"; + description = '' + List of paths to instance icons. + + These will be linked in {file}`$XDG_DATA_HOME/PrismLauncher/icons` on Linux and + {file}`~/Library/Application Support/PrismLauncher/icons` on macOS. + ''; + }; + + settings = mkOption { + type = types.attrsOf iniFormat.lib.types.atom; + default = { }; + example = { + ShowConsole = true; + ConsoleMaxLines = 100000; + }; + description = '' + Configuration written to {file}`prismlauncher.cfg`. + ''; + }; + }; + + config = mkIf cfg.enable { + home.packages = mkMerge ([ (mkIf (cfg.package != null) [ cfg.package ]) ] ++ cfg.extraPackages); + + home.activation = { + prismlauncherConfigActivation = ( + lib.hm.dag.entryAfter [ "linkGeneration" ] ( + impureConfigMerger "${dataDir}/prismlauncher.cfg" (iniFormat.generate "prismlauncher-static.cfg" { + General = cfg.settings; + }) (iniFormat.generate "prismlauncher-empty.cfg" { General = { }; }) + ) + ); + }; + + home.file = mkIf (cfg.icons != [ ]) ( + listToAttrs ( + map (source: { + name = "${dataDir}/icons/${baseNameOf source}"; + value = { inherit source; }; + }) cfg.icons + ) + ); + }; +} diff --git a/tests/modules/programs/prismlauncher/default.nix b/tests/modules/programs/prismlauncher/default.nix new file mode 100644 index 00000000..4400999a --- /dev/null +++ b/tests/modules/programs/prismlauncher/default.nix @@ -0,0 +1,3 @@ +{ + prismlauncher-settings = ./settings.nix; +} diff --git a/tests/modules/programs/prismlauncher/settings.nix b/tests/modules/programs/prismlauncher/settings.nix new file mode 100644 index 00000000..ac0843de --- /dev/null +++ b/tests/modules/programs/prismlauncher/settings.nix @@ -0,0 +1,65 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + configPath = ".local/share/PrismLauncher/prismlauncher.cfg"; + + preexistingConfig = pkgs.writeText "preexisting.cfg" '' + [General] + ApplicationTheme=system + MaxMemAlloc=8192 + MinMemAlloc=512 + ''; + + expectedConfig = pkgs.writeText "expected.cfg" '' + [General] + ApplicationTheme=dark + MaxMemAlloc=8192 + MinMemAlloc=512 + ConsoleMaxLines=100000 + ShowConsole=true + ''; + + activationScript = pkgs.writeScript "activation" config.home.activation.prismlauncherConfigActivation.data; +in + +{ + programs.prismlauncher = { + enable = true; + package = config.lib.test.mkStubPackage { }; + + settings = { + ApplicationTheme = "dark"; + ShowConsole = true; + ConsoleMaxLines = 100000; + }; + }; + + home.homeDirectory = lib.mkForce "/@TMPDIR@/hm-user"; + + nmt.script = '' + export HOME=$TMPDIR/hm-user + + # write preexisting config + mkdir -p $HOME/.local/share/PrismLauncher + cat ${preexistingConfig} > $HOME/${configPath} + + # run the activation script + substitute ${activationScript} $TMPDIR/activate --subst-var TMPDIR + chmod +x $TMPDIR/activate + $TMPDIR/activate + + # validate the merged config + assertFileExists "$HOME/${configPath}" + assertFileContent "$HOME/${configPath}" "${expectedConfig}" + + # test idempotence + $TMPDIR/activate + assertFileExists "$HOME/${configPath}" + assertFileContent "$HOME/${configPath}" "${expectedConfig}" + ''; +} From bbccee87134fdf6d153d10ff8c06f94d7339c830 Mon Sep 17 00:00:00 2001 From: Anton Date: Thu, 5 Feb 2026 14:36:40 +0500 Subject: [PATCH 15/83] halloy: fix URL in description --- modules/programs/halloy.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/halloy.nix b/modules/programs/halloy.nix index 29b9aac6..c6ff774e 100644 --- a/modules/programs/halloy.nix +++ b/modules/programs/halloy.nix @@ -36,7 +36,7 @@ in }; description = '' Configuration settings for halloy. All available options can be - found here: . Note that + found here: . Note that halloy requires at least one `server` to be configured, see example. ''; }; From 332027bc0ac95d230b8c680dcc44e56f9a3c9339 Mon Sep 17 00:00:00 2001 From: K900 Date: Thu, 5 Feb 2026 19:20:28 +0300 Subject: [PATCH 16/83] treewide: use pkgs.lndir instead of pkgs.xorg.lndir Follow the change in nixpkgs. --- modules/files.nix | 2 +- tests/default.nix | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/files.nix b/modules/files.nix index bc96fbe8..e26dbd46 100644 --- a/modules/files.nix +++ b/modules/files.nix @@ -291,7 +291,7 @@ in home-files = pkgs.runCommandLocal "home-manager-files" { - nativeBuildInputs = [ pkgs.xorg.lndir ]; + nativeBuildInputs = [ pkgs.lndir ]; } ( '' diff --git a/tests/default.nix b/tests/default.nix index aa76438c..19d2b0f8 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -64,9 +64,8 @@ let # Needed by pretty much all tests that have anything to do with fish. babelfish fish + lndir ; - - xorg = super.xorg.overrideScope (self: super: { inherit (pkgs.xorg) lndir; }); }; outer = From f915881ba7d700821a559d6f4c6c6f468cc88cd6 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 5 Feb 2026 11:10:34 -0600 Subject: [PATCH 17/83] prismlauncher: code style and lint cleanup Signed-off-by: Austin Horstman --- modules/programs/prismlauncher.nix | 84 +++++++++++++++--------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/modules/programs/prismlauncher.nix b/modules/programs/prismlauncher.nix index fc15c6a3..b426ad33 100644 --- a/modules/programs/prismlauncher.nix +++ b/modules/programs/prismlauncher.nix @@ -10,37 +10,16 @@ let inherit (lib) escapeShellArg - getExe listToAttrs literalExpression - mkEnableOption mkIf - mkMerge mkOption - mkPackageOption types ; cfg = config.programs.prismlauncher; - dataDir = - if (isDarwin && !config.xdg.enable) then - "Library/Application Support/PrismLauncher" - else - "${config.xdg.dataHome}/PrismLauncher"; - iniFormat = pkgs.formats.ini { }; - - impureConfigMerger = filePath: staticSettingsFile: emptySettingsFile: '' - mkdir -p $(dirname '${escapeShellArg filePath}') - - if [ ! -e '${escapeShellArg filePath}' ]; then - cat '${escapeShellArg emptySettingsFile}' > '${escapeShellArg filePath}' - fi - - ${getExe pkgs.crudini} --merge --ini-options=nospace \ - '${escapeShellArg filePath}' < '${escapeShellArg staticSettingsFile}' - ''; in { @@ -49,9 +28,9 @@ in ]; options.programs.prismlauncher = { - enable = mkEnableOption "Prism Launcher"; + enable = lib.mkEnableOption "Prism Launcher"; - package = mkPackageOption pkgs "prismlauncher" { nullable = true; }; + package = lib.mkPackageOption pkgs "prismlauncher" { nullable = true; }; extraPackages = mkOption { type = types.listOf types.package; @@ -89,26 +68,45 @@ in }; }; - config = mkIf cfg.enable { - home.packages = mkMerge ([ (mkIf (cfg.package != null) [ cfg.package ]) ] ++ cfg.extraPackages); + config = + let + dataDir = + if (isDarwin && !config.xdg.enable) then + "Library/Application Support/PrismLauncher" + else + "${config.xdg.dataHome}/PrismLauncher"; - home.activation = { - prismlauncherConfigActivation = ( - lib.hm.dag.entryAfter [ "linkGeneration" ] ( - impureConfigMerger "${dataDir}/prismlauncher.cfg" (iniFormat.generate "prismlauncher-static.cfg" { - General = cfg.settings; - }) (iniFormat.generate "prismlauncher-empty.cfg" { General = { }; }) - ) - ); + impureConfigMerger = filePath: staticSettingsFile: emptySettingsFile: '' + mkdir -p $(dirname '${escapeShellArg filePath}') + + if [ ! -e '${escapeShellArg filePath}' ]; then + cat '${escapeShellArg emptySettingsFile}' > '${escapeShellArg filePath}' + fi + + ${lib.getExe pkgs.crudini} --merge --ini-options=nospace \ + '${escapeShellArg filePath}' < '${escapeShellArg staticSettingsFile}' + ''; + in + mkIf cfg.enable { + home = { + packages = lib.mkMerge ([ (mkIf (cfg.package != null) [ cfg.package ]) ] ++ cfg.extraPackages); + + activation = { + prismlauncherConfigActivation = lib.hm.dag.entryAfter [ "linkGeneration" ] ( + impureConfigMerger "${dataDir}/prismlauncher.cfg" (iniFormat.generate "prismlauncher-static.cfg" { + General = cfg.settings; + }) (iniFormat.generate "prismlauncher-empty.cfg" { General = { }; }) + ); + }; + + file = mkIf (cfg.icons != [ ]) ( + listToAttrs ( + map (source: { + name = "${dataDir}/icons/${baseNameOf source}"; + value = { inherit source; }; + }) cfg.icons + ) + ); + }; }; - - home.file = mkIf (cfg.icons != [ ]) ( - listToAttrs ( - map (source: { - name = "${dataDir}/icons/${baseNameOf source}"; - value = { inherit source; }; - }) cfg.icons - ) - ); - }; } From 471e6a065f9efed51488d7c51a9abbd387df91b8 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 5 Feb 2026 13:03:24 -0600 Subject: [PATCH 18/83] prismlauncher: merge only when configured Signed-off-by: Austin Horstman --- modules/programs/prismlauncher.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/prismlauncher.nix b/modules/programs/prismlauncher.nix index b426ad33..decc3e21 100644 --- a/modules/programs/prismlauncher.nix +++ b/modules/programs/prismlauncher.nix @@ -91,7 +91,7 @@ in home = { packages = lib.mkMerge ([ (mkIf (cfg.package != null) [ cfg.package ]) ] ++ cfg.extraPackages); - activation = { + activation = lib.mkIf (cfg.settings != { }) { prismlauncherConfigActivation = lib.hm.dag.entryAfter [ "linkGeneration" ] ( impureConfigMerger "${dataDir}/prismlauncher.cfg" (iniFormat.generate "prismlauncher-static.cfg" { General = cfg.settings; From 6cee0821577643e0b34e2c5d9a90d0b1b5cdca70 Mon Sep 17 00:00:00 2001 From: Francesco Noacco Date: Fri, 6 Feb 2026 11:42:23 +0100 Subject: [PATCH 19/83] zed-editor: don't generate empty keymap files default value for user keymaps is an empty list, but the check was made against an empty set, generating the file when no value was given --- modules/programs/zed-editor.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/zed-editor.nix b/modules/programs/zed-editor.nix index e3009df2..7b5b9e88 100644 --- a/modules/programs/zed-editor.nix +++ b/modules/programs/zed-editor.nix @@ -304,7 +304,7 @@ in (mkIf (!cfg.mutableUserSettings && mergedSettings != { }) { "zed/settings.json".source = jsonFormat.generate "zed-user-settings" mergedSettings; }) - (mkIf (!cfg.mutableUserKeymaps && cfg.userKeymaps != { }) { + (mkIf (!cfg.mutableUserKeymaps && cfg.userKeymaps != [ ]) { "zed/keymap.json".source = jsonFormat.generate "zed-user-keymaps" cfg.userKeymaps; }) (mkIf (!cfg.mutableUserTasks && cfg.userTasks != [ ]) { From cbd8a72e5fe6af19d40e2741dc440d9227836860 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 7 Feb 2026 09:00:55 -0600 Subject: [PATCH 20/83] treewide: remove xorg package set Xorg package set removed, now aliases. Signed-off-by: Austin Horstman --- modules/config/home-cursor.nix | 2 +- modules/programs/autorandr.nix | 2 +- modules/services/fusuma.nix | 6 +++--- modules/services/grobi.nix | 6 +++--- modules/services/screen-locker.nix | 2 +- modules/xresources.nix | 2 +- modules/xsession.nix | 2 +- tests/modules/misc/xsession/basic.nix | 10 ---------- .../modules/misc/xsession/keyboard-without-layout.nix | 10 ---------- tests/modules/services/fusuma/expected-service.service | 2 +- tests/modules/services/fusuma/service.nix | 2 +- 11 files changed, 13 insertions(+), 33 deletions(-) diff --git a/modules/config/home-cursor.nix b/modules/config/home-cursor.nix index 4db3cc19..eeb71110 100644 --- a/modules/config/home-cursor.nix +++ b/modules/config/home-cursor.nix @@ -224,7 +224,7 @@ in (mkIf cfg.x11.enable { xsession.profileExtra = '' - ${pkgs.xorg.xsetroot}/bin/xsetroot -xcf ${cursorPath} ${toString cfg.size} + ${lib.getExe pkgs.xsetroot} -xcf ${cursorPath} ${toString cfg.size} ''; xresources.properties = { diff --git a/modules/programs/autorandr.nix b/modules/programs/autorandr.nix index 940070d3..41a2ed8a 100644 --- a/modules/programs/autorandr.nix +++ b/modules/programs/autorandr.nix @@ -371,7 +371,7 @@ in exit 1 esac - echo "Xft.dpi: $DPI" | ''${pkgs.xorg.xrdb}/bin/xrdb -merge + echo "Xft.dpi: $DPI" | ''${lib.getExe pkgs.xrdb} -merge ''' }; } diff --git a/modules/services/fusuma.nix b/modules/services/fusuma.nix index e9d5c654..d200636c 100644 --- a/modules/services/fusuma.nix +++ b/modules/services/fusuma.nix @@ -100,11 +100,11 @@ in default = with pkgs; [ xdotool coreutils - xorg.xprop + xprop ]; - defaultText = literalExpression "pkgs.xdotool pkgs.coreutils pkgs.xorg.xprop"; + defaultText = literalExpression "pkgs.xdotool pkgs.coreutils pkgs.xprop"; example = literalExpression '' - with pkgs; [ xdotool coreutils xorg.xprop ]; + with pkgs; [ xdotool coreutils xprop ]; ''; description = '' Extra packages needs to bring to the scope of fusuma service. diff --git a/modules/services/grobi.nix b/modules/services/grobi.nix index eb2daef6..64344548 100644 --- a/modules/services/grobi.nix +++ b/modules/services/grobi.nix @@ -45,7 +45,7 @@ in primary = true; atomic = true; execute_after = [ - "''${pkgs.xorg.xrandr}/bin/xrandr --dpi 96" + "''${lib.getExe pkgs.xrandr} --dpi 96" "''${pkgs.xmonad-with-packages}/bin/xmonad --restart"; ]; } @@ -56,7 +56,7 @@ in primary = true; atomic = true; execute_after = [ - "''${pkgs.xorg.xrandr}/bin/xrandr --dpi 120" + "''${lib.getExe pkgs.xrandr} --dpi 120" "''${pkgs.xmonad-with-packages}/bin/xmonad --restart"; ]; } @@ -92,7 +92,7 @@ in ExecStart = "${lib.getExe cfg.package} watch -v"; Restart = "always"; RestartSec = "2s"; - Environment = [ "PATH=${pkgs.xorg.xrandr}/bin:${pkgs.bash}/bin" ]; + Environment = [ "PATH=${pkgs.xrandr}/bin:${pkgs.bash}/bin" ]; }; Install = { diff --git a/modules/services/screen-locker.nix b/modules/services/screen-locker.nix index f3639147..c8269327 100644 --- a/modules/services/screen-locker.nix +++ b/modules/services/screen-locker.nix @@ -161,7 +161,7 @@ in }; } (mkIf (!cfg.xautolock.enable) { - systemd.user.services.xss-lock.Service.ExecStartPre = "${pkgs.xorg.xset}/bin/xset s ${ + systemd.user.services.xss-lock.Service.ExecStartPre = "${lib.getExe pkgs.xset} s ${ toString (cfg.inactiveInterval * 60) } ${toString cfg.xss-lock.screensaverCycle}"; }) diff --git a/modules/xresources.nix b/modules/xresources.nix index fccc702f..b038bca6 100644 --- a/modules/xresources.nix +++ b/modules/xresources.nix @@ -31,7 +31,7 @@ let in "${n}: ${formatValue v}"; - xrdbMerge = "${pkgs.xorg.xrdb}/bin/xrdb -merge ${cfg.path}"; + xrdbMerge = "${lib.getExe pkgs.xrdb} -merge ${cfg.path}"; in { diff --git a/modules/xsession.nix b/modules/xsession.nix index 81ecd254..f21481e8 100644 --- a/modules/xsession.nix +++ b/modules/xsession.nix @@ -150,7 +150,7 @@ in ++ [ "-option ''" ] ++ map (v: "-option '${v}'") options; in - "${pkgs.xorg.setxkbmap}/bin/setxkbmap ${toString args}"; + "${lib.getExe pkgs.setxkbmap} ${toString args}"; }; }; diff --git a/tests/modules/misc/xsession/basic.nix b/tests/modules/misc/xsession/basic.nix index 70ff0592..86d78ab4 100644 --- a/tests/modules/misc/xsession/basic.nix +++ b/tests/modules/misc/xsession/basic.nix @@ -8,16 +8,6 @@ profileExtra = "profile extra commands"; }; - nixpkgs.overlays = [ - (self: super: { - xorg = super.xorg // { - setxkbmap = super.xorg.setxkbmap // { - outPath = "@setxkbmap@"; - }; - }; - }) - ]; - nmt.script = '' assertFileExists home-files/.xprofile assertFileContent \ diff --git a/tests/modules/misc/xsession/keyboard-without-layout.nix b/tests/modules/misc/xsession/keyboard-without-layout.nix index 0618bca4..8a22e17a 100644 --- a/tests/modules/misc/xsession/keyboard-without-layout.nix +++ b/tests/modules/misc/xsession/keyboard-without-layout.nix @@ -17,16 +17,6 @@ profileExtra = "profile extra commands"; }; - nixpkgs.overlays = [ - (self: super: { - xorg = super.xorg // { - setxkbmap = super.xorg.setxkbmap // { - outPath = "@setxkbmap@"; - }; - }; - }) - ]; - nmt.script = '' assertFileExists home-files/.config/systemd/user/setxkbmap.service assertFileContent \ diff --git a/tests/modules/services/fusuma/expected-service.service b/tests/modules/services/fusuma/expected-service.service index e84077da..6dda13d6 100644 --- a/tests/modules/services/fusuma/expected-service.service +++ b/tests/modules/services/fusuma/expected-service.service @@ -2,7 +2,7 @@ WantedBy=graphical-session.target [Service] -Environment=PATH=@coreutils@/bin:@xdotool@/bin:@xorg.xprop@/bin +Environment=PATH=@coreutils@/bin:@xdotool@/bin:@xprop@/bin ExecStart=@fusuma@/bin/fusuma [Unit] diff --git a/tests/modules/services/fusuma/service.nix b/tests/modules/services/fusuma/service.nix index 028054e4..ea1679d3 100644 --- a/tests/modules/services/fusuma/service.nix +++ b/tests/modules/services/fusuma/service.nix @@ -8,7 +8,7 @@ extraPackages = [ (config.lib.test.mkStubPackage { outPath = "@coreutils@"; }) (config.lib.test.mkStubPackage { outPath = "@xdotool@"; }) - (config.lib.test.mkStubPackage { outPath = "@xorg.xprop@"; }) + (config.lib.test.mkStubPackage { outPath = "@xprop@"; }) ]; settings = { }; }; From f5d50fd8cba8a92af2dabc317f8e3d4e722ae0a6 Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Sun, 8 Feb 2026 10:58:23 -0300 Subject: [PATCH 21/83] lazyworktree: add module --- modules/programs/lazyworktree.nix | 48 +++++++++++++++++++ .../modules/programs/lazyworktree/config.yaml | 8 ++++ .../modules/programs/lazyworktree/default.nix | 1 + .../programs/lazyworktree/settings.nix | 21 ++++++++ 4 files changed, 78 insertions(+) create mode 100644 modules/programs/lazyworktree.nix create mode 100644 tests/modules/programs/lazyworktree/config.yaml create mode 100644 tests/modules/programs/lazyworktree/default.nix create mode 100644 tests/modules/programs/lazyworktree/settings.nix diff --git a/modules/programs/lazyworktree.nix b/modules/programs/lazyworktree.nix new file mode 100644 index 00000000..ba675c09 --- /dev/null +++ b/modules/programs/lazyworktree.nix @@ -0,0 +1,48 @@ +{ + lib, + pkgs, + config, + ... +}: +let + inherit (lib) + mkIf + mkEnableOption + mkPackageOption + mkOption + ; + cfg = config.programs.lazyworktree; + yamlFormat = pkgs.formats.yaml { }; +in +{ + meta.maintainers = with lib.hm.maintainers; [ aguirre-matteo ]; + options.programs.lazyworktree = { + enable = mkEnableOption "lazyworktree"; + package = mkPackageOption pkgs "lazyworktree" { nullable = true; }; + settings = mkOption { + inherit (yamlFormat) type; + default = { }; + example = { + worktree_dir = "~/.local/share/worktrees"; + sort_mode = "switched"; + auto_fetch_prs = false; + auto_refresh = true; + refresh_interval = 10; + icon_set = "nerd-font-v3"; + search_auto_select = false; + fuzzy_finder_input = false; + }; + description = '' + Configuration settings for lazyworktree. All the available options can be found here: + + ''; + }; + }; + + config = mkIf cfg.enable { + home.packages = mkIf (cfg.package != null) [ cfg.package ]; + xdg.configFile."lazyworktree/config.yaml" = mkIf (cfg.settings != { }) { + source = yamlFormat.generate "lazyworktree.yaml" cfg.settings; + }; + }; +} diff --git a/tests/modules/programs/lazyworktree/config.yaml b/tests/modules/programs/lazyworktree/config.yaml new file mode 100644 index 00000000..76839ef1 --- /dev/null +++ b/tests/modules/programs/lazyworktree/config.yaml @@ -0,0 +1,8 @@ +auto_fetch_prs: false +auto_refresh: true +fuzzy_finder_input: false +icon_set: nerd-font-v3 +refresh_interval: 10 +search_auto_select: false +sort_mode: switched +worktree_dir: ~/.local/share/worktrees diff --git a/tests/modules/programs/lazyworktree/default.nix b/tests/modules/programs/lazyworktree/default.nix new file mode 100644 index 00000000..cc19e1b1 --- /dev/null +++ b/tests/modules/programs/lazyworktree/default.nix @@ -0,0 +1 @@ +{ lazyworktree-settings = ./settings.nix; } diff --git a/tests/modules/programs/lazyworktree/settings.nix b/tests/modules/programs/lazyworktree/settings.nix new file mode 100644 index 00000000..d5a77f20 --- /dev/null +++ b/tests/modules/programs/lazyworktree/settings.nix @@ -0,0 +1,21 @@ +{ + programs.lazyworktree = { + enable = true; + settings = { + worktree_dir = "~/.local/share/worktrees"; + sort_mode = "switched"; + auto_fetch_prs = false; + auto_refresh = true; + refresh_interval = 10; + icon_set = "nerd-font-v3"; + search_auto_select = false; + fuzzy_finder_input = false; + }; + }; + + nmt.script = '' + assertFileExists home-files/.config/lazyworktree/config.yaml + assertFileContent home-files/.config/lazyworktree/config.yaml \ + ${./config.yaml} + ''; +} From 42c607ecb4473db62df303553eb77ed8ad0a3059 Mon Sep 17 00:00:00 2001 From: Aguirre Matteo Date: Sun, 8 Feb 2026 11:01:23 -0300 Subject: [PATCH 22/83] news: add lazyworktree entry --- modules/misc/news/2026/02/2026-02-08_10-59-14.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 modules/misc/news/2026/02/2026-02-08_10-59-14.nix diff --git a/modules/misc/news/2026/02/2026-02-08_10-59-14.nix b/modules/misc/news/2026/02/2026-02-08_10-59-14.nix new file mode 100644 index 00000000..4fe41f90 --- /dev/null +++ b/modules/misc/news/2026/02/2026-02-08_10-59-14.nix @@ -0,0 +1,10 @@ +{ + time = "2026-02-08T13:59:14+00:00"; + condition = true; + message = '' + A new module is available: `programs.lazyworktree` + + LazyWorktree is a TUI for Git worktrees. It provides a keyboard-driven + workflow for creating, inspecting, and navigating worktrees within a repository. + ''; +} From afaa85cf32a3f0d093afe4a49a14f97b0514e6f9 Mon Sep 17 00:00:00 2001 From: Francesco Noacco Date: Sat, 7 Feb 2026 14:21:07 +0100 Subject: [PATCH 23/83] vivid: add nushell integration --- modules/programs/vivid.nix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/programs/vivid.nix b/modules/programs/vivid.nix index f4fe8009..301d8d70 100644 --- a/modules/programs/vivid.nix +++ b/modules/programs/vivid.nix @@ -17,8 +17,11 @@ let mkBashIntegrationOption mkZshIntegrationOption mkFishIntegrationOption + mkNushellIntegrationOption ; + inherit (lib.hm.nushell) mkNushellInline; + cfg = config.programs.vivid; yamlFormat = pkgs.formats.yaml { }; in @@ -35,6 +38,7 @@ in enableBashIntegration = mkBashIntegrationOption { inherit config; }; enableZshIntegration = mkZshIntegrationOption { inherit config; }; enableFishIntegration = mkFishIntegrationOption { inherit config; }; + enableNushellIntegration = mkNushellIntegrationOption { inherit config; }; colorMode = mkOption { type = @@ -160,5 +164,9 @@ in programs.fish.interactiveShellInit = mkIf cfg.enableFishIntegration '' set -gx LS_COLORS "$(${vividCommand})" ''; + + programs.nushell.environmentVariables = mkIf cfg.enableNushellIntegration { + LS_COLORS = mkNushellInline vividCommand; + }; }; } From a0da0f24fdb24861e793355a28654f6e5a479c41 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 05:18:24 +0000 Subject: [PATCH 24/83] 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/e6eae2ee2110f3d31110d5c222cd395303343b08?narHash=sha256-KHFT9UWOF2yRPlAnSXQJh6uVcgNcWlFqqiAZ7OVlHNc%3D' (2026-02-03) → 'github:NixOS/nixpkgs/00c21e4c93d963c50d4c0c89bfa84ed6e0694df2?narHash=sha256-AYqlWrX09%2BHvGs8zM6ebZ1pwUqjkfpnv8mewYwAo%2BiM%3D' (2026-02-04) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index c2a8a2cd..dbf2fd5e 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1770115704, - "narHash": "sha256-KHFT9UWOF2yRPlAnSXQJh6uVcgNcWlFqqiAZ7OVlHNc=", + "lastModified": 1770197578, + "narHash": "sha256-AYqlWrX09+HvGs8zM6ebZ1pwUqjkfpnv8mewYwAo+iM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e6eae2ee2110f3d31110d5c222cd395303343b08", + "rev": "00c21e4c93d963c50d4c0c89bfa84ed6e0694df2", "type": "github" }, "original": { From b1f916ba052341edc1f80d4b2399f1092a4873ca Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 8 Feb 2026 15:25:47 -0600 Subject: [PATCH 25/83] news: don't specify nixpkgs revision Not necessary for using nix-shell and causes extra maintenance. Signed-off-by: Austin Horstman --- modules/misc/news/create-news-entry.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/misc/news/create-news-entry.sh b/modules/misc/news/create-news-entry.sh index 7becc0fc..10cc4e93 100755 --- a/modules/misc/news/create-news-entry.sh +++ b/modules/misc/news/create-news-entry.sh @@ -1,5 +1,5 @@ #!/usr/bin/env nix-shell -#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/72ac591e737060deab2b86d6952babd1f896d7c5.tar.gz -i bash -p coreutils +#! nix-shell -i bash -p coreutils DATE="$(date --iso-8601=second --universal)" YEAR="$(date --date="$DATE" +"%Y")" @@ -15,7 +15,7 @@ cd "$DIRNAME" || { exit 1 } -cat - << EOF > "$YEAR/$MONTH/$FILENAME_BASE.nix" +cat - <"$YEAR/$MONTH/$FILENAME_BASE.nix" { time = "$DATE"; condition = true; From 6c8def1df8556687ba99ecaf631bd343a162b03d Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 8 Feb 2026 14:29:48 -0600 Subject: [PATCH 26/83] codex: add enableMcpIntegration option Work with the shared mcp module for integration and transformation. Signed-off-by: Austin Horstman --- modules/programs/codex.nix | 57 ++++++++++++++++++- tests/modules/programs/codex/default.nix | 2 + .../codex/mcp-integration-with-override.nix | 49 ++++++++++++++++ .../codex/mcp-integration-with-override.toml | 18 ++++++ .../programs/codex/mcp-integration.nix | 36 ++++++++++++ .../programs/codex/mcp-integration.toml | 16 ++++++ 6 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 tests/modules/programs/codex/mcp-integration-with-override.nix create mode 100644 tests/modules/programs/codex/mcp-integration-with-override.toml create mode 100644 tests/modules/programs/codex/mcp-integration.nix create mode 100644 tests/modules/programs/codex/mcp-integration.toml diff --git a/modules/programs/codex.nix b/modules/programs/codex.nix index d931dcc5..68be40b7 100644 --- a/modules/programs/codex.nix +++ b/modules/programs/codex.nix @@ -26,6 +26,20 @@ in package = lib.mkPackageOption pkgs "codex" { nullable = true; }; + enableMcpIntegration = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to integrate the MCP server config from + {option}`programs.mcp.servers` into + {option}`programs.codex.settings.mcp_servers`. + + Note: Settings defined in {option}`programs.mcp.servers` are merged + with {option}`programs.codex.settings.mcp_servers`, with settings-based + values taking precedence. + ''; + }; + settings = lib.mkOption { # NOTE: `yaml` type supports null, using `nullOr` for backwards compatibility period type = lib.types.nullOr tomlFormat.type; @@ -48,6 +62,15 @@ in envKey = "OLLAMA_API_KEY"; }; }; + mcp_servers = { + context7 = { + command = "npx"; + args = [ + "-y" + "@upstash/context7-mcp" + ]; + }; + }; } ''; }; @@ -111,10 +134,36 @@ in config = let - useXdgDirectories = (config.home.preferXdgDirectories && isTomlConfig); + useXdgDirectories = config.home.preferXdgDirectories && isTomlConfig; xdgConfigHome = lib.removePrefix config.home.homeDirectory config.xdg.configHome; configDir = if useXdgDirectories then "${xdgConfigHome}/codex" else ".codex"; configFileName = if isTomlConfig then "config.toml" else "config.yaml"; + + transformedMcpServers = lib.optionalAttrs (cfg.enableMcpIntegration && config.programs.mcp.enable) ( + lib.mapAttrs ( + _name: server: + # NOTE: Convert shared programs.mcp fields to Codex config keys: + # - removeAttrs drops keys that Codex does not use directly + # - "disabled" becomes inverse "enabled" + # - "headers" is renamed to "http_headers" + # See: https://developers.openai.com/codex/mcp#other-configuration-options + (lib.removeAttrs server [ + "disabled" + "headers" + ]) + // (lib.optionalAttrs (server ? headers && !(server ? http_headers)) { + http_headers = server.headers; + }) + // { + enabled = !(server.disabled or false); + } + ) config.programs.mcp.servers + ); + + settingMcpServers = lib.attrByPath [ "mcp_servers" ] { } cfg.settings; + mergedMcpServers = transformedMcpServers // settingMcpServers; + mergedSettings = + cfg.settings // lib.optionalAttrs (mergedMcpServers != { }) { mcp_servers = mergedMcpServers; }; in mkIf cfg.enable { assertions = [ @@ -126,9 +175,10 @@ in home = { packages = mkIf (cfg.package != null) [ cfg.package ]; + file = { - "${configDir}/${configFileName}" = lib.mkIf (cfg.settings != { }) { - source = settingsFormat.generate "codex-config" cfg.settings; + "${configDir}/${configFileName}" = lib.mkIf (mergedSettings != { }) { + source = settingsFormat.generate "codex-config" mergedSettings; }; "${configDir}/AGENTS.md" = lib.mkIf (cfg.custom-instructions != "") { text = cfg.custom-instructions; @@ -150,6 +200,7 @@ in if lib.isPath content then { source = content; } else { text = content; } ) ) (if builtins.isAttrs cfg.skills then cfg.skills else { })); + sessionVariables = mkIf useXdgDirectories { CODEX_HOME = "${config.xdg.configHome}/codex"; }; diff --git a/tests/modules/programs/codex/default.nix b/tests/modules/programs/codex/default.nix index 23eb27b1..6341172a 100644 --- a/tests/modules/programs/codex/default.nix +++ b/tests/modules/programs/codex/default.nix @@ -6,6 +6,8 @@ codex-custom-instructions = ./custom-instructions.nix; codex-custom-instructions-prefer-xdg-directories = ./custom-instructions-prefer-xdg-directories.nix; codex-empty-custom-instructions = ./empty-custom-instructions.nix; + codex-mcp-integration = ./mcp-integration.nix; + codex-mcp-integration-with-override = ./mcp-integration-with-override.nix; codex-skills-inline = ./skills-inline.nix; codex-skills-dir = ./skills-dir.nix; codex-skills-path-not-directory = ./skills-path-not-directory.nix; diff --git a/tests/modules/programs/codex/mcp-integration-with-override.nix b/tests/modules/programs/codex/mcp-integration-with-override.nix new file mode 100644 index 00000000..6907e66b --- /dev/null +++ b/tests/modules/programs/codex/mcp-integration-with-override.nix @@ -0,0 +1,49 @@ +{ + programs.mcp = { + enable = true; + servers = { + everything = { + command = "npx"; + args = [ + "-y" + "@modelcontextprotocol/server-everything" + ]; + }; + context7 = { + url = "https://mcp.context7.com/mcp"; + headers = { + CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}"; + }; + }; + }; + }; + + programs.codex = { + enable = true; + enableMcpIntegration = true; + settings = { + model = "gpt-5-codex"; + mcp_servers = { + custom-server = { + url = "http://localhost:3000/mcp"; + enabled = true; + enabled_tools = [ + "open" + "screenshot" + ]; + }; + everything = { + command = "final-command"; + enabled = false; + tool_timeout_sec = 45; + }; + }; + }; + }; + + nmt.script = '' + assertFileExists home-files/.codex/config.toml + assertFileContent home-files/.codex/config.toml \ + ${./mcp-integration-with-override.toml} + ''; +} diff --git a/tests/modules/programs/codex/mcp-integration-with-override.toml b/tests/modules/programs/codex/mcp-integration-with-override.toml new file mode 100644 index 00000000..bf046589 --- /dev/null +++ b/tests/modules/programs/codex/mcp-integration-with-override.toml @@ -0,0 +1,18 @@ +model = "gpt-5-codex" + +[mcp_servers.context7] +enabled = true +url = "https://mcp.context7.com/mcp" + +[mcp_servers.context7.http_headers] +CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}" + +[mcp_servers.custom-server] +enabled = true +enabled_tools = ["open", "screenshot"] +url = "http://localhost:3000/mcp" + +[mcp_servers.everything] +command = "final-command" +enabled = false +tool_timeout_sec = 45 diff --git a/tests/modules/programs/codex/mcp-integration.nix b/tests/modules/programs/codex/mcp-integration.nix new file mode 100644 index 00000000..e7b87cf1 --- /dev/null +++ b/tests/modules/programs/codex/mcp-integration.nix @@ -0,0 +1,36 @@ +{ + programs.mcp = { + enable = true; + servers = { + everything = { + command = "npx"; + args = [ + "-y" + "@modelcontextprotocol/server-everything" + ]; + }; + context7 = { + url = "https://mcp.context7.com/mcp"; + headers = { + CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}"; + }; + }; + disabled-server = { + command = "echo"; + args = [ "test" ]; + disabled = true; + }; + }; + }; + + programs.codex = { + enable = true; + enableMcpIntegration = true; + }; + + nmt.script = '' + assertFileExists home-files/.codex/config.toml + assertFileContent home-files/.codex/config.toml \ + ${./mcp-integration.toml} + ''; +} diff --git a/tests/modules/programs/codex/mcp-integration.toml b/tests/modules/programs/codex/mcp-integration.toml new file mode 100644 index 00000000..c46f5acd --- /dev/null +++ b/tests/modules/programs/codex/mcp-integration.toml @@ -0,0 +1,16 @@ +[mcp_servers.context7] +enabled = true +url = "https://mcp.context7.com/mcp" + +[mcp_servers.context7.http_headers] +CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}" + +[mcp_servers.disabled-server] +args = ["test"] +command = "echo" +enabled = false + +[mcp_servers.everything] +args = ["-y", "@modelcontextprotocol/server-everything"] +command = "npx" +enabled = true From 13a1beb7c9962e0d2ba35a4d5c87546509b89b7d Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 8 Feb 2026 15:06:35 -0600 Subject: [PATCH 27/83] news: add codex mcp integration Just letting users know module has the integration enabled now. Signed-off-by: Austin Horstman --- modules/misc/news/2026/02/2026-02-08_15-04-50.nix | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 modules/misc/news/2026/02/2026-02-08_15-04-50.nix diff --git a/modules/misc/news/2026/02/2026-02-08_15-04-50.nix b/modules/misc/news/2026/02/2026-02-08_15-04-50.nix new file mode 100644 index 00000000..12e46dea --- /dev/null +++ b/modules/misc/news/2026/02/2026-02-08_15-04-50.nix @@ -0,0 +1,13 @@ +{ config, ... }: +{ + time = "2026-02-08T21:04:50+00:00"; + condition = config.programs.codex.enable; + message = '' + The `programs.codex` module now supports MCP integration via + `programs.codex.enableMcpIntegration`. + + When enabled, shared servers from `programs.mcp.servers` are merged + into `programs.codex.settings.mcp_servers`, with settings-based values + taking precedence. + ''; +} From 6c4fdbe1ad198fac36c320fd45c5957324a80b8e Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 09:53:36 +0000 Subject: [PATCH 28/83] 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:** 282 → 283 maintainers **✅ Added:** mikaeladev 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 b25b8a6b..5923a8fb 100644 --- a/all-maintainers.nix +++ b/all-maintainers.nix @@ -1640,6 +1640,13 @@ name = "Shahar \"Dawn\" Or"; source = "nixpkgs"; }; + mikaeladev = { + email = "mikaeladev@users.noreply.github.com"; + github = "mikaeladev"; + githubId = 100416544; + name = "mikaeladev"; + source = "home-manager"; + }; mikilio = { email = "official.mikilio+dev@gmail.com"; github = "mikilio"; From b3f43db171474132528be57610bfa5fb3b766879 Mon Sep 17 00:00:00 2001 From: Adam Poit Date: Tue, 10 Feb 2026 12:18:33 -0800 Subject: [PATCH 29/83] colima: fix KeepAlive to prevent process accumulation on macOS Change KeepAlive from boolean true to SuccessfulExit dictionary in the LaunchAgent configuration. When colima fails to start (e.g., disk already attached), the boolean true setting causes launchd to immediately restart it, spawning orphaned limactl usernet processes with each restart. Using SuccessfulExit = true ensures the service only restarts on clean exits (exit code 0), preventing the aggressive restart loop that accumulates orphaned processes. --- modules/services/colima.nix | 4 +++- tests/modules/services/colima/darwin/expected-agent.plist | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/services/colima.nix b/modules/services/colima.nix index 3855895f..988c3ada 100644 --- a/modules/services/colima.nix +++ b/modules/services/colima.nix @@ -202,7 +202,9 @@ in "--activate=${if profile.isActive then "true" else "false"}" "--save-config=false" ]; - KeepAlive = true; + KeepAlive = { + SuccessfulExit = true; + }; RunAtLoad = true; EnvironmentVariables.PATH = lib.makeBinPath [ cfg.package diff --git a/tests/modules/services/colima/darwin/expected-agent.plist b/tests/modules/services/colima/darwin/expected-agent.plist index 6a73e56c..9a1f4b94 100644 --- a/tests/modules/services/colima/darwin/expected-agent.plist +++ b/tests/modules/services/colima/darwin/expected-agent.plist @@ -8,7 +8,10 @@ @colima@/bin:@perl@/bin:@docker@/bin:@openssh@/bin:@coreutils@/bin:@curl@/bin:@bashNonInteractive@/bin:@DarwinTools@/bin KeepAlive - + + SuccessfulExit + + Label org.nix-community.home.colima-default ProgramArguments From 0acbd1180697de56724821184ad2c3e6e7202cd7 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Wed, 11 Feb 2026 05:21:02 +0000 Subject: [PATCH 30/83] 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/00c21e4c93d963c50d4c0c89bfa84ed6e0694df2?narHash=sha256-AYqlWrX09%2BHvGs8zM6ebZ1pwUqjkfpnv8mewYwAo%2BiM%3D' (2026-02-04) → 'github:NixOS/nixpkgs/d6c71932130818840fc8fe9509cf50be8c64634f?narHash=sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84%3D' (2026-02-08) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index dbf2fd5e..e5578c1f 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1770197578, - "narHash": "sha256-AYqlWrX09+HvGs8zM6ebZ1pwUqjkfpnv8mewYwAo+iM=", + "lastModified": 1770562336, + "narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "00c21e4c93d963c50d4c0c89bfa84ed6e0694df2", + "rev": "d6c71932130818840fc8fe9509cf50be8c64634f", "type": "github" }, "original": { From 0825a0922a5d677f5f984bb79524569bbd1f9954 Mon Sep 17 00:00:00 2001 From: teto <886074+teto@users.noreply.github.com> Date: Sat, 24 Jan 2026 22:24:02 +0100 Subject: [PATCH 31/83] feat: introduce nix-shell flakes are not stable yet, community is divided but maintaining both flake and a set of dependencies in our scripts is hard. So provide a nix-shell that fetches the nixpkgs recorded in the flake.lock to keep a single source of truth --- shell.nix | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 shell.nix diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..f983d48d --- /dev/null +++ b/shell.nix @@ -0,0 +1,15 @@ +let + nixpkgs = ( + import ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + n = lock.nodes.nixpkgs.locked; + in + fetchTarball { + url = "https://github.com/${n.owner}/${n.repo}/archive/${n.rev}.tar.gz"; + sha256 = n.narHash; + } + ) { } + ); +in +nixpkgs.callPackage ./home-manager { } From 2363f5a3772d230051bd0f8447585848fb99b0f9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 11 Feb 2026 23:19:22 -0600 Subject: [PATCH 32/83] flameshot: add darwin support Signed-off-by: Austin Horstman --- modules/services/flameshot.nix | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/modules/services/flameshot.nix b/modules/services/flameshot.nix index 664f63fa..65954b13 100644 --- a/modules/services/flameshot.nix +++ b/modules/services/flameshot.nix @@ -39,10 +39,6 @@ in }; config = lib.mkIf cfg.enable { - assertions = [ - (lib.hm.assertions.assertPlatform "services.flameshot" pkgs lib.platforms.linux) - ]; - home.packages = [ cfg.package ]; xdg.configFile = lib.mkIf (cfg.settings != { }) { @@ -67,7 +63,7 @@ in Service = { Environment = [ "PATH=${config.home.profileDirectory}/bin" ]; - ExecStart = "${cfg.package}/bin/flameshot"; + ExecStart = lib.getExe cfg.package; Restart = "on-abort"; # Sandboxing. @@ -80,5 +76,18 @@ in SystemCallFilter = "@system-service"; }; }; + + launchd.agents.flameshot = { + enable = true; + config = { + ProgramArguments = [ (lib.getExe cfg.package) ]; + KeepAlive = { + Crashed = true; + SuccessfulExit = false; + }; + ProcessType = "Interactive"; + RunAtLoad = true; + }; + }; }; } From ede6d1d95e55ac8833de35b51167697d269c9f4c Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 11 Feb 2026 23:19:57 -0600 Subject: [PATCH 33/83] tests/flameshot: add darwin tests Signed-off-by: Austin Horstman --- tests/darwinScrublist.nix | 1 + tests/modules/services/flameshot/default.nix | 8 ++++- .../services/flameshot/launchd-agent.nix | 36 +++++++++++++++++++ tests/modules/services/flameshot/service.nix | 33 +++++++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 tests/modules/services/flameshot/launchd-agent.nix create mode 100644 tests/modules/services/flameshot/service.nix diff --git a/tests/darwinScrublist.nix b/tests/darwinScrublist.nix index 9487b456..a3a1fcf4 100644 --- a/tests/darwinScrublist.nix +++ b/tests/darwinScrublist.nix @@ -50,6 +50,7 @@ let "eza" "fastfetch" "feh" + "flameshot" "fzf" "gallery-dl" "getconf" diff --git a/tests/modules/services/flameshot/default.nix b/tests/modules/services/flameshot/default.nix index 417411c3..ac50855b 100644 --- a/tests/modules/services/flameshot/default.nix +++ b/tests/modules/services/flameshot/default.nix @@ -1,6 +1,12 @@ { lib, pkgs, ... }: -lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { +{ flameshot-empty-settings = ./empty-settings.nix; flameshot-example-settings = ./example-settings.nix; } +// lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { + flameshot-service = ./service.nix; +} +// lib.optionalAttrs pkgs.stdenv.hostPlatform.isDarwin { + flameshot-agent = ./launchd-agent.nix; +} diff --git a/tests/modules/services/flameshot/launchd-agent.nix b/tests/modules/services/flameshot/launchd-agent.nix new file mode 100644 index 00000000..f7b2dbe8 --- /dev/null +++ b/tests/modules/services/flameshot/launchd-agent.nix @@ -0,0 +1,36 @@ +{ + services.flameshot = { + enable = true; + }; + + nmt.script = '' + serviceFile="LaunchAgents/org.nix-community.home.flameshot.plist" + assertFileExists "$serviceFile" + assertFileContent "$serviceFile" ${builtins.toFile "expected-agent.plist" '' + + + + + KeepAlive + + Crashed + + SuccessfulExit + + + Label + org.nix-community.home.flameshot + ProcessType + Interactive + ProgramArguments + + /bin/sh + -c + /bin/wait4path /nix/store && exec @flameshot@/bin/flameshot + + RunAtLoad + + + ''} + ''; +} diff --git a/tests/modules/services/flameshot/service.nix b/tests/modules/services/flameshot/service.nix new file mode 100644 index 00000000..d976c2b1 --- /dev/null +++ b/tests/modules/services/flameshot/service.nix @@ -0,0 +1,33 @@ +{ + services.flameshot = { + enable = true; + }; + + nmt.script = '' + serviceFile="home-files/.config/systemd/user/flameshot.service" + assertFileExists "$serviceFile" + assertFileContent "$serviceFile" ${builtins.toFile "expected.service" '' + [Install] + WantedBy=graphical-session.target + + [Service] + Environment=PATH=/home/hm-user/.nix-profile/bin + ExecStart=@flameshot@/bin/flameshot + LockPersonality=true + MemoryDenyWriteExecute=true + NoNewPrivileges=true + PrivateUsers=true + Restart=on-abort + RestrictNamespaces=true + SystemCallArchitectures=native + SystemCallFilter=@system-service + + [Unit] + After=graphical-session.target + After=tray.target + Description=Flameshot screenshot tool + PartOf=graphical-session.target + Requires=tray.target + ''} + ''; +} From 6a1f7101d2c3ee87d485a87880d73b4665c6a4bd Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 11 Feb 2026 23:20:08 -0600 Subject: [PATCH 34/83] news: add flameshot darwin entry Signed-off-by: Austin Horstman --- modules/misc/news/2026/02/2026-02-11_23-16-46.nix | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 modules/misc/news/2026/02/2026-02-11_23-16-46.nix diff --git a/modules/misc/news/2026/02/2026-02-11_23-16-46.nix b/modules/misc/news/2026/02/2026-02-11_23-16-46.nix new file mode 100644 index 00000000..4e906e03 --- /dev/null +++ b/modules/misc/news/2026/02/2026-02-11_23-16-46.nix @@ -0,0 +1,8 @@ +{ + time = "2026-02-12T05:16:46+00:00"; + condition = true; + message = '' + The `services.flameshot` module now supports Darwin by generating a + launchd agent in addition to the existing Linux systemd service. + ''; +} From 2cc195b4783991b30fb8499c0dd2f9a7bf44d347 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 15:09:21 -0600 Subject: [PATCH 35/83] docs: update flake module example Just include some more detail in example Signed-off-by: Austin Horstman --- docs/manual/nix-flakes/flake-parts.md | 39 ++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/docs/manual/nix-flakes/flake-parts.md b/docs/manual/nix-flakes/flake-parts.md index b314bef3..b5dc5070 100644 --- a/docs/manual/nix-flakes/flake-parts.md +++ b/docs/manual/nix-flakes/flake-parts.md @@ -1,10 +1,9 @@ # flake-parts module {#sec-flakes-flake-parts-module} -When using [flake-parts](https://flake.parts) -you may wish to import Home Manager's flake module, -`flakeModules.home-manager`. +When using [flake-parts](https://flake.parts) you may wish to import Home +Manager's flake module, `flakeModules.home-manager`. -``` nix +```nix { description = "flake-parts configuration"; @@ -15,15 +14,41 @@ you may wish to import Home Manager's flake module, flake-parts.url = "github:hercules-ci/flake-parts"; }; - outputs = inputs@{ flake-parts, ... }: + outputs = inputs@{ + flake-parts, + home-manager, + nixpkgs, + ... + }: flake-parts.lib.mkFlake { inherit inputs; } { imports = [ # Import home-manager's flake module inputs.home-manager.flakeModules.home-manager ]; flake = { - # Define `homeModules`, `homeConfigurations`, - # `nixosConfigurations`, etc here + # Reusable Home Manager module. + homeModules.bash= { pkgs, ... }: { + programs.bash = { + enable = true; + shellAliases = { + ll = "ls -l"; + }; + }; + home.packages = [ pkgs.hello ]; + }; + + # Concrete Home Manager configuration. + homeConfigurations.alice = home-manager.lib.homeManagerConfiguration { + pkgs = import nixpkgs { system = "x86_64-linux"; }; + modules = [ + inputs.self.homeModules.bash + { + home.username = "alice"; + home.homeDirectory = "/home/alice"; + home.stateVersion = "25.11"; + } + ]; + }; }; # See flake.parts for more features, such as `perSystem` }; From 258db1d39b5945d70157029c237f39cff83531f4 Mon Sep 17 00:00:00 2001 From: nescias Date: Sun, 1 Feb 2026 15:56:57 +0000 Subject: [PATCH 36/83] git: respect `programs.msmtp.package` Since the user may override `programs.msmtp.package` using `pkgs.msmtp` as the sendmail binary might result in the wrong package being used. --- modules/programs/git.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/git.nix b/modules/programs/git.nix index a9ef6887..039d4afd 100644 --- a/modules/programs/git.nix +++ b/modules/programs/git.nix @@ -402,7 +402,7 @@ in lib.nameValuePair "sendemail.${name}" ( if account.msmtp.enable then { - sendmailCmd = "${pkgs.msmtp}/bin/msmtp"; + sendmailCmd = lib.getExe config.programs.msmtp.package; envelopeSender = "auto"; from = "${realName} <${address}>"; } From 9bdb6938109884cb8b6a79ab79ba18e7b585a881 Mon Sep 17 00:00:00 2001 From: nescias Date: Mon, 2 Feb 2026 13:49:23 +0000 Subject: [PATCH 37/83] notmuch: add `programs.notmuch.package` option Allow users to specify which notmuch package should be used. --- modules/programs/notmuch/default.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/programs/notmuch/default.nix b/modules/programs/notmuch/default.nix index c8d6b478..f051c8cc 100644 --- a/modules/programs/notmuch/default.nix +++ b/modules/programs/notmuch/default.nix @@ -72,6 +72,8 @@ in programs.notmuch = { enable = lib.mkEnableOption "Notmuch mail indexer"; + package = lib.mkPackageOption pkgs "notmuch" { }; + new = mkOption { type = types.submodule { options = { @@ -197,7 +199,7 @@ in } ]; - home.packages = [ pkgs.notmuch ]; + home.packages = [ cfg.package ]; home.sessionVariables = { NOTMUCH_CONFIG = "${config.xdg.configHome}/notmuch/default/config"; @@ -208,7 +210,7 @@ in let hook = name: cmds: { "notmuch/default/hooks/${name}".source = pkgs.writeShellScript name '' - export PATH="${pkgs.notmuch}/bin''${PATH:+:}$PATH" + export PATH="${cfg.package}/bin''${PATH:+:}$PATH" export NOTMUCH_CONFIG="${config.xdg.configHome}/notmuch/default/config" export NMBGIT="${config.xdg.dataHome}/notmuch/nmbug" From 133585ddfa1b7121f478648bd3c2fab9a092437e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 13:21:40 -0600 Subject: [PATCH 38/83] pay-respects: add rules support Allow creating rules files now following docs from https://github.com/iffse/pay-respects/blob/main/rules.md Signed-off-by: Austin Horstman --- modules/programs/pay-respects.nix | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/modules/programs/pay-respects.nix b/modules/programs/pay-respects.nix index 53877c39..1936a5a8 100644 --- a/modules/programs/pay-respects.nix +++ b/modules/programs/pay-respects.nix @@ -8,6 +8,7 @@ let cfg = config.programs.pay-respects; payRespectsCmd = lib.getExe cfg.package; cfgOptions = lib.concatStringsSep " " cfg.options; + tomlFormat = pkgs.formats.toml { }; in { meta.maintainers = [ lib.maintainers.ALameLlama ]; @@ -29,6 +30,49 @@ in ''; }; + rules = lib.mkOption { + type = lib.types.attrsOf tomlFormat.type; + default = { }; + example = lib.literalExpression '' + { + cargo = { + command = "cargo"; + match_err = [ + { + pattern = [ "run `cargo init` to initialize a new rust project" ]; + suggest = [ "cargo init" ]; + } + ]; + }; + + _PR_GENERAL = { + match_err = [ + { + pattern = [ "permission denied" ]; + suggest = [ + "#[executable(sudo), !cmd_contains(sudo)]\nsudo {{command}}" + ]; + } + ]; + }; + } + ''; + description = '' + Runtime rule files written to + {file}`$XDG_CONFIG_HOME/pay-respects/rules/.toml`. + + Attribute names map to filenames. For example, setting `rules.cargo = { ... };` + creates {file}`$XDG_CONFIG_HOME/pay-respects/rules/cargo.toml`. + The filename must match the command name, except for `_PR_GENERAL`. + + See + for runtime rule syntax and behavior. + + Note that these rules are only applied when the runtime-rules module is + available to `pay-respects`. + ''; + }; + enableBashIntegration = lib.hm.shell.mkBashIntegrationOption { inherit config; }; enableFishIntegration = lib.hm.shell.mkFishIntegrationOption { inherit config; }; @@ -41,6 +85,13 @@ in config = lib.mkIf cfg.enable { home.packages = [ cfg.package ]; + xdg.configFile = lib.mapAttrs' ( + name: rule: + lib.nameValuePair "pay-respects/rules/${name}.toml" { + source = tomlFormat.generate "pay-respects-rule-${name}.toml" rule; + } + ) cfg.rules; + programs = { bash.initExtra = lib.mkIf cfg.enableBashIntegration '' eval "$(${payRespectsCmd} bash ${cfgOptions})" From 2fe1e0ea385943074d0df783e66419d5049b559f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 13:24:27 -0600 Subject: [PATCH 39/83] tests/pay-respects: add rules test Signed-off-by: Austin Horstman --- .../programs/pay-respects/cargo-expected.toml | 5 +++ .../modules/programs/pay-respects/default.nix | 1 + .../pay-respects/general-expected.toml | 3 ++ tests/modules/programs/pay-respects/rules.nix | 45 +++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 tests/modules/programs/pay-respects/cargo-expected.toml create mode 100644 tests/modules/programs/pay-respects/general-expected.toml create mode 100644 tests/modules/programs/pay-respects/rules.nix diff --git a/tests/modules/programs/pay-respects/cargo-expected.toml b/tests/modules/programs/pay-respects/cargo-expected.toml new file mode 100644 index 00000000..c2f94269 --- /dev/null +++ b/tests/modules/programs/pay-respects/cargo-expected.toml @@ -0,0 +1,5 @@ +command = "cargo" + +[[match_err]] +pattern = ["run `cargo init` to initialize a new rust project"] +suggest = ["cargo init"] diff --git a/tests/modules/programs/pay-respects/default.nix b/tests/modules/programs/pay-respects/default.nix index 44334d3a..a820a965 100644 --- a/tests/modules/programs/pay-respects/default.nix +++ b/tests/modules/programs/pay-respects/default.nix @@ -1,4 +1,5 @@ { pay-respects-integration-enabled = ./integration-enabled.nix; pay-respects-integration-disabled = ./integration-disabled.nix; + pay-respects-rules = ./rules.nix; } diff --git a/tests/modules/programs/pay-respects/general-expected.toml b/tests/modules/programs/pay-respects/general-expected.toml new file mode 100644 index 00000000..d7809728 --- /dev/null +++ b/tests/modules/programs/pay-respects/general-expected.toml @@ -0,0 +1,3 @@ +[[match_err]] +pattern = ["permission denied"] +suggest = ["#[executable(sudo), !cmd_contains(sudo)]\nsudo {{command}}\n"] diff --git a/tests/modules/programs/pay-respects/rules.nix b/tests/modules/programs/pay-respects/rules.nix new file mode 100644 index 00000000..57cce0a9 --- /dev/null +++ b/tests/modules/programs/pay-respects/rules.nix @@ -0,0 +1,45 @@ +{ config, ... }: + +{ + programs = { + pay-respects = { + enable = true; + package = config.lib.test.mkStubPackage { }; + rules = { + cargo = { + command = "cargo"; + match_err = [ + { + pattern = [ "run `cargo init` to initialize a new rust project" ]; + suggest = [ "cargo init" ]; + } + ]; + }; + + _PR_GENERAL = { + match_err = [ + { + pattern = [ "permission denied" ]; + suggest = [ + '' + #[executable(sudo), !cmd_contains(sudo)] + sudo {{command}} + '' + ]; + } + ]; + }; + }; + }; + }; + + nmt.script = '' + assertFileContent \ + "home-files/.config/pay-respects/rules/cargo.toml" \ + ${./cargo-expected.toml} + + assertFileContent \ + "home-files/.config/pay-respects/rules/_PR_GENERAL.toml" \ + ${./general-expected.toml} + ''; +} From f24a75598043a12cd5c958db55763cead9dfc036 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 13:24:38 -0600 Subject: [PATCH 40/83] news: add pay-respects rules addition Signed-off-by: Austin Horstman --- .../misc/news/2026/02/2026-02-12_13-22-50.nix | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 modules/misc/news/2026/02/2026-02-12_13-22-50.nix diff --git a/modules/misc/news/2026/02/2026-02-12_13-22-50.nix b/modules/misc/news/2026/02/2026-02-12_13-22-50.nix new file mode 100644 index 00000000..4966d9fc --- /dev/null +++ b/modules/misc/news/2026/02/2026-02-12_13-22-50.nix @@ -0,0 +1,16 @@ +{ config, ... }: +{ + time = "2026-02-12T19:22:50+00:00"; + condition = config.programs.pay-respects.enable; + message = '' + The option `programs.pay-respects.rules` was added. + + It generates runtime rule files at + {file}`$XDG_CONFIG_HOME/pay-respects/rules/.toml`, where each + attribute name under `rules` becomes a filename (for example, `rules.cargo` + writes `cargo.toml`). + + For the full runtime-rules format and command matching requirements, see + . + ''; +} From a6c93262f349afeeebb390fb441910abffef329c Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 13:26:44 -0600 Subject: [PATCH 41/83] pay-respects: minor style cleanup Signed-off-by: Austin Horstman --- modules/programs/pay-respects.nix | 44 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/modules/programs/pay-respects.nix b/modules/programs/pay-respects.nix index 1936a5a8..6cb79874 100644 --- a/modules/programs/pay-respects.nix +++ b/modules/programs/pay-respects.nix @@ -6,8 +6,7 @@ }: let cfg = config.programs.pay-respects; - payRespectsCmd = lib.getExe cfg.package; - cfgOptions = lib.concatStringsSep " " cfg.options; + tomlFormat = pkgs.formats.toml { }; in { @@ -92,26 +91,31 @@ in } ) cfg.rules; - programs = { - bash.initExtra = lib.mkIf cfg.enableBashIntegration '' - eval "$(${payRespectsCmd} bash ${cfgOptions})" - ''; + programs = + let + payRespectsCmd = lib.getExe cfg.package; + cfgOptions = lib.concatStringsSep " " cfg.options; + in + { + bash.initExtra = lib.mkIf cfg.enableBashIntegration '' + eval "$(${payRespectsCmd} bash ${cfgOptions})" + ''; - zsh.initContent = lib.mkIf cfg.enableZshIntegration '' - eval "$(${payRespectsCmd} zsh ${cfgOptions})" - ''; + zsh.initContent = lib.mkIf cfg.enableZshIntegration '' + eval "$(${payRespectsCmd} zsh ${cfgOptions})" + ''; - fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration '' - ${payRespectsCmd} fish ${cfgOptions} | source - ''; + fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration '' + ${payRespectsCmd} fish ${cfgOptions} | source + ''; - nushell.extraConfig = lib.mkIf cfg.enableNushellIntegration '' - source ${ - pkgs.runCommand "pay-respects-nushell-config.nu" { } '' - ${payRespectsCmd} nushell ${cfgOptions} >> "$out" - '' - } - ''; - }; + nushell.extraConfig = lib.mkIf cfg.enableNushellIntegration '' + source ${ + pkgs.runCommand "pay-respects-nushell-config.nu" { } '' + ${payRespectsCmd} nushell ${cfgOptions} >> "$out" + '' + } + ''; + }; }; } From de4cfffc98f43ab8ba90739b56991f068f9e9018 Mon Sep 17 00:00:00 2001 From: Yus314 Date: Fri, 30 Jan 2026 13:06:47 +0900 Subject: [PATCH 42/83] kitty: add autoThemeFiles option Add support for kitty's automatic theme switching based on OS color scheme. This creates the required auto theme config files: - light-theme.auto.conf - dark-theme.auto.conf - no-preference-theme.auto.conf Closes: nix-community/home-manager#6869 --- modules/programs/kitty.nix | 74 +++++++++++++++++-- .../standalone/kitty-auto-theme-bad-home.nix | 17 +++++ tests/integration/standalone/kitty.nix | 8 ++ .../programs/kitty/auto-theme-files.nix | 27 +++++++ tests/modules/programs/kitty/default.nix | 1 + 5 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 tests/integration/standalone/kitty-auto-theme-bad-home.nix create mode 100644 tests/modules/programs/kitty/auto-theme-files.nix diff --git a/modules/programs/kitty.nix b/modules/programs/kitty.nix index ac184596..0c4809be 100644 --- a/modules/programs/kitty.nix +++ b/modules/programs/kitty.nix @@ -168,10 +168,52 @@ in in `kitty-themes`, without the `.conf` suffix. See for a list of themes. + + Note that if any automatic themes are configured via + `programs.kitty.autoThemeFiles`, Kitty will prefer them based on the + OS color scheme and they will override other color and background image + settings. ''; example = "SpaceGray_Eighties"; }; + autoThemeFiles = mkOption { + type = types.nullOr ( + types.submodule { + options = { + light = mkOption { + type = types.str; + description = "Theme name for light color scheme."; + }; + dark = mkOption { + type = types.str; + description = "Theme name for dark color scheme."; + }; + noPreference = mkOption { + type = types.str; + description = "Theme name for no-preference color scheme."; + }; + }; + } + ); + default = null; + description = '' + Configure Kitty automatic color themes. This creates + {file}`$XDG_CONFIG_HOME/kitty/light-theme.auto.conf`, + {file}`$XDG_CONFIG_HOME/kitty/dark-theme.auto.conf`, and + {file}`$XDG_CONFIG_HOME/kitty/no-preference-theme.auto.conf`. + Kitty applies these based on the OS color scheme, and they override + other color and background image settings. + ''; + example = literalExpression '' + { + light = "GitHub"; + dark = "TokyoNight"; + noPreference = "OneDark"; + } + ''; + }; + font = mkOption { type = types.nullOr lib.hm.types.fontType; default = null; @@ -358,15 +400,35 @@ in ''; }; - home.activation.checkKittyTheme = mkIf (cfg.themeFile != null) ( + xdg.configFile."kitty/light-theme.auto.conf" = mkIf (cfg.autoThemeFiles != null) { + text = "include ${pkgs.kitty-themes}/share/kitty-themes/themes/${cfg.autoThemeFiles.light}.conf\n"; + }; + + xdg.configFile."kitty/dark-theme.auto.conf" = mkIf (cfg.autoThemeFiles != null) { + text = "include ${pkgs.kitty-themes}/share/kitty-themes/themes/${cfg.autoThemeFiles.dark}.conf\n"; + }; + + xdg.configFile."kitty/no-preference-theme.auto.conf" = mkIf (cfg.autoThemeFiles != null) { + text = "include ${pkgs.kitty-themes}/share/kitty-themes/themes/${cfg.autoThemeFiles.noPreference}.conf\n"; + }; + + home.activation.checkKittyTheme = mkIf (cfg.themeFile != null || cfg.autoThemeFiles != null) ( let - themePath = "${pkgs.kitty-themes}/share/kitty-themes/themes/${cfg.themeFile}.conf"; + themePath = name: "${pkgs.kitty-themes}/share/kitty-themes/themes/${name}.conf"; + checkThemeFile = name: '' + if [[ ! -f "${themePath name}" ]]; then + errorEcho "kitty-themes does not contain the theme file ${themePath name}!" + exit 1 + fi + ''; in lib.hm.dag.entryBefore [ "writeBoundary" ] '' - if [[ ! -f "${themePath}" ]]; then - errorEcho "kitty-themes does not contain the theme file ${themePath}!" - exit 1 - fi + ${lib.optionalString (cfg.themeFile != null) (checkThemeFile cfg.themeFile)} + ${lib.optionalString (cfg.autoThemeFiles != null) '' + ${checkThemeFile cfg.autoThemeFiles.light} + ${checkThemeFile cfg.autoThemeFiles.dark} + ${checkThemeFile cfg.autoThemeFiles.noPreference} + ''} '' ); diff --git a/tests/integration/standalone/kitty-auto-theme-bad-home.nix b/tests/integration/standalone/kitty-auto-theme-bad-home.nix new file mode 100644 index 00000000..b6e0ab06 --- /dev/null +++ b/tests/integration/standalone/kitty-auto-theme-bad-home.nix @@ -0,0 +1,17 @@ +{ + home.username = "alice"; + home.homeDirectory = "/home/alice"; + home.stateVersion = "24.11"; + + # Let Home Manager install and manage itself. + programs.home-manager.enable = true; + + programs.kitty = { + enable = true; + autoThemeFiles = { + light = "GitHub"; + dark = "No Such Theme"; + noPreference = "OneDark"; + }; + }; +} diff --git a/tests/integration/standalone/kitty.nix b/tests/integration/standalone/kitty.nix index f9692792..2addf12a 100644 --- a/tests/integration/standalone/kitty.nix +++ b/tests/integration/standalone/kitty.nix @@ -59,6 +59,14 @@ assert expected in actual, \ f"expected home-manager switch to contain {expected}, but got {actual}" + with subtest("Switch to Bad Kitty Auto Theme"): + succeed_as_alice("cp ${./kitty-auto-theme-bad-home.nix} /home/alice/.config/home-manager/home.nix") + + actual = fail_as_alice("home-manager switch") + expected = "kitty-themes does not contain the theme file" + assert expected in actual, \ + f"expected home-manager switch to contain {expected}, but got {actual}" + with subtest("Switch to Good Kitty"): succeed_as_alice("cp ${./kitty-theme-good-home.nix} /home/alice/.config/home-manager/home.nix") diff --git a/tests/modules/programs/kitty/auto-theme-files.nix b/tests/modules/programs/kitty/auto-theme-files.nix new file mode 100644 index 00000000..e0bf6510 --- /dev/null +++ b/tests/modules/programs/kitty/auto-theme-files.nix @@ -0,0 +1,27 @@ +{ config, pkgs, ... }: +{ + programs.kitty = { + enable = true; + autoThemeFiles = { + light = "GitHub"; + dark = "TokyoNight"; + noPreference = "OneDark"; + }; + }; + + test = { + assertFileExists = [ + "home-files/.config/kitty/light-theme.auto.conf" + "home-files/.config/kitty/dark-theme.auto.conf" + "home-files/.config/kitty/no-preference-theme.auto.conf" + ]; + assertFileRegex = [ + "home-files/.config/kitty/light-theme.auto.conf" + "^include .*themes/GitHub\\.conf$" + "home-files/.config/kitty/dark-theme.auto.conf" + "^include .*themes/TokyoNight\\.conf$" + "home-files/.config/kitty/no-preference-theme.auto.conf" + "^include .*themes/OneDark\\.conf$" + ]; + }; +} diff --git a/tests/modules/programs/kitty/default.nix b/tests/modules/programs/kitty/default.nix index bbb3afae..78965036 100644 --- a/tests/modules/programs/kitty/default.nix +++ b/tests/modules/programs/kitty/default.nix @@ -4,4 +4,5 @@ kitty-null-shellIntegration = ./null-shellIntegration.nix; kitty-example-mkOrder = ./example-mkOrder.nix; kitty-example-quickAccessTerminalConfig = ./example-quickAccessTerminalConfig.nix; + kitty-auto-theme-files = ./auto-theme-files.nix; } From 5e90b62996d56da9acb21e502c078e7c4e6ab40f Mon Sep 17 00:00:00 2001 From: Malik Date: Fri, 13 Feb 2026 12:15:24 +0100 Subject: [PATCH 43/83] tirith: add module Adds Home Manager module for Tirith, a shell security monitor. The module supports: - Shell integration for Bash, Fish, and Zsh - Allowlist configuration for bypassing Tirith analysis - Policy configuration for customizing security behavior --- .../misc/news/2026/02/2026-02-13_12-21-10.nix | 11 +++ modules/programs/tirith.nix | 84 +++++++++++++++++++ tests/modules/programs/tirith/basic.nix | 27 ++++++ tests/modules/programs/tirith/default.nix | 3 + 4 files changed, 125 insertions(+) create mode 100644 modules/misc/news/2026/02/2026-02-13_12-21-10.nix create mode 100644 modules/programs/tirith.nix create mode 100644 tests/modules/programs/tirith/basic.nix create mode 100644 tests/modules/programs/tirith/default.nix diff --git a/modules/misc/news/2026/02/2026-02-13_12-21-10.nix b/modules/misc/news/2026/02/2026-02-13_12-21-10.nix new file mode 100644 index 00000000..bdab8f0d --- /dev/null +++ b/modules/misc/news/2026/02/2026-02-13_12-21-10.nix @@ -0,0 +1,11 @@ +{ + time = "2026-02-13T11:21:10+00:00"; + condition = true; + message = '' + + A new module is available: 'programs.tirith'. + + Tirith is a shell security monitor that helps protect against + malicious commands by analyzing shell inputs before execution. + ''; +} diff --git a/modules/programs/tirith.nix b/modules/programs/tirith.nix new file mode 100644 index 00000000..d8703d3e --- /dev/null +++ b/modules/programs/tirith.nix @@ -0,0 +1,84 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.programs.tirith; + yamlFormat = pkgs.formats.yaml { }; +in +{ + meta.maintainers = with lib.maintainers; [ malik ]; + + options.programs.tirith = { + enable = lib.mkEnableOption "Tirith, a shell security monitor"; + + package = lib.mkPackageOption pkgs "tirith" { }; + + allowlist = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + apply = builtins.filter (s: s != ""); + example = [ + "localhost" + ]; + description = '' + List of allowed domains that bypass Tirith analysis. + Written to `$XDG_CONFIG_HOME/tirith/allowlist`. + ''; + }; + + policy = lib.mkOption { + type = yamlFormat.type; + default = { }; + example = lib.literalExpression '' + { + version = 1; + fail_mode = "open"; + allow_bypass = true; + severity_overrides = { + docker_untrusted_registry = "critical"; + }; + } + ''; + description = '' + Tirith policy configuration. + Written to `$XDG_CONFIG_HOME/tirith/policy.yaml`. + + See + for policy examples. + ''; + }; + + enableBashIntegration = lib.hm.shell.mkBashIntegrationOption { inherit config; }; + enableFishIntegration = lib.hm.shell.mkFishIntegrationOption { inherit config; }; + enableZshIntegration = lib.hm.shell.mkZshIntegrationOption { inherit config; }; + }; + + config = lib.mkIf cfg.enable { + home.packages = [ cfg.package ]; + + xdg.configFile = { + "tirith/allowlist" = lib.mkIf (cfg.allowlist != [ ]) { + text = (lib.concatStringsSep "\n" cfg.allowlist) + "\n"; + }; + + "tirith/policy.yaml" = lib.mkIf (cfg.policy != { }) { + source = yamlFormat.generate "tirith-policy.yaml" cfg.policy; + }; + }; + + programs.bash.initExtra = lib.mkIf cfg.enableBashIntegration '' + eval "$(${lib.getExe cfg.package} init --shell bash)" + ''; + + programs.fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration '' + ${lib.getExe cfg.package} init --shell fish | source + ''; + + programs.zsh.initExtra = lib.mkIf cfg.enableZshIntegration '' + eval "$(${lib.getExe cfg.package} init --shell zsh)" + ''; + }; +} diff --git a/tests/modules/programs/tirith/basic.nix b/tests/modules/programs/tirith/basic.nix new file mode 100644 index 00000000..dc2451a4 --- /dev/null +++ b/tests/modules/programs/tirith/basic.nix @@ -0,0 +1,27 @@ +{ ... }: + +{ + programs.tirith = { + enable = true; + allowlist = [ + "localhost" + "example.com" + ]; + policy = { + version = 1; + fail_mode = "open"; + }; + }; + + nmt.script = '' + assertFileExists home-files/.config/tirith/allowlist + assertFileContent \ + home-files/.config/tirith/allowlist \ + ${builtins.toFile "expected" '' + localhost + example.com + ''} + + assertFileExists home-files/.config/tirith/policy.yaml + ''; +} diff --git a/tests/modules/programs/tirith/default.nix b/tests/modules/programs/tirith/default.nix new file mode 100644 index 00000000..a5a837c9 --- /dev/null +++ b/tests/modules/programs/tirith/default.nix @@ -0,0 +1,3 @@ +{ + tirith-basic = ./basic.nix; +} From 975606b2a56aeb274c7ad837460094a183ddbe0a Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:05:56 +0000 Subject: [PATCH 44/83] 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/d6c71932130818840fc8fe9509cf50be8c64634f?narHash=sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84%3D' (2026-02-08) → 'github:NixOS/nixpkgs/ec7c70d12ce2fc37cb92aff673dcdca89d187bae?narHash=sha256-9xejG0KoqsoKEGp2kVbXRlEYtFFcDTHjidiuX8hGO44%3D' (2026-02-11) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index e5578c1f..1b8772e3 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1770562336, - "narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=", + "lastModified": 1770841267, + "narHash": "sha256-9xejG0KoqsoKEGp2kVbXRlEYtFFcDTHjidiuX8hGO44=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d6c71932130818840fc8fe9509cf50be8c64634f", + "rev": "ec7c70d12ce2fc37cb92aff673dcdca89d187bae", "type": "github" }, "original": { From 54ab8bc155888f3894327bfb67b821e21723a5c4 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 13 Feb 2026 13:22:59 -0600 Subject: [PATCH 45/83] maintainers: remove offline Removed upstream https://github.com/NixOS/nixpkgs/pull/483520 Signed-off-by: Austin Horstman --- all-maintainers.nix | 7 ------- modules/services/xsuspender.nix | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/all-maintainers.nix b/all-maintainers.nix index 5923a8fb..5cb3f9b0 100644 --- a/all-maintainers.nix +++ b/all-maintainers.nix @@ -1827,13 +1827,6 @@ name = "Judson Lester"; source = "nixpkgs"; }; - offlinehacker = { - email = "jaka@x-truder.net"; - github = "offlinehacker"; - githubId = 585547; - name = "Jaka Hudoklin"; - source = "nixpkgs"; - }; olmokramer = { email = "olmokramer@users.noreply.github.com"; github = "olmokramer"; diff --git a/modules/services/xsuspender.nix b/modules/services/xsuspender.nix index 5b31a252..aed36fcc 100644 --- a/modules/services/xsuspender.nix +++ b/modules/services/xsuspender.nix @@ -113,7 +113,7 @@ let in { - meta.maintainers = [ lib.maintainers.offline ]; + meta.maintainers = [ ]; options = { services.xsuspender = { From 3017414609212b435231b37971fba1af4f478c40 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 22:04:50 -0600 Subject: [PATCH 46/83] launchd: add missing option description Signed-off-by: Austin Horstman --- modules/launchd/launchd.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/launchd/launchd.nix b/modules/launchd/launchd.nix index a212efd3..ee202944 100644 --- a/modules/launchd/launchd.nix +++ b/modules/launchd/launchd.nix @@ -259,6 +259,10 @@ in AfterInitialDemand = mkOption { type = types.nullOr types.bool; default = null; + description = '' + Whether to defer evaluating other `KeepAlive` conditions until + the job has been started at least once by demand. + ''; }; }; From 4c4771cb01a6f3577b3792ea7bbd151634c4c0d2 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 22:04:57 -0600 Subject: [PATCH 47/83] rofi: add missing option description Signed-off-by: Austin Horstman --- modules/programs/rofi.nix | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/programs/rofi.nix b/modules/programs/rofi.nix index 7925fac6..92cc6543 100644 --- a/modules/programs/rofi.nix +++ b/modules/programs/rofi.nix @@ -275,8 +275,14 @@ in listOf ( either str (submodule { options = { - name = mkOption { type = str; }; - path = mkOption { type = str; }; + name = mkOption { + type = str; + description = "Name used to reference the custom mode in the mode list."; + }; + path = mkOption { + type = str; + description = "Executable path for the custom rofi script mode."; + }; }; }) ); From 29d617ecc8bba39853fcf3543c51de95455b3159 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 22:07:55 -0600 Subject: [PATCH 48/83] docs: improve submodule options rendering Certain types don't offer `getSubOptions` upstream in nixpkgs, at the moment. We can do some overriding and manually calling it for now to fetch the options for docs. Signed-off-by: Austin Horstman --- docs/default.nix | 56 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/docs/default.nix b/docs/default.nix index 0d8bc8c2..f374f93b 100644 --- a/docs/default.nix +++ b/docs/default.nix @@ -69,6 +69,57 @@ let hmPath = toString ./..; + # Keep submodule option docs visible when wrapped in `either` (and therefore + # in `nullOr (either ...)`), which upstream currently omits. + docsLib = lib.extend ( + _self: super: + let + mergeEitherSubOptions = + prefix: leftType: rightType: + let + getSubOptionsOrEmpty = + optionType: + let + subOptions = optionType.getSubOptions prefix; + in + if builtins.isAttrs subOptions then subOptions else { }; + + mkOptionDecl = options: { + _file = ""; + pos = null; + inherit options; + }; + + optionSets = lib.filter (options: options != { }) [ + (getSubOptionsOrEmpty leftType) + (getSubOptionsOrEmpty rightType) + ]; + mergedOptions = lib.foldl' ( + acc: options: + if acc == { } then + options + else + (super.mergeOptionDecls prefix [ + (mkOptionDecl acc) + (mkOptionDecl options) + ]).options + ) { } optionSets; + in + mergedOptions; + + in + { + types = super.types // { + either = + leftType: rightType: + (super.types.either leftType rightType) + // { + getSubOptions = prefix: mergeEitherSubOptions prefix leftType rightType; + }; + }; + } + ); + buildOptionsDocs = args@{ modules, @@ -103,7 +154,7 @@ let }; options = - (lib.evalModules { + (docsLib.evalModules { modules = modules ++ [ poisonModule ]; class = "homeManager"; }).options; @@ -141,7 +192,8 @@ let hmOptionsDocs = buildOptionsDocs { modules = import ../modules/modules.nix { - inherit lib pkgs; + lib = docsLib; + inherit pkgs; check = false; } ++ [ scrubbedPkgsModule ]; From bc5652b22775f4e882f07116123697d4f4702ce1 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 22:53:42 -0600 Subject: [PATCH 49/83] tests/types: add suboptions doc test Verify we are able to extract suboptions properly with our custom lib extension. Signed-off-by: Austin Horstman --- docs/default.nix | 3 ++ tests/lib/types/default.nix | 2 + .../lib/types/either-suboptions-docs-lib.nix | 49 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 tests/lib/types/either-suboptions-docs-lib.nix diff --git a/docs/default.nix b/docs/default.nix index f374f93b..4098bcc0 100644 --- a/docs/default.nix +++ b/docs/default.nix @@ -298,4 +298,7 @@ in in builtins.toJSON result.config.meta.maintainers ); + + # Unstable, for tests. + _internal = { inherit docsLib; }; } diff --git a/tests/lib/types/default.nix b/tests/lib/types/default.nix index f8a7e77c..c9ed3031 100644 --- a/tests/lib/types/default.nix +++ b/tests/lib/types/default.nix @@ -2,5 +2,7 @@ lib-types-dag-submodule = ./dag-submodule.nix; lib-types-dag-merge = ./dag-merge.nix; + lib-types-either-suboptions-docs-lib = ./either-suboptions-docs-lib.nix; + lib-types-gvariant-merge = ./gvariant-merge.nix; } diff --git a/tests/lib/types/either-suboptions-docs-lib.nix b/tests/lib/types/either-suboptions-docs-lib.nix new file mode 100644 index 00000000..b57cf5b5 --- /dev/null +++ b/tests/lib/types/either-suboptions-docs-lib.nix @@ -0,0 +1,49 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + inherit (lib) mkOption; + + docs = import ../../../docs { + inherit pkgs lib; + inherit (config.home.version) release isReleaseBranch; + }; + + inherit (docs._internal.docsLib) types; + + scalarOrSubmodule = types.either types.str ( + types.submodule { + options = { + foo = mkOption { type = types.str; }; + bar = mkOption { type = types.int; }; + }; + } + ); + + scalarOrSubmoduleSubOptions = scalarOrSubmodule.getSubOptions [ ]; + nullOrScalarOrSubmoduleSubOptions = (types.nullOr scalarOrSubmodule).getSubOptions [ ]; +in +{ + assertions = [ + { + assertion = scalarOrSubmoduleSubOptions ? foo; + message = "docsLib.types.either should expose submodule options when one side is scalar."; + } + { + assertion = scalarOrSubmoduleSubOptions ? bar; + message = "docsLib.types.either should expose all submodule options when one side is scalar."; + } + { + assertion = nullOrScalarOrSubmoduleSubOptions ? foo; + message = "docsLib.types.nullOr (types.either ...) should keep submodule options visible."; + } + { + assertion = nullOrScalarOrSubmoduleSubOptions ? bar; + message = "docsLib.types.nullOr (types.either ...) should keep all submodule options visible."; + } + ]; +} From cadfe449aa5cbcfe1543c921526bf0a751e94eb3 Mon Sep 17 00:00:00 2001 From: Peter Kling <1018801+pitkling@users.noreply.github.com> Date: Thu, 17 Apr 2025 09:48:48 +0200 Subject: [PATCH 50/83] syncthing: more reliable syncthing launchd agent Starts the syncthing-init launchd agent (responsible for updating the configuration) as a oneshot agent instead of via WatchPaths (the latter is too unreliable). Ideally, the syncthing-init agent should be started after the syncthing agent started the Syncthing server, since syncthing-init updates the configuration using API calls to the Syncthing server. The Linux systemd service versions handle this via the Requires and After attribute. Launchd under macOS is missing a reliable way to order agents. Theoretically, one can achieve similar things via, e.g., WatchPaths (used before this commit). But this is known to be very unreliable (see also my comment in issue #6542). Thus, we just start syncthing-init and rely on the wrapped curl bash function (see curlShellFunction in nix file) to wait for the Syncthing server to be up and running. --- modules/services/syncthing.nix | 55 +++++++++++++++------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/modules/services/syncthing.nix b/modules/services/syncthing.nix index a924caaf..1de24029 100644 --- a/modules/services/syncthing.nix +++ b/modules/services/syncthing.nix @@ -825,40 +825,33 @@ in }; }; - launchd.agents = - let - # agent `syncthing` uses `${syncthing_dir}/${watch_file}` to notify agent `syncthing-init` - watch_file = ".launchd_update_config"; - in - { - syncthing = { - enable = true; - config = { - ProgramArguments = [ - "${pkgs.writers.writeBash "syncthing-wrapper" '' - ${copyKeys} # simulate systemd's `syncthing-init.Service.ExecStartPre` - touch "${syncthing_dir}/${watch_file}" # notify syncthing-init agent - exec ${lib.escapeShellArgs syncthingArgs} - ''}" - ]; - KeepAlive = { - Crashed = true; - SuccessfulExit = false; - }; - ProcessType = "Background"; - }; - }; - - syncthing-init = { - enable = cleanedConfig != { }; - config = { - ProgramArguments = [ "${updateConfig}" ]; - WatchPaths = [ - "${config.home.homeDirectory}/Library/Application Support/Syncthing/${watch_file}" - ]; + launchd.agents = { + syncthing = { + enable = true; + config = { + ProgramArguments = [ + "${pkgs.writers.writeBash "syncthing-wrapper" '' + ${copyKeys} # simulate systemd's `syncthing-init.Service.ExecStartPre` + exec ${lib.escapeShellArgs syncthingArgs} + ''}" + ]; + KeepAlive = { + Crashed = true; + SuccessfulExit = false; }; + ProcessType = "Background"; }; }; + + syncthing-init = { + enable = cleanedConfig != { }; + config = { + ProgramArguments = [ "${updateConfig}" ]; + ProcessType = "Background"; + RunAtLoad = true; + }; + }; + }; }) (lib.mkIf cfg.tray.enable { From e9b706bef7facaacc7881c9336401231d343ffee Mon Sep 17 00:00:00 2001 From: Peter Kling <1018801+pitkling@users.noreply.github.com> Date: Thu, 1 Jan 2026 13:04:41 +0100 Subject: [PATCH 51/83] syncthing: activate logging for macOS agents --- modules/services/syncthing.nix | 4 ++++ tests/modules/services/syncthing/expected-agent.plist | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/modules/services/syncthing.nix b/modules/services/syncthing.nix index 1de24029..a8b38318 100644 --- a/modules/services/syncthing.nix +++ b/modules/services/syncthing.nix @@ -840,6 +840,8 @@ in SuccessfulExit = false; }; ProcessType = "Background"; + StandardOutPath = "${config.home.homeDirectory}/Library/Logs/Syncthing/syncthing-stdout.log"; + StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/Syncthing/syncthing-stderr.log"; }; }; @@ -849,6 +851,8 @@ in ProgramArguments = [ "${updateConfig}" ]; ProcessType = "Background"; RunAtLoad = true; + StandardOutPath = "${config.home.homeDirectory}/Library/Logs/Syncthing/syncthing-init-stdout.log"; + StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/Syncthing/syncthing-init-stderr.log"; }; }; }; diff --git a/tests/modules/services/syncthing/expected-agent.plist b/tests/modules/services/syncthing/expected-agent.plist index 72cf9870..79550be9 100644 --- a/tests/modules/services/syncthing/expected-agent.plist +++ b/tests/modules/services/syncthing/expected-agent.plist @@ -19,5 +19,9 @@ -c /bin/wait4path /nix/store && exec @syncthing-wrapper@ + StandardErrorPath + /home/hm-user/Library/Logs/Syncthing/syncthing-stderr.log + StandardOutPath + /home/hm-user/Library/Logs/Syncthing/syncthing-stdout.log \ No newline at end of file From b3258dece8af087fd471a4ca6b106d02c330dd37 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 12 Feb 2026 16:30:20 +0100 Subject: [PATCH 52/83] programs.radicle: Document how "rad auth" must be used with this Signed-off-by: Matthias Beyer --- modules/programs/radicle.nix | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/programs/radicle.nix b/modules/programs/radicle.nix index 3fd51e35..38334bb9 100644 --- a/modules/programs/radicle.nix +++ b/modules/programs/radicle.nix @@ -136,7 +136,13 @@ in node = { alias = mkOption { type = str; - description = "Human readable alias for your node."; + description = '' + Human readable alias for your node. + + When running 'rad auth' for the first time, you need to use the exact same alias, + otherwise 'rad auth' will fail because it cannot write the configuration + file in your home directory (which is generated by nix). + ''; default = config.home.username; defaultText = lib.literalExpression "config.home.username"; }; From 26dfad95d92c50a56ce708f4256bf720bb30a630 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Mon, 12 Jan 2026 20:54:04 -0500 Subject: [PATCH 53/83] nix: add assumeXdg option The use-xdg-base-directories Nix setting can be set globally in /etc/nix/nix.conf, in which case Home Manager doesn't know about it. Users could fix that by also setting it, redundantly, in `nix.settings`, but then Nix issues a lot of spurious warnings about use-xdg-base-directories being a restricted setting that untrusted users can't pass on to the daemon. As an alternative, users can now set `nix.assumeXdg`, which makes Home Manager assume that use-xdg-base-directories is in effect without adding it to the user's nix.conf file. --- modules/home-environment.nix | 2 +- modules/misc/nix.nix | 108 +++++++++++++----- modules/targets/generic-linux.nix | 2 +- tests/modules/misc/nix/default.nix | 1 + tests/modules/misc/nix/use-xdg.nix | 92 +++++++++++++++ tests/modules/targets-linux/default.nix | 1 + .../targets-linux/generic-linux-xdg.nix | 39 +++++++ 7 files changed, 217 insertions(+), 28 deletions(-) create mode 100644 tests/modules/misc/nix/use-xdg.nix create mode 100644 tests/modules/targets-linux/generic-linux-xdg.nix diff --git a/modules/home-environment.nix b/modules/home-environment.nix index bc131015..4f4904c7 100644 --- a/modules/home-environment.nix +++ b/modules/home-environment.nix @@ -618,7 +618,7 @@ in home.profileDirectory = if config.submoduleSupport.enable && config.submoduleSupport.externalPackageInstall then "/etc/profiles/per-user/${cfg.username}" - else if config.nix.enable && (config.nix.settings.use-xdg-base-directories or false) then + else if config.nix.useXdg then "${config.xdg.stateHome}/nix/profile" else cfg.homeDirectory + "/.nix-profile"; diff --git a/modules/misc/nix.nix b/modules/misc/nix.nix index c07e0330..11b22296 100644 --- a/modules/misc/nix.nix +++ b/modules/misc/nix.nix @@ -1,6 +1,7 @@ { config, lib, + osConfig, pkgs, ... }: @@ -21,6 +22,7 @@ let isList isString literalExpression + literalMD mapAttrsToList mkDefault mkEnableOption @@ -41,7 +43,7 @@ let nixPath = concatStringsSep ":" cfg.nixPath; - useXdg = config.nix.enable && (config.nix.settings.use-xdg-base-directories or false); + inherit (config.nix) useXdg; defexprDir = if useXdg then "${config.xdg.stateHome}/nix/defexpr" @@ -296,6 +298,30 @@ in ''; }; + assumeXdg = mkOption { + type = types.bool; + default = false; + description = '' + Whether Home Manager should assume that Nix is configured to use XDG + base directories. Note that this doesn't change the behavior of Nix. To + do that, set nix.settings.use-xdg-base-directories instead. This option + is intended for settings in which use-xdg-base-directories is set + globally or nix.conf is unmanaged by Home Manager. + ''; + }; + + useXdg = mkOption { + type = types.bool; + visible = false; + readOnly = true; + defaultText = literalMD '' + In descending priority: + * `true` if `nix.assumeXdg` is `true` + * `nix.settings.use-xdg-base-directories` if it is set and `nix.enable` is `true` + * `osConfig.nix.settings.use-xdg-base-directories` if it is set and `osConfig.nix.enable` is `true` + ''; + }; + extraOptions = mkOption { type = types.lines; default = ""; @@ -326,39 +352,69 @@ in }; }; - config = mkIf cfg.enable (mkMerge [ - (mkIf (cfg.nixPath != [ ] && !cfg.keepOldNixPath) { - home.sessionVariables.NIX_PATH = "${nixPath}"; - }) + config = mkMerge [ + { + nix.useXdg = + let + # Helper that checks if use-xdg-base-directories is in effect for the + # given Nix configuration, falling back to a given default value if + # it is undefined or the configuration is not enabled. + checkNixXdg = def: cfg: if cfg.enable then cfg.settings.use-xdg-base-directories or def else def; - (mkIf (cfg.nixPath != [ ] && cfg.keepOldNixPath) { - home.sessionVariables.NIX_PATH = "${nixPath}\${NIX_PATH:+:$NIX_PATH}"; - }) + # Whether the OS configuration indicates that XDG directories should + # be used. + osUseXdg = checkNixXdg false osConfig.nix or { enable = false; }; - (lib.mkIf (cfg.channels != { }) { - nix.nixPath = [ channelPath ]; - home.file."${channelPath}".source = channelsDrv; - }) + # Whether the user configuration indicates that XDG directories + # should be used, falling back to the OS configuration if not + # specified. + hmUseXdg = checkNixXdg osUseXdg cfg; + in + cfg.assumeXdg || hmUseXdg; - (mkIf (cfg.registry != { }) { - xdg.configFile."nix/registry.json".source = jsonFormat.generate "registry.json" { - version = cfg.registryVersion; - flakes = mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry; - }; - }) - - (mkIf (cfg.settings != { } || cfg.extraOptions != "") { assertions = [ { - assertion = cfg.package != null; + assertion = !(cfg.assumeXdg && cfg.enable && cfg.settings ? use-xdg-base-directories); message = '' - A corresponding Nix package must be specified via `nix.package` for generating - nix.conf. + `nix.assumeXdg` should not be set if `nix.settings.use-xdg-base-directories` is. ''; } ]; + } + (mkIf cfg.enable (mkMerge [ + (mkIf (cfg.nixPath != [ ] && !cfg.keepOldNixPath) { + home.sessionVariables.NIX_PATH = "${nixPath}"; + }) - xdg.configFile."nix/nix.conf".source = nixConf; - }) - ]); + (mkIf (cfg.nixPath != [ ] && cfg.keepOldNixPath) { + home.sessionVariables.NIX_PATH = "${nixPath}\${NIX_PATH:+:$NIX_PATH}"; + }) + + (lib.mkIf (cfg.channels != { }) { + nix.nixPath = [ channelPath ]; + home.file."${channelPath}".source = channelsDrv; + }) + + (mkIf (cfg.registry != { }) { + xdg.configFile."nix/registry.json".source = jsonFormat.generate "registry.json" { + version = cfg.registryVersion; + flakes = mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry; + }; + }) + + (mkIf (cfg.settings != { } || cfg.extraOptions != "") { + assertions = [ + { + assertion = cfg.package != null; + message = '' + A corresponding Nix package must be specified via `nix.package` for generating + nix.conf. + ''; + } + ]; + + xdg.configFile."nix/nix.conf".source = nixConf; + }) + ])) + ]; } diff --git a/modules/targets/generic-linux.nix b/modules/targets/generic-linux.nix index 31529fc2..6ebf5d9c 100644 --- a/modules/targets/generic-linux.nix +++ b/modules/targets/generic-linux.nix @@ -112,7 +112,7 @@ in in { NIX_PATH = - if config.nix.enable && (config.nix.settings.use-xdg-base-directories or false) then + if config.nix.useXdg then "${config.xdg.stateHome}/nix/defexpr/channels\${NIX_PATH:+:}$NIX_PATH" else "$HOME/.nix-defexpr/channels\${NIX_PATH:+:}$NIX_PATH"; diff --git a/tests/modules/misc/nix/default.nix b/tests/modules/misc/nix/default.nix index 4c8822bf..50a8b1e3 100644 --- a/tests/modules/misc/nix/default.nix +++ b/tests/modules/misc/nix/default.nix @@ -5,4 +5,5 @@ nix-keep-old-nix-path = ./keep-old-nix-path.nix; nix-example-channels = ./example-channels.nix; nix-example-channels-xdg = ./example-channels-xdg.nix; + nix-use-xdg = ./use-xdg.nix; } diff --git a/tests/modules/misc/nix/use-xdg.nix b/tests/modules/misc/nix/use-xdg.nix new file mode 100644 index 00000000..f4d4b189 --- /dev/null +++ b/tests/modules/misc/nix/use-xdg.nix @@ -0,0 +1,92 @@ +{ lib, pkgs, ... }: +let + nixosLib = import /${pkgs.path}/nixos/lib { inherit lib; }; + getUseXdg = + osNix: userNix: + (nixosLib.evalTest { + hostPkgs = pkgs; + nodes.machine.imports = [ + ../../../../nixos + { + nix = osNix; + home-manager.users.user = { + home.stateVersion = "26.05"; + nix = userNix; + }; + } + ]; + }).config.nodes.machine.home-manager.users.user.nix.useXdg; +in +{ + nmt.script = + # Defaults to false + assert !getUseXdg { } { }; + + # Test OS config + assert !getUseXdg { enable = true; } { }; + assert + !getUseXdg { + enable = false; + settings.use-xdg-base-directories = true; + } { }; + assert getUseXdg { + enable = true; + settings.use-xdg-base-directories = true; + } { }; + + # Test user config + assert !getUseXdg { } { enable = true; }; + assert + !getUseXdg { } { + enable = false; + settings.use-xdg-base-directories = true; + }; + assert getUseXdg { } { + enable = true; + settings.use-xdg-base-directories = true; + }; + + # Fallback to OS config if user config is unset + assert getUseXdg + { + enable = true; + settings.use-xdg-base-directories = true; + } + { + enable = true; + }; + + # But user config takes precedence + assert + !getUseXdg + { + enable = true; + settings.use-xdg-base-directories = true; + } + { + enable = true; + settings.use-xdg-base-directories = false; + }; + assert getUseXdg + { + enable = true; + settings.use-xdg-base-directories = false; + } + { + enable = true; + settings.use-xdg-base-directories = true; + }; + + # assumeXdg also takes precedence + assert getUseXdg { } { assumeXdg = true; }; + assert getUseXdg + { + enable = true; + settings.use-xdg-base-directories = false; + } + { + enable = false; + assumeXdg = true; + }; + ""; +} diff --git a/tests/modules/targets-linux/default.nix b/tests/modules/targets-linux/default.nix index 24786f8d..5ae5fd0f 100644 --- a/tests/modules/targets-linux/default.nix +++ b/tests/modules/targets-linux/default.nix @@ -3,4 +3,5 @@ targets-generic-linux-gpu-basic = ./generic-linux-gpu/basic-enable.nix; targets-generic-linux-gpu-setup-contents = ./generic-linux-gpu/setup-package-contents.nix; targets-generic-linux-gpu-nvidia = ./generic-linux-gpu/nvidia-enabled.nix; + targets-generic-linux-xdg = ./generic-linux-xdg.nix; } diff --git a/tests/modules/targets-linux/generic-linux-xdg.nix b/tests/modules/targets-linux/generic-linux-xdg.nix new file mode 100644 index 00000000..fa462b86 --- /dev/null +++ b/tests/modules/targets-linux/generic-linux-xdg.nix @@ -0,0 +1,39 @@ +{ lib, pkgs, ... }: +let + expectedXdgDataDirs = lib.concatStringsSep ":" [ + "\${NIX_STATE_DIR:-/nix/var/nix}/profiles/default/share" + "/home/hm-user/.local/state/nix/profile/share" + "/usr/share/ubuntu" + "/usr/local/share" + "/usr/share" + "/var/lib/snapd/desktop" + "/foo" + ]; +in +{ + config = { + targets.genericLinux.enable = true; + + xdg.systemDirs.data = [ "/foo" ]; + + nix.assumeXdg = true; + + nmt.script = '' + envFile=home-files/.config/environment.d/10-home-manager.conf + assertFileExists $envFile + assertFileContains $envFile \ + 'XDG_DATA_DIRS=${expectedXdgDataDirs}''${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}' + assertFileContains $envFile \ + 'TERMINFO_DIRS=/home/hm-user/.local/state/nix/profile/share/terminfo:$TERMINFO_DIRS''${TERMINFO_DIRS:+:}/etc/terminfo:/lib/terminfo:/usr/share/terminfo' + + sessionVarsFile=home-path/etc/profile.d/hm-session-vars.sh + assertFileExists $sessionVarsFile + assertFileContains $sessionVarsFile \ + '. "${pkgs.nix}/etc/profile.d/nix.sh"' + + assertFileContains \ + home-path/etc/profile.d/hm-session-vars.sh \ + 'export TERM="$TERM"' + ''; + }; +} From 05e6dc0f6ed936f918cb6f0f21f1dad1e4c53150 Mon Sep 17 00:00:00 2001 From: mikaeladev <100416544+mikaeladev@users.noreply.github.com> Date: Sat, 14 Feb 2026 02:04:30 +0000 Subject: [PATCH 54/83] qt: fix typo in qtct example --- modules/misc/qt.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/misc/qt.nix b/modules/misc/qt.nix index 6bee4217..d83311b2 100644 --- a/modules/misc/qt.nix +++ b/modules/misc/qt.nix @@ -302,7 +302,7 @@ in Appearance = { style = "kvantum"; icon_theme = "Papirus-Dark"; - standar_dialogs = "xdgdesktopportal"; + standard_dialogs = "xdgdesktopportal"; }; Fonts = { fixed = "\"DejaVuSansM Nerd Font Mono,12\""; From ff5e5d882c51f9a032479595cbab40fd04f56399 Mon Sep 17 00:00:00 2001 From: Bruno Bigras <24027+bbigras@users.noreply.github.com> Date: Fri, 13 Feb 2026 23:26:14 -0500 Subject: [PATCH 55/83] tirith: fix case sensitive example `LOW`, `MEDIUM`, `HIGH` and `CRITICAL` are case sensitive --- modules/programs/tirith.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/tirith.nix b/modules/programs/tirith.nix index d8703d3e..c0fa9688 100644 --- a/modules/programs/tirith.nix +++ b/modules/programs/tirith.nix @@ -38,7 +38,7 @@ in fail_mode = "open"; allow_bypass = true; severity_overrides = { - docker_untrusted_registry = "critical"; + docker_untrusted_registry = "CRITICAL"; }; } ''; From ee49ee29a1fc37f7cc0de65cb5e884a3e090f528 Mon Sep 17 00:00:00 2001 From: phucleeuwu <125681538+phucleeuwu@users.noreply.github.com> Date: Sat, 31 May 2025 19:26:16 +0700 Subject: [PATCH 56/83] =?UTF-8?q?yazi:=20change=20the=20default=20`shellWr?= =?UTF-8?q?apperName`=20`yy`=20=E2=86=92`=20y`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Austin Horstman --- modules/programs/yazi.nix | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/programs/yazi.nix b/modules/programs/yazi.nix index 0fcf0247..9aaa445a 100644 --- a/modules/programs/yazi.nix +++ b/modules/programs/yazi.nix @@ -48,8 +48,23 @@ in shellWrapperName = lib.mkOption { type = types.str; - default = "yy"; - example = "y"; + default = + if lib.versionAtLeast config.home.stateVersion "26.05" then + "y" + else + lib.warn '' + The default value of `programs.yazi.shellWrapperName` has changed from `yy` to `y`. + You are currently using the legacy default (`yy`) because `home.stateVersion` is less than "26.05". + To silence this warning and keep legacy behavior, set: + programs.yazi.shellWrapperName = "yy"; + To adopt the new default behavior, set: + programs.yazi.shellWrapperName = "y"; + '' "yy"; + defaultText = literalExpression '' + "y" for state version ≥ 26.05 + "yy" for state version < 26.05 + ''; + example = "yy"; description = '' Name of the shell wrapper to be called. ''; From 1e53254671f36cb7d0e2dcca08730f066d5e69b4 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 13 Feb 2026 14:18:50 -0600 Subject: [PATCH 57/83] news: inform about yazi alias change Signed-off-by: Austin Horstman --- docs/release-notes/rl-2605.md | 4 ++++ modules/misc/news/2026/02/2026-02-13_14-17-15.nix | 9 +++++++++ 2 files changed, 13 insertions(+) create mode 100644 modules/misc/news/2026/02/2026-02-13_14-17-15.nix diff --git a/docs/release-notes/rl-2605.md b/docs/release-notes/rl-2605.md index 4758869a..848802dd 100644 --- a/docs/release-notes/rl-2605.md +++ b/docs/release-notes/rl-2605.md @@ -24,6 +24,10 @@ changes are only active if the `home.stateVersion` option is set to configuration directory (usually `~/.config/zsh`) when [](#opt-xdg.enable) is true. +- The [](#opt-programs.yazi.shellWrapperName) option now defaults to + `y` instead of `yy`. For users with older `home.stateVersion` values, + the legacy default `yy` is retained. + - The [](#opt-xdg.userDirs.setSessionVariables) option now defaults to `false` instead of `true`. diff --git a/modules/misc/news/2026/02/2026-02-13_14-17-15.nix b/modules/misc/news/2026/02/2026-02-13_14-17-15.nix new file mode 100644 index 00000000..1596a623 --- /dev/null +++ b/modules/misc/news/2026/02/2026-02-13_14-17-15.nix @@ -0,0 +1,9 @@ +{ config, ... }: +{ + time = "2026-02-13T20:17:15+00:00"; + condition = config.programs.yazi.enable; + message = " + The option `programs.yazi.shellWrapperName` default has changed from `yy` to `y` + to align with the recommendation from upstream. + "; +} From ae8003d8b61d0d373e7ca3da1a48f9c870d15df9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 12 Feb 2026 21:36:59 -0600 Subject: [PATCH 58/83] mkFirefoxModule: remove visible option Just default everything to show options to remove confusion / chance of disabling documentation. We already set visible = true so it wasn't even being used really. Signed-off-by: Austin Horstman --- modules/programs/firefox/default.nix | 1 - modules/programs/firefox/mkFirefoxModule.nix | 8 -------- modules/programs/floorp.nix | 1 - modules/programs/librewolf.nix | 1 - 4 files changed, 11 deletions(-) diff --git a/modules/programs/firefox/default.nix b/modules/programs/firefox/default.nix index f9bbdfac..a9600c71 100644 --- a/modules/programs/firefox/default.nix +++ b/modules/programs/firefox/default.nix @@ -26,7 +26,6 @@ in name = "Firefox"; wrappedPackageName = "firefox"; unwrappedPackageName = "firefox-unwrapped"; - visible = true; platforms.linux = { configPath = ".mozilla/firefox"; diff --git a/modules/programs/firefox/mkFirefoxModule.nix b/modules/programs/firefox/mkFirefoxModule.nix index 5cf049c2..13e61c89 100644 --- a/modules/programs/firefox/mkFirefoxModule.nix +++ b/modules/programs/firefox/mkFirefoxModule.nix @@ -5,7 +5,6 @@ wrappedPackageName ? null, unwrappedPackageName ? null, platforms, - visible ? false, enableBookmarks ? true, }: { @@ -209,12 +208,10 @@ in example = true; description = '' Whether to enable ${appName}.${optionalString (description != null) " ${description}"} - ${optionalString (!visible) "See `${moduleName}` for more configuration options."} ''; }; package = mkOption { - inherit visible; type = with types; nullOr package; default = pkgs.${defaultPackageName}; defaultText = literalExpression "pkgs.${packageName}"; @@ -317,7 +314,6 @@ in }; nativeMessagingHosts = mkOption { - inherit visible; type = types.listOf types.package; default = [ ]; description = '' @@ -327,14 +323,12 @@ in }; finalPackage = mkOption { - inherit visible; type = with types; nullOr package; readOnly = true; description = "Resulting ${appName} package."; }; policies = lib.optionalAttrs (wrappedPackageName != null) (mkOption { - inherit visible; type = types.attrsOf jsonFormat.type; default = { }; description = "[See list of policies](https://mozilla.github.io/policy-templates/)."; @@ -360,7 +354,6 @@ in }; profiles = mkOption { - inherit visible; type = types.attrsOf ( types.submodule ( { config, name, ... }: @@ -913,7 +906,6 @@ in }; enableGnomeExtensions = mkOption { - inherit visible; type = types.bool; default = false; description = '' diff --git a/modules/programs/floorp.nix b/modules/programs/floorp.nix index 29db1633..2da23672 100644 --- a/modules/programs/floorp.nix +++ b/modules/programs/floorp.nix @@ -18,7 +18,6 @@ in name = "Floorp"; wrappedPackageName = "floorp-bin"; unwrappedPackageName = "floorp-bin-unwrapped"; - visible = true; platforms.linux = { configPath = ".floorp"; diff --git a/modules/programs/librewolf.nix b/modules/programs/librewolf.nix index b90e666b..c69d2322 100644 --- a/modules/programs/librewolf.nix +++ b/modules/programs/librewolf.nix @@ -34,7 +34,6 @@ in description = "LibreWolf is a privacy enhanced Firefox fork."; wrappedPackageName = "librewolf"; unwrappedPackageName = "librewolf-unwrapped"; - visible = true; platforms.linux = { configPath = ".librewolf"; From c7d7ba954aa2bdbf141a29cc1167afbd32ab9f14 Mon Sep 17 00:00:00 2001 From: teto <886074+teto@users.noreply.github.com> Date: Mon, 16 Feb 2026 04:35:03 +0100 Subject: [PATCH 59/83] mpv: fix include example/improve doc I just learnt about "~~/config.inc" syntax from https://mpv.io/manual/stable/#paths which is quite nice. --- modules/programs/mpv.nix | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/programs/mpv.nix b/modules/programs/mpv.nix index a9b6f8d8..2438163c 100644 --- a/modules/programs/mpv.nix +++ b/modules/programs/mpv.nix @@ -139,11 +139,14 @@ in default = [ ]; example = literalExpression '' [ - "~/path/to/config.inc"; - "~/path/to/conditional.inc"; + "~/path/to/config.inc" + "~~/conditional.inc" ] ''; - description = "List of configuration files to include at the end of mpv.conf."; + description = '' + List of configuration files to include at the end of mpv.conf. + Mpv accepts several useful [prefixes](https://mpv.io/manual/stable/#paths). + ''; }; profiles = mkOption { From 5f1d42a97b19803041434f66681d5c44c9ae62e3 Mon Sep 17 00:00:00 2001 From: "home-manager-ci[bot]" <214323736+home-manager-ci[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 09:43:28 +0000 Subject: [PATCH 60/83] 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:** 282 → 283 maintainers **✅ Added:** malikwirin Generated by: lib/python/generate-all-maintainers.py --- all-maintainers.nix | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/all-maintainers.nix b/all-maintainers.nix index 5cb3f9b0..faad4a5e 100644 --- a/all-maintainers.nix +++ b/all-maintainers.nix @@ -50,7 +50,7 @@ source = "nixpkgs"; }; ALameLlama = { - email = "NicholasACiechanowski@gmail.com"; + email = "nicholas@ciech.anow.ski"; github = "ALameLlama"; githubId = 55490546; name = "Nicholas Ciechanowski"; @@ -1551,6 +1551,18 @@ name = "mainrs"; source = "home-manager"; }; + malikwirin = { + email = "abdelmalik.najhi@stud.hs-kempten.de"; + github = "malikwirin"; + githubId = 117918464; + keys = [ + { + fingerprint = "B5ED 595C 8C7E 133C 6B68 63C8 CFEF 1E35 0351 F72D"; + } + ]; + name = "Malik"; + source = "nixpkgs"; + }; malte-v = { email = "nixpkgs@mal.tc"; github = "malte-v"; From b0b01ab65b0c29be841ae1ca8f5dd2e5ff51c4b1 Mon Sep 17 00:00:00 2001 From: teto <886074+teto@users.noreply.github.com> Date: Wed, 18 Feb 2026 11:08:26 +0100 Subject: [PATCH 61/83] Revert "feat: introduce nix-shell" This reverts commit bb45c036593f4da6a9490fd7593f60a76c444ad2. --- shell.nix | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 shell.nix diff --git a/shell.nix b/shell.nix deleted file mode 100644 index f983d48d..00000000 --- a/shell.nix +++ /dev/null @@ -1,15 +0,0 @@ -let - nixpkgs = ( - import ( - let - lock = builtins.fromJSON (builtins.readFile ./flake.lock); - n = lock.nodes.nixpkgs.locked; - in - fetchTarball { - url = "https://github.com/${n.owner}/${n.repo}/archive/${n.rev}.tar.gz"; - sha256 = n.narHash; - } - ) { } - ); -in -nixpkgs.callPackage ./home-manager { } From b0d7d25a4646f6916caf02e0388dc341edc5b9dc Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Wed, 18 Feb 2026 10:15:58 +0000 Subject: [PATCH 62/83] i3status-rust: remove thiagokokada from maintainers --- modules/programs/i3status-rust.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/programs/i3status-rust.nix b/modules/programs/i3status-rust.nix index bb45ac41..93e2ab9f 100644 --- a/modules/programs/i3status-rust.nix +++ b/modules/programs/i3status-rust.nix @@ -14,7 +14,6 @@ let in { meta.maintainers = with lib.maintainers; [ - thiagokokada workflow ]; From bfc89f193668d479898fe8cca8db92a7a731e0ab Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Wed, 18 Feb 2026 10:16:53 +0000 Subject: [PATCH 63/83] nixpkgs-disabled: remove thiagokokada from maintainers --- modules/misc/nixpkgs-disabled.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/misc/nixpkgs-disabled.nix b/modules/misc/nixpkgs-disabled.nix index daf51c3a..628c2ee0 100644 --- a/modules/misc/nixpkgs-disabled.nix +++ b/modules/misc/nixpkgs-disabled.nix @@ -59,7 +59,7 @@ let in { - meta.maintainers = with lib.maintainers; [ thiagokokada ]; + meta.maintainers = [ ]; options.nixpkgs = { config = lib.mkOption { From f71fa310cfc4026dd9dc3c4a9b1d0a5f0b2c1479 Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Wed, 18 Feb 2026 10:17:35 +0000 Subject: [PATCH 64/83] hexchat: remove thiagokokada from maintainers --- modules/programs/hexchat.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/hexchat.nix b/modules/programs/hexchat.nix index 590759a4..437b1f94 100644 --- a/modules/programs/hexchat.nix +++ b/modules/programs/hexchat.nix @@ -239,7 +239,7 @@ let in { - meta.maintainers = with lib.maintainers; [ thiagokokada ]; + meta.maintainers = [ ]; options.programs.hexchat = { enable = lib.mkEnableOption "HexChat, a graphical IRC client"; From 671ba1f70e242a16c84e91b866b391532aeed6c7 Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Wed, 18 Feb 2026 10:18:01 +0000 Subject: [PATCH 65/83] mpv: remove thiagokokada from maintainers --- modules/programs/mpv.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/programs/mpv.nix b/modules/programs/mpv.nix index 2438163c..ad356b96 100644 --- a/modules/programs/mpv.nix +++ b/modules/programs/mpv.nix @@ -67,7 +67,6 @@ let in { meta.maintainers = with lib.maintainers; [ - thiagokokada chuangzhu ]; From 30f54c7014a19da60aa34c1595c1e249c3d0cb04 Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Wed, 18 Feb 2026 10:18:26 +0000 Subject: [PATCH 66/83] qt: remove thiagokokada from maintainers --- modules/misc/qt.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/misc/qt.nix b/modules/misc/qt.nix index d83311b2..71e728d8 100644 --- a/modules/misc/qt.nix +++ b/modules/misc/qt.nix @@ -92,7 +92,6 @@ in { meta.maintainers = with lib.maintainers; [ rycee - thiagokokada ]; imports = [ From 4ebb171bc540720756eba6070432b4ae8b9eb84b Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Wed, 18 Feb 2026 10:19:03 +0000 Subject: [PATCH 67/83] nnn: remove thiagokokada from maintainers --- modules/programs/nnn.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/nnn.nix b/modules/programs/nnn.nix index 57434bf0..457bcd79 100644 --- a/modules/programs/nnn.nix +++ b/modules/programs/nnn.nix @@ -49,7 +49,7 @@ let }; in { - meta.maintainers = with lib.maintainers; [ thiagokokada ]; + meta.maintainers = [ ]; options = { programs.nnn = { From 77c47a454236cede268990eb3e457f062014f414 Mon Sep 17 00:00:00 2001 From: carschandler <92899389+carschandler@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:05:52 -0600 Subject: [PATCH 68/83] carapace: `ignoreCase` option for case-insensitive completion Add option for case-insensitive matching in carapace completions. --- modules/programs/carapace.nix | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/modules/programs/carapace.nix b/modules/programs/carapace.nix index fefa72cc..7e0556f7 100644 --- a/modules/programs/carapace.nix +++ b/modules/programs/carapace.nix @@ -27,9 +27,32 @@ in enableNushellIntegration = lib.hm.shell.mkNushellIntegrationOption { inherit config; }; enableZshIntegration = lib.hm.shell.mkZshIntegrationOption { inherit config; }; + + ignoreCase = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to enable case-insensitive matching for carapace completions. + When enabled, the carapace binary is wrapped with {env}`CARAPACE_MATCH` + set to `1`. + ''; + }; }; config = lib.mkIf cfg.enable { + programs.carapace.package = lib.mkIf cfg.ignoreCase ( + pkgs.symlinkJoin { + name = "carapace-wrapped"; + paths = [ pkgs.carapace ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/carapace \ + --set CARAPACE_MATCH 1 + ''; + meta.mainProgram = "carapace"; + } + ); + home.packages = [ cfg.package ]; programs = { From b3ccd4bb262f4e6d3248b46cede92b90c4a42094 Mon Sep 17 00:00:00 2001 From: MrTipson Date: Fri, 16 Jan 2026 23:22:26 +0100 Subject: [PATCH 69/83] fish: source event handling functions on shell init Functions that contain event handler switches should be sourced during init, otherwise they become active only after being called manually. --- modules/programs/fish.nix | 17 ++++++++ tests/modules/programs/fish/default.nix | 1 + .../modules/programs/fish/source-handlers.nix | 42 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/modules/programs/fish/source-handlers.nix diff --git a/modules/programs/fish.nix b/modules/programs/fish.nix index 57ada99c..0e5176bc 100644 --- a/modules/programs/fish.nix +++ b/modules/programs/fish.nix @@ -397,6 +397,20 @@ let echo "end" echo "setup_hm_session_vars") > $out/${sessionVarsFile} ''; + sourceHandlersStr = + let + handlerAttrs = [ + "onJobExit" + "onProcessExit" + "onVariable" + "onSignal" + "onEvent" + ]; + isHandler = name: def: isAttrs def && builtins.any (attr: builtins.hasAttr attr def) handlerAttrs; + handlerFunctions = lib.filterAttrs isHandler cfg.functions; + sourceFunction = name: def: "source ${config.xdg.configHome}/fish/functions/${name}.fish"; + in + builtins.concatStringsSep "\n" (lib.mapAttrsToList sourceFunction handlerFunctions); in { @@ -726,6 +740,9 @@ in source ${cfg.sessionVariablesPackage}/${sessionVarsFile} + # Source handler functions + ${sourceHandlersStr} + ${cfg.shellInit} status is-login; and begin diff --git a/tests/modules/programs/fish/default.nix b/tests/modules/programs/fish/default.nix index 1e7353a0..0b562fc1 100644 --- a/tests/modules/programs/fish/default.nix +++ b/tests/modules/programs/fish/default.nix @@ -4,6 +4,7 @@ fish-functions = ./functions.nix; fish-completions = ./completions.nix; fish-no-functions = ./no-functions.nix; + fish-source-handlers = ./source-handlers.nix; fish-plugins = ./plugins.nix; fish-manpage = ./manpage.nix; fish-binds = ./binds.nix; diff --git a/tests/modules/programs/fish/source-handlers.nix b/tests/modules/programs/fish/source-handlers.nix new file mode 100644 index 00000000..f9690214 --- /dev/null +++ b/tests/modules/programs/fish/source-handlers.nix @@ -0,0 +1,42 @@ +{ ... }: +{ + config = { + programs.fish = { + enable = true; + + functions = { + normal-function = ""; + event-handler = { + body = ""; + onEvent = "test"; + }; + variable-handler = { + body = ""; + onVariable = "test"; + }; + job-handler = { + body = ""; + onJobExit = "10"; + }; + signal-handler = { + body = ""; + onSignal = "10"; + }; + process-handler = { + body = ""; + onProcessExit = "10"; + }; + }; + }; + + nmt.script = '' + assertFileExists home-files/.config/fish/config.fish + assertFileContains home-files/.config/fish/config.fish "source /home/hm-user/.config/fish/functions/event-handler.fish" + assertFileContains home-files/.config/fish/config.fish "source /home/hm-user/.config/fish/functions/variable-handler.fish" + assertFileContains home-files/.config/fish/config.fish "source /home/hm-user/.config/fish/functions/job-handler.fish" + assertFileContains home-files/.config/fish/config.fish "source /home/hm-user/.config/fish/functions/signal-handler.fish" + assertFileContains home-files/.config/fish/config.fish "source /home/hm-user/.config/fish/functions/process-handler.fish" + assertFileNotRegex home-files/.config/fish/config.fish "source /home/hm-user/.config/fish/functions/normal-function.fish" + ''; + }; +} From f1ebddedab2ec72eff67f5f42515a5925f5f5b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ana=20=E6=B1=9F?= Date: Wed, 18 Feb 2026 20:59:06 +0100 Subject: [PATCH 70/83] tmux: fix double press prefix passthrough --- modules/programs/tmux.nix | 2 +- tests/modules/programs/tmux/prefix.conf | 2 +- tests/modules/programs/tmux/shortcut-without-prefix.conf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/programs/tmux.nix b/modules/programs/tmux.nix index 15fd703c..d669873e 100644 --- a/modules/programs/tmux.nix +++ b/modules/programs/tmux.nix @@ -92,7 +92,7 @@ let # rebind main key: ${prefix} unbind ${defaultPrefix} set -g prefix ${prefix} - bind -n -N "Send the prefix key through to the application" \ + bind -N "Send the prefix key through to the application" \ ${prefix} send-prefix '' } diff --git a/tests/modules/programs/tmux/prefix.conf b/tests/modules/programs/tmux/prefix.conf index eee6ff9e..cf7bfcdf 100644 --- a/tests/modules/programs/tmux/prefix.conf +++ b/tests/modules/programs/tmux/prefix.conf @@ -15,7 +15,7 @@ set -g mode-keys emacs # rebind main key: C-a unbind C-b set -g prefix C-a -bind -n -N "Send the prefix key through to the application" \ +bind -N "Send the prefix key through to the application" \ C-a send-prefix diff --git a/tests/modules/programs/tmux/shortcut-without-prefix.conf b/tests/modules/programs/tmux/shortcut-without-prefix.conf index eee6ff9e..cf7bfcdf 100644 --- a/tests/modules/programs/tmux/shortcut-without-prefix.conf +++ b/tests/modules/programs/tmux/shortcut-without-prefix.conf @@ -15,7 +15,7 @@ set -g mode-keys emacs # rebind main key: C-a unbind C-b set -g prefix C-a -bind -n -N "Send the prefix key through to the application" \ +bind -N "Send the prefix key through to the application" \ C-a send-prefix From 8e0f4cdeee4913f12a310be3a5a978592a563ec1 Mon Sep 17 00:00:00 2001 From: teto <886074+teto@users.noreply.github.com> Date: Sat, 3 Jan 2026 22:48:19 +0100 Subject: [PATCH 71/83] tests: merge both flakes we used to have a flake in tests/flake.nix on top of the top-level one. This results in 2 lockfiles for the same purpose and is not practical. Instead this moves tests to legacyPackages: - so they dont appear in "nix flake show" as it can be inconvenient - legacyPackages supported nested attribute sets of derivations which could let us regroup tests under a same set and build that instead. For instance, one could `nix build .#tests.neovim` and build all of neovim packages. One could still pick a single `nix build .#tests.neovim.plugin-config` dont expose tests into devShells to limit fix python test runner to find the tests where appropriate --- flake.nix | 136 ++++++++++++++++++++++++++++++++++++++ tests/flake.nix | 169 ------------------------------------------------ tests/tests.py | 3 +- 3 files changed, 137 insertions(+), 171 deletions(-) delete mode 100644 tests/flake.nix diff --git a/flake.nix b/flake.nix index 30d8d778..9a2c58c3 100644 --- a/flake.nix +++ b/flake.nix @@ -48,6 +48,113 @@ forAllPkgs = f: nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed (system: f nixpkgs.legacyPackages.${system}); + + forCI = nixpkgs.lib.genAttrs [ + "aarch64-darwin" + "x86_64-linux" + ]; + + testChunks = + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + + # Create chunked test packages for better CI parallelization + tests = import ./tests { + inherit pkgs; + # Disable big tests since this is only used for CI + enableBig = false; + }; + allTests = lib.attrNames tests.build; + # Remove 'all' from the test list as it's a meta-package + filteredTests = lib.filter (name: name != "all") allTests; + # NOTE: Just a starting value, we can tweak this to find a good value. + targetTestsPerChunk = 50; + numChunks = lib.max 1 ( + builtins.ceil ((builtins.length filteredTests) / (targetTestsPerChunk * 1.0)) + ); + chunkSize = builtins.ceil ((builtins.length filteredTests) / (numChunks * 1.0)); + + makeChunk = + chunkNum: testList: + let + start = (chunkNum - 1) * chunkSize; + end = lib.min (start + chunkSize) (builtins.length testList); + chunkTests = lib.sublist start (end - start) testList; + chunkAttrs = lib.genAttrs chunkTests (name: tests.build.${name}); + in + pkgs.symlinkJoin { + name = "test-chunk-${toString chunkNum}"; + paths = lib.attrValues chunkAttrs; + passthru.tests = chunkTests; + }; + in + lib.listToAttrs ( + lib.genList ( + i: lib.nameValuePair "test-chunk-${toString (i + 1)}" (makeChunk (i + 1) filteredTests) + ) numChunks + ); + + integrationTests = + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + in + lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux ( + let + tests = import ./tests/integration { inherit pkgs lib; }; + renameTestPkg = n: v: lib.nameValuePair "integration-${n}" v; + in + lib.mapAttrs' renameTestPkg (lib.removeAttrs tests [ "all" ]) + ); + + buildTests = + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + tests = import ./tests { inherit pkgs; }; + renameTestPkg = n: nixpkgs.lib.nameValuePair "test-${n}"; + in + nixpkgs.lib.mapAttrs' renameTestPkg tests.build; + + buildTestsNoBig = + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + tests = import ./tests { + inherit pkgs; + enableBig = false; + }; + in + { + test-all-enableBig-false-enableLegacyIfd-false = tests.build.all; + }; + + buildTestsNoBigIfd = + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + tests = import ./tests { + inherit pkgs; + enableBig = false; + enableLegacyIfd = true; + }; + in + { + test-all-enableBig-false-enableLegacyIfd-true = tests.build.all; + }; + + integrationTestPackages = + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + tests = import ./tests/integration { inherit pkgs lib; }; + renameTestPkg = n: lib.nameValuePair "integration-test-${n}"; + in + lib.mapAttrs' renameTestPkg tests; in { formatter = forAllPkgs ( @@ -63,6 +170,22 @@ } ); + # TODO: increase buildbot testing scope + buildbot = forCI ( + system: + let + allIntegrationTests = integrationTests system; + workingIntegrationTests = nixpkgs.lib.filterAttrs ( + name: _: + nixpkgs.lib.elem name [ + "integration-nixos-basics" + "integration-nixos-legacy-profile-management" + ] + ) allIntegrationTests; + in + (testChunks system) // workingIntegrationTests + ); + packages = forAllPkgs ( pkgs: let @@ -90,6 +213,19 @@ docs-manpages = docs.manPages; } ); + + legacyPackages = forAllPkgs ( + pkgs: + let + system = pkgs.stdenv.hostPlatform.system; + in + (buildTests system) + // (integrationTestPackages system) + // (buildTestsNoBig system) + // (buildTestsNoBigIfd system) + // (testChunks system) + // (integrationTests system) + ); } ); } diff --git a/tests/flake.nix b/tests/flake.nix deleted file mode 100644 index 64f53e30..00000000 --- a/tests/flake.nix +++ /dev/null @@ -1,169 +0,0 @@ -# This is an internal Nix Flake intended for use when running tests. -# -# You can build all tests or specific tests by running -# -# nix build --reference-lock-file flake.lock ./tests#test-all -# nix build --reference-lock-file flake.lock ./tests#test-alacritty-empty-settings -# -# in the Home Manager project root directory. -# -# Similarly for integration tests -# -# nix build --reference-lock-file flake.lock ./tests#integration-test-all -# nix build --reference-lock-file flake.lock ./tests#integration-test-standalone-standard-basics - -{ - description = "Tests of Home Manager for Nix"; - - inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - - outputs = - { nixpkgs, ... }: - let - forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed; - forCI = nixpkgs.lib.genAttrs [ - "aarch64-darwin" - "x86_64-linux" - ]; - - testChunks = - system: - let - pkgs = nixpkgs.legacyPackages.${system}; - inherit (pkgs) lib; - - # Create chunked test packages for better CI parallelization - tests = import ./. { - inherit pkgs; - # Disable big tests since this is only used for CI - enableBig = false; - }; - allTests = lib.attrNames tests.build; - # Remove 'all' from the test list as it's a meta-package - filteredTests = lib.filter (name: name != "all") allTests; - # NOTE: Just a starting value, we can tweak this to find a good value. - targetTestsPerChunk = 50; - numChunks = lib.max 1 ( - builtins.ceil ((builtins.length filteredTests) / (targetTestsPerChunk * 1.0)) - ); - chunkSize = builtins.ceil ((builtins.length filteredTests) / (numChunks * 1.0)); - - makeChunk = - chunkNum: testList: - let - start = (chunkNum - 1) * chunkSize; - end = lib.min (start + chunkSize) (builtins.length testList); - chunkTests = lib.sublist start (end - start) testList; - chunkAttrs = lib.genAttrs chunkTests (name: tests.build.${name}); - in - pkgs.symlinkJoin { - name = "test-chunk-${toString chunkNum}"; - paths = lib.attrValues chunkAttrs; - passthru.tests = chunkTests; - }; - in - lib.listToAttrs ( - lib.genList ( - i: lib.nameValuePair "test-chunk-${toString (i + 1)}" (makeChunk (i + 1) filteredTests) - ) numChunks - ); - - integrationTests = - system: - let - pkgs = nixpkgs.legacyPackages.${system}; - inherit (pkgs) lib; - in - lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux ( - let - tests = import ./integration { inherit pkgs lib; }; - renameTestPkg = n: v: lib.nameValuePair "integration-${n}" v; - in - lib.mapAttrs' renameTestPkg (lib.removeAttrs tests [ "all" ]) - ); - - # Test group definitions - buildTests = - system: - let - pkgs = nixpkgs.legacyPackages.${system}; - tests = import ./. { inherit pkgs; }; - renameTestPkg = n: nixpkgs.lib.nameValuePair "test-${n}"; - in - nixpkgs.lib.mapAttrs' renameTestPkg tests.build; - - buildTestsNoBig = - system: - let - pkgs = nixpkgs.legacyPackages.${system}; - tests = import ./. { - inherit pkgs; - enableBig = false; - }; - in - { - test-all-enableBig-false-enableLegacyIfd-false = tests.build.all; - }; - - buildTestsNoBigIfd = - system: - let - pkgs = nixpkgs.legacyPackages.${system}; - tests = import ./. { - inherit pkgs; - enableBig = false; - enableLegacyIfd = true; - }; - in - { - test-all-enableBig-false-enableLegacyIfd-true = tests.build.all; - }; - - integrationTestPackages = - system: - let - pkgs = nixpkgs.legacyPackages.${system}; - inherit (pkgs) lib; - tests = import ./integration { inherit pkgs lib; }; - renameTestPkg = n: lib.nameValuePair "integration-test-${n}"; - in - lib.mapAttrs' renameTestPkg tests; - - in - { - # TODO: increase buildbot testing scope - buildbot = forCI ( - system: - let - allIntegrationTests = integrationTests system; - workingIntegrationTests = nixpkgs.lib.filterAttrs ( - name: _: - nixpkgs.lib.elem name [ - "integration-nixos-basics" - "integration-nixos-legacy-profile-management" - ] - ) allIntegrationTests; - in - (testChunks system) // workingIntegrationTests - ); - - devShells = forAllSystems ( - system: - let - pkgs = nixpkgs.legacyPackages.${system}; - tests = import ./. { inherit pkgs; }; - in - tests.run - ); - - packages = forAllSystems ( - system: - (buildTests system) - // (integrationTestPackages system) - // (buildTestsNoBig system) - // (buildTestsNoBigIfd system) - // (testChunks system) - // (integrationTests system) - ); - }; -} diff --git a/tests/tests.py b/tests/tests.py index ffc12555..b68c6d80 100755 --- a/tests/tests.py +++ b/tests/tests.py @@ -64,8 +64,7 @@ class TestRunner: ) cmd = [ - "nix", "eval", "--raw", "--reference-lock-file", "flake.lock", - f"./tests#packages.{system}", "--apply", nix_apply_expr + "nix", "eval", "--raw", f".#legacyPackages.{system}", "--apply", nix_apply_expr ] result = _run_command(cmd, cwd=self.repo_root) From 0f156f177da97604a9a91f4875a2ced7ba2941c5 Mon Sep 17 00:00:00 2001 From: teto <886074+teto@users.noreply.github.com> Date: Tue, 17 Feb 2026 23:40:37 +0100 Subject: [PATCH 72/83] doc: updated instructions with tests being moved around no need to set flake now --- docs/manual/contributing/tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual/contributing/tests.md b/docs/manual/contributing/tests.md index c4793101..5a1514cd 100644 --- a/docs/manual/contributing/tests.md +++ b/docs/manual/contributing/tests.md @@ -217,13 +217,13 @@ and may cause failures. To run against the Nixpkgs from the `flake.lock` file, use instead e.g. ``` shell -$ nix build --reference-lock-file flake.lock --option allow-import-from-derivation false ./tests#test-all +$ nix build --option allow-import-from-derivation false .#test-all ``` or ``` shell -$ nix build --reference-lock-file flake.lock --option allow-import-from-derivation false ./tests#test-alacritty-empty-settings +$ nix build --option allow-import-from-derivation false .#test-alacritty-empty-settings ``` Some tests may be marked with `enableLegacyIfd`, those may be run by run with e.g. From f19a99503cedfc21db747b65802daec8ca0d9505 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 18 Feb 2026 21:17:10 -0600 Subject: [PATCH 73/83] ci: update buildbot test config After https://github.com/nix-community/home-manager/pull/8464 we need to update this to not try another flake. Signed-off-by: Austin Horstman --- buildbot-nix.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/buildbot-nix.toml b/buildbot-nix.toml index cef36aac..2f7c5cbc 100644 --- a/buildbot-nix.toml +++ b/buildbot-nix.toml @@ -1,3 +1 @@ attribute = "buildbot" -flake_dir = "./tests" -lock_file = "./flake.lock" From 2dedeb55b2c140d9a123ae931588e8903fe202ef Mon Sep 17 00:00:00 2001 From: Vinicius Deolindo Date: Wed, 11 Feb 2026 12:09:58 -0300 Subject: [PATCH 74/83] man: make `package` nullable and default to `null` on darwin --- docs/release-notes/rl-2605.md | 6 ++++++ modules/programs/man.nix | 21 ++++++++++++++++--- tests/modules/programs/man/default.nix | 6 ++++++ .../man/no-caches-without-package.nix | 19 +++++++++++++++++ .../programs/man/no-package-on-darwin.nix | 12 +++++++++++ 5 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 tests/modules/programs/man/no-caches-without-package.nix create mode 100644 tests/modules/programs/man/no-package-on-darwin.nix diff --git a/docs/release-notes/rl-2605.md b/docs/release-notes/rl-2605.md index 848802dd..020e95f5 100644 --- a/docs/release-notes/rl-2605.md +++ b/docs/release-notes/rl-2605.md @@ -36,3 +36,9 @@ changes are only active if the `home.stateVersion` option is set to you had the key `XDG_DESKTOP_DIR` before, you should now use the key `DESKTOP`. Home Manager 26.05 introduced a warning when the `XDG__DIR` form is used. + +- The [](#opt-programs.man.package) option now defaults to `null` on + Darwin because the GNU `man` from nixpkgs ships `apropos`/`man -k` + and `whatis`/`man -f` binaries that don't work on Darwin. Nix-installed + manual pages still work with macOS's built-in `man` via + [](#opt-home.extraOutputsToInstall). diff --git a/modules/programs/man.nix b/modules/programs/man.nix index 3b41eb34..dc062e39 100644 --- a/modules/programs/man.nix +++ b/modules/programs/man.nix @@ -21,7 +21,18 @@ in ''; }; - package = lib.mkPackageOption pkgs "man" { }; + package = mkOption { + type = with types; nullOr package; + default = + if pkgs.stdenv.isDarwin && lib.versionAtLeast config.home.stateVersion "26.05" then + null + else + pkgs.man; + defaultText = lib.literalExpression '' + if pkgs.stdenv.isDarwin && lib.versionAtLeast config.home.stateVersion "26.05" then null else pkgs.man + ''; + description = "The {command}`man` package to use."; + }; extraConfig = mkOption { type = types.lines; @@ -51,11 +62,15 @@ in }; config = lib.mkIf cfg.enable { - home.packages = [ cfg.package ]; + warnings = lib.optional ( + cfg.generateCaches && cfg.package == null + ) "programs.man.generateCaches has no effect when programs.man.package is null"; + + home.packages = lib.optional (cfg.package != null) cfg.package; home.extraOutputsToInstall = [ "man" ]; # This is mostly copy/pasted/adapted from NixOS' documentation.nix. - home.file = lib.mkIf cfg.generateCaches { + home.file = lib.mkIf (cfg.generateCaches && cfg.package != null) { ".manpath".text = let # Generate a directory containing installed packages' manpages. diff --git a/tests/modules/programs/man/default.nix b/tests/modules/programs/man/default.nix index 2e9d340f..9b066db7 100644 --- a/tests/modules/programs/man/default.nix +++ b/tests/modules/programs/man/default.nix @@ -1,4 +1,10 @@ +{ lib, pkgs, ... }: + { man-apropos = ./apropos.nix; man-no-manpath = ./no-manpath.nix; + man-no-caches-without-package = ./no-caches-without-package.nix; +} +// lib.optionalAttrs pkgs.stdenv.hostPlatform.isDarwin { + man-no-package-on-darwin = ./no-package-on-darwin.nix; } diff --git a/tests/modules/programs/man/no-caches-without-package.nix b/tests/modules/programs/man/no-caches-without-package.nix new file mode 100644 index 00000000..6ec8b4a7 --- /dev/null +++ b/tests/modules/programs/man/no-caches-without-package.nix @@ -0,0 +1,19 @@ +{ + config = { + home.stateVersion = "26.05"; + + programs.man = { + enable = true; + package = null; + generateCaches = true; + }; + + test.asserts.warnings.expected = [ + "programs.man.generateCaches has no effect when programs.man.package is null" + ]; + + nmt.script = '' + assertPathNotExists home-files/.manpath + ''; + }; +} diff --git a/tests/modules/programs/man/no-package-on-darwin.nix b/tests/modules/programs/man/no-package-on-darwin.nix new file mode 100644 index 00000000..7ec15a0e --- /dev/null +++ b/tests/modules/programs/man/no-package-on-darwin.nix @@ -0,0 +1,12 @@ +{ + config = { + home.stateVersion = "26.05"; + + programs.man.enable = true; + + nmt.script = '' + assertPathNotExists home-path/bin/man + assertPathNotExists home-files/.manpath + ''; + }; +} From a0a01d8811fd5e99e003078ed64a0e7b531545dd Mon Sep 17 00:00:00 2001 From: Ayush Pramanik Date: Tue, 17 Feb 2026 13:44:33 -0400 Subject: [PATCH 75/83] sioyek: add `startupCommands` option --- modules/programs/sioyek.nix | 45 ++++++++++++++++--- .../sioyek/sioyek-basic-configuration.nix | 5 +++ .../programs/sioyek/test_prefs_user.config | 1 + 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/modules/programs/sioyek.nix b/modules/programs/sioyek.nix index 0d16f41d..566a1520 100644 --- a/modules/programs/sioyek.nix +++ b/modules/programs/sioyek.nix @@ -18,7 +18,6 @@ let mkKeyValue = key: value: "${key} ${value}"; listsAsDuplicateKeys = true; }; - in { meta.maintainers = [ lib.maintainers.podocarp ]; @@ -58,24 +57,58 @@ in {file}`$XDG_CONFIG_HOME/sioyek/prefs_user.config`. See . ''; - type = types.attrsOf types.str; default = { }; example = literalExpression '' { "background_color" = "1.0 1.0 1.0"; "text_highlight_color" = "1.0 0.0 0.0"; + startup_commands = [ + "toggle_visual_scroll" + "toggle_dark_mode" + ]; } ''; - }; + type = types.submodule { + freeformType = types.attrsOf types.str; + options.startup_commands = mkOption { + description = '' + Commands to be run upon startup. Will be written to {file}`$XDG_CONFIG_HOME/sioyek/prefs_user.config`. + See . + ''; + type = types.either types.str (types.listOf types.str); + apply = + x: + lib.warnIf (lib.isString x) + "`programs.sioyek.config.startup_commands` should now be a list of strings instead of a string." + x; + default = [ ]; + example = [ + "toggle_visual_scroll" + "toggle_dark_mode" + ]; + }; + }; + }; }; }; config = mkIf cfg.enable ( + let + prefsCfg = + cfg.config + // + lib.optionalAttrs (cfg.config.startup_commands != [ ] && !lib.isString cfg.config.startup_commands) + { + startup_commands = lib.concatStringsSep ";" cfg.config.startup_commands; + }; + in lib.mkMerge [ - { home.packages = [ cfg.package ]; } - (mkIf (cfg.config != { }) { - xdg.configFile."sioyek/prefs_user.config".text = renderConfig cfg.config; + { + home.packages = [ cfg.package ]; + } + (mkIf (prefsCfg != { }) { + xdg.configFile."sioyek/prefs_user.config".text = renderConfig prefsCfg; }) (mkIf (cfg.bindings != { }) { xdg.configFile."sioyek/keys_user.config".text = renderConfig cfg.bindings; diff --git a/tests/modules/programs/sioyek/sioyek-basic-configuration.nix b/tests/modules/programs/sioyek/sioyek-basic-configuration.nix index 8e884d5c..2e2d0619 100644 --- a/tests/modules/programs/sioyek/sioyek-basic-configuration.nix +++ b/tests/modules/programs/sioyek/sioyek-basic-configuration.nix @@ -18,6 +18,11 @@ config = { "dark_mode_background_color" = "0.0 0.0 0.0"; "dark_mode_contrast" = "0.8"; + + startup_commands = [ + "toggle_visual_scroll" + "toggle_dark_mode" + ]; }; }; diff --git a/tests/modules/programs/sioyek/test_prefs_user.config b/tests/modules/programs/sioyek/test_prefs_user.config index 730dd51b..02a30419 100644 --- a/tests/modules/programs/sioyek/test_prefs_user.config +++ b/tests/modules/programs/sioyek/test_prefs_user.config @@ -1,2 +1,3 @@ dark_mode_background_color 0.0 0.0 0.0 dark_mode_contrast 0.8 +startup_commands toggle_visual_scroll;toggle_dark_mode From 167e0b6837115e672ec5f58e2b0ea39093abe807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ana=20=E6=B1=9F?= Date: Tue, 17 Feb 2026 22:33:26 +0100 Subject: [PATCH 76/83] zsh: set nix-zsh-completions as lowPrio to avoid subpath conflict --- modules/programs/zsh/default.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/programs/zsh/default.nix b/modules/programs/zsh/default.nix index e7f209e8..c14a71c2 100644 --- a/modules/programs/zsh/default.nix +++ b/modules/programs/zsh/default.nix @@ -478,7 +478,10 @@ in } { - home.packages = [ cfg.package ] ++ lib.optional cfg.enableCompletion pkgs.nix-zsh-completions; + home.packages = [ + cfg.package + ] + ++ lib.optional cfg.enableCompletion (lib.lowPrio pkgs.nix-zsh-completions); # NOTE: Always include "main" highlighter with normal priority. # Option default priority will cause `main` to get dropped by customization. From 91be7cce763fa4022c7cf025a71b0c366d1b6e77 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 18 Feb 2026 20:51:14 -0600 Subject: [PATCH 77/83] claude-code: fix skills implementation Claude skills are only found when using `SKILL.md` entrypoints. Attribute names should be used for the directory structure, not the filename. Signed-off-by: Austin Horstman --- modules/programs/claude-code.nix | 15 ++++++++------- .../programs/claude-code/mixed-content.nix | 6 +++--- .../programs/claude-code/skill-subdir/SKILL.md | 12 ++++++++++++ tests/modules/programs/claude-code/skills-dir.nix | 8 ++++---- .../modules/programs/claude-code/skills-path.nix | 4 ++-- .../programs/claude-code/skills-subdir.nix | 5 +++++ .../skills/{test-skill.md => test-skill/SKILL.md} | 0 7 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 tests/modules/programs/claude-code/skill-subdir/SKILL.md rename tests/modules/programs/claude-code/skills/{test-skill.md => test-skill/SKILL.md} (100%) diff --git a/modules/programs/claude-code.nix b/modules/programs/claude-code.nix index dfc0fc55..16387e67 100644 --- a/modules/programs/claude-code.nix +++ b/modules/programs/claude-code.nix @@ -298,14 +298,14 @@ in default = { }; description = '' Custom skills for Claude Code. - The attribute name becomes the skill filename or directory name, and the value is either: - - Inline content as a string (creates .claude/skills/.md) - - A path to a file (creates .claude/skills/.md) + The attribute name becomes the skill directory name, and the value is either: + - Inline content as a string (creates .claude/skills//SKILL.md) + - A path to a file (creates .claude/skills//SKILL.md) - A path to a directory (creates .claude/skills// with all files) ''; example = lib.literalExpression '' { - xlsx = ./skills/xlsx.md; + xlsx = ./skills/xlsx/SKILL.md; data-analysis = ./skills/data-analysis; pdf-processing = ''' --- @@ -334,8 +334,9 @@ in type = lib.types.nullOr lib.types.path; default = null; description = '' - Path to a directory containing skill files for Claude Code. - Skill files from this directory will be symlinked to .claude/skills/. + Path to a directory containing skill directories for Claude Code. + Each skill directory should contain a SKILL.md entrypoint file. + Skill directories from this path will be symlinked to .claude/skills/. ''; example = lib.literalExpression "./skills"; }; @@ -516,7 +517,7 @@ in recursive = true; } else - lib.nameValuePair ".claude/skills/${name}.md" ( + lib.nameValuePair ".claude/skills/${name}/SKILL.md" ( if lib.isPath content then { source = content; } else { text = content; } ) ) cfg.skills; diff --git a/tests/modules/programs/claude-code/mixed-content.nix b/tests/modules/programs/claude-code/mixed-content.nix index 74864200..67c6ef79 100644 --- a/tests/modules/programs/claude-code/mixed-content.nix +++ b/tests/modules/programs/claude-code/mixed-content.nix @@ -38,14 +38,14 @@ assertFileExists home-files/.claude/commands/path-command.md assertFileExists home-files/.claude/agents/inline-agent.md assertFileExists home-files/.claude/agents/path-agent.md - assertFileExists home-files/.claude/skills/inline-skill.md - assertFileExists home-files/.claude/skills/path-skill.md + assertFileExists home-files/.claude/skills/inline-skill/SKILL.md + assertFileExists home-files/.claude/skills/path-skill/SKILL.md assertFileContent home-files/.claude/commands/path-command.md \ ${./test-command.md} assertFileContent home-files/.claude/agents/path-agent.md \ ${./test-agent.md} - assertFileContent home-files/.claude/skills/path-skill.md \ + assertFileContent home-files/.claude/skills/path-skill/SKILL.md \ ${./test-skill.md} ''; } diff --git a/tests/modules/programs/claude-code/skill-subdir/SKILL.md b/tests/modules/programs/claude-code/skill-subdir/SKILL.md new file mode 100644 index 00000000..6f154df6 --- /dev/null +++ b/tests/modules/programs/claude-code/skill-subdir/SKILL.md @@ -0,0 +1,12 @@ +--- +name: data-processing +description: Process and transform structured data files. +--- + +# Data Processing Skill + +Use this skill for data-processing tasks. + +## Supporting files +- `extract.md`: extraction workflow details +- `convert.md`: conversion workflow details diff --git a/tests/modules/programs/claude-code/skills-dir.nix b/tests/modules/programs/claude-code/skills-dir.nix index 4f15a558..0a43e387 100644 --- a/tests/modules/programs/claude-code/skills-dir.nix +++ b/tests/modules/programs/claude-code/skills-dir.nix @@ -5,10 +5,10 @@ }; nmt.script = '' - assertFileExists home-files/.claude/skills/test-skill.md - assertLinkExists home-files/.claude/skills/test-skill.md + assertFileExists home-files/.claude/skills/test-skill/SKILL.md + assertLinkExists home-files/.claude/skills/test-skill/SKILL.md assertFileContent \ - home-files/.claude/skills/test-skill.md \ - ${./skills/test-skill.md} + home-files/.claude/skills/test-skill/SKILL.md \ + ${./skills/test-skill/SKILL.md} ''; } diff --git a/tests/modules/programs/claude-code/skills-path.nix b/tests/modules/programs/claude-code/skills-path.nix index 374e97a0..89424647 100644 --- a/tests/modules/programs/claude-code/skills-path.nix +++ b/tests/modules/programs/claude-code/skills-path.nix @@ -7,8 +7,8 @@ }; nmt.script = '' - assertFileExists home-files/.claude/skills/test-skill.md - assertFileContent home-files/.claude/skills/test-skill.md \ + assertFileExists home-files/.claude/skills/test-skill/SKILL.md + assertFileContent home-files/.claude/skills/test-skill/SKILL.md \ ${./test-skill.md} ''; } diff --git a/tests/modules/programs/claude-code/skills-subdir.nix b/tests/modules/programs/claude-code/skills-subdir.nix index 3c7567fd..9b2c31bf 100644 --- a/tests/modules/programs/claude-code/skills-subdir.nix +++ b/tests/modules/programs/claude-code/skills-subdir.nix @@ -7,10 +7,15 @@ }; nmt.script = '' + assertFileExists home-files/.claude/skills/data-processing/SKILL.md assertFileExists home-files/.claude/skills/data-processing/extract.md assertFileExists home-files/.claude/skills/data-processing/convert.md + assertLinkExists home-files/.claude/skills/data-processing/SKILL.md assertLinkExists home-files/.claude/skills/data-processing/extract.md assertLinkExists home-files/.claude/skills/data-processing/convert.md + assertFileContent \ + home-files/.claude/skills/data-processing/SKILL.md \ + ${./skill-subdir/SKILL.md} assertFileContent \ home-files/.claude/skills/data-processing/extract.md \ ${./skill-subdir/extract.md} diff --git a/tests/modules/programs/claude-code/skills/test-skill.md b/tests/modules/programs/claude-code/skills/test-skill/SKILL.md similarity index 100% rename from tests/modules/programs/claude-code/skills/test-skill.md rename to tests/modules/programs/claude-code/skills/test-skill/SKILL.md From 8b6212ebd61e5d672a69bbbc4ed275b3eeedf2eb Mon Sep 17 00:00:00 2001 From: Jasper Chan Date: Fri, 13 Feb 2026 17:50:50 -0800 Subject: [PATCH 78/83] nnn: add cd on quit option --- modules/programs/nnn.nix | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/modules/programs/nnn.nix b/modules/programs/nnn.nix index 457bcd79..5032c2ab 100644 --- a/modules/programs/nnn.nix +++ b/modules/programs/nnn.nix @@ -100,6 +100,14 @@ in ''; default = { }; }; + + enableBashIntegration = lib.hm.shell.mkBashIntegrationOption { inherit config; }; + + enableFishIntegration = lib.hm.shell.mkFishIntegrationOption { inherit config; }; + + enableZshIntegration = lib.hm.shell.mkZshIntegrationOption { inherit config; }; + + quitcd = lib.mkEnableOption "cd on quit"; }; }; @@ -116,10 +124,25 @@ in --prefix NNN_PLUG : "${renderSettings cfg.plugins.mappings}" ''; }); + + quitcd = { + bash_sh_zsh = "source ${nnnPackage}/share/quitcd/quitcd.bash_sh_zsh"; + fish = "source ${nnnPackage}/share/quitcd/quitcd.fish"; + }; in lib.mkIf cfg.enable { programs.nnn.finalPackage = nnnPackage; home.packages = [ nnnPackage ]; xdg.configFile."nnn/plugins" = lib.mkIf (cfg.plugins.src != null) { source = cfg.plugins.src; }; + + programs.bash.initExtra = lib.mkIf (cfg.enableBashIntegration && cfg.quitcd) ( + lib.mkAfter quitcd.bash_sh_zsh + ); + programs.fish.interactiveShellInit = lib.mkIf (cfg.enableFishIntegration && cfg.quitcd) ( + lib.mkAfter quitcd.fish + ); + programs.zsh.initContent = lib.mkIf (cfg.enableZshIntegration && cfg.quitcd) ( + lib.mkAfter quitcd.bash_sh_zsh + ); }; } From 1dcebb44c60813e4b4d606b471c04eab7a9de47e Mon Sep 17 00:00:00 2001 From: rsahwe Date: Thu, 19 Feb 2026 09:57:47 +0100 Subject: [PATCH 79/83] maintainers: add rsahwe --- modules/lib/maintainers.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/lib/maintainers.nix b/modules/lib/maintainers.nix index a853024f..b1f8842f 100644 --- a/modules/lib/maintainers.nix +++ b/modules/lib/maintainers.nix @@ -494,6 +494,12 @@ github = "Rosuavio"; githubId = 7164552; }; + rsahwe = { + name = "rsahwe"; + email = "rsahwe@gmx.net"; + github = "rsahwe"; + githubId = 201613730; + }; rszamszur = { name = "Radosław Szamszur"; email = "radoslawszamszur@gmail.com"; From 168fbe8891f7ff9ca2a20ad406a339a08df6c642 Mon Sep 17 00:00:00 2001 From: rsahwe Date: Thu, 19 Feb 2026 10:04:56 +0100 Subject: [PATCH 80/83] rizin: add module This adds a basic module for rizin which supports creating a basic rizinrc for configuration. --- .../misc/news/2026/02/2026-02-18_18-20-43.nix | 12 +++++ modules/programs/rizin.nix | 44 +++++++++++++++++++ .../programs/rizin/basic-configuration.nix | 13 ++++++ tests/modules/programs/rizin/default.nix | 4 ++ .../programs/rizin/disabled-configuration.nix | 13 ++++++ 5 files changed, 86 insertions(+) create mode 100644 modules/misc/news/2026/02/2026-02-18_18-20-43.nix create mode 100644 modules/programs/rizin.nix create mode 100644 tests/modules/programs/rizin/basic-configuration.nix create mode 100644 tests/modules/programs/rizin/default.nix create mode 100644 tests/modules/programs/rizin/disabled-configuration.nix diff --git a/modules/misc/news/2026/02/2026-02-18_18-20-43.nix b/modules/misc/news/2026/02/2026-02-18_18-20-43.nix new file mode 100644 index 00000000..60efe6d2 --- /dev/null +++ b/modules/misc/news/2026/02/2026-02-18_18-20-43.nix @@ -0,0 +1,12 @@ +{ + time = "2026-02-18T17:20:43+00:00"; + condition = true; + message = '' + A new module is available: 'programs.rizin'. + + Rizin is a free and open-source reverse engineering framework + that delivers a comprehensive binary analysis experience. + It focuses on usability, stability, and functional features, + striving to create a welcoming environment for developers and users. + ''; +} diff --git a/modules/programs/rizin.nix b/modules/programs/rizin.nix new file mode 100644 index 00000000..43a6afab --- /dev/null +++ b/modules/programs/rizin.nix @@ -0,0 +1,44 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.programs.rizin; +in +{ + meta.maintainers = [ + lib.hm.maintainers.rsahwe + ]; + + options = { + programs.rizin = { + enable = lib.mkEnableOption "Rizin"; + + package = lib.mkPackageOption pkgs "rizin" { nullable = true; }; + + extraConfig = lib.mkOption { + type = lib.types.lines; + default = ""; + example = '' + e asm.bytes=true + e asm.bytes.space=true + ''; + description = '' + Run configuration written to {file}`rizinrc`. + See + for more information. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + + xdg.configFile."rizin/rizinrc" = lib.mkIf (cfg.extraConfig != "") { + text = cfg.extraConfig; + }; + }; +} diff --git a/tests/modules/programs/rizin/basic-configuration.nix b/tests/modules/programs/rizin/basic-configuration.nix new file mode 100644 index 00000000..ca740885 --- /dev/null +++ b/tests/modules/programs/rizin/basic-configuration.nix @@ -0,0 +1,13 @@ +{ + programs.rizin = { + enable = true; + extraConfig = '' + e asm.bytes=true + e asm.bytes.space=true + ''; + }; + + nmt.script = '' + assertFileExists "home-files/.config/rizin/rizinrc" + ''; +} diff --git a/tests/modules/programs/rizin/default.nix b/tests/modules/programs/rizin/default.nix new file mode 100644 index 00000000..6358b458 --- /dev/null +++ b/tests/modules/programs/rizin/default.nix @@ -0,0 +1,4 @@ +{ + rizin-basic-configuration = ./basic-configuration.nix; + rizin-disabled-configuration = ./disabled-configuration.nix; +} diff --git a/tests/modules/programs/rizin/disabled-configuration.nix b/tests/modules/programs/rizin/disabled-configuration.nix new file mode 100644 index 00000000..822d56a8 --- /dev/null +++ b/tests/modules/programs/rizin/disabled-configuration.nix @@ -0,0 +1,13 @@ +{ + programs.rizin = { + enable = false; + extraConfig = '' + e asm.bytes=true + e asm.bytes.space=true + ''; + }; + + nmt.script = '' + assertPathNotExists "home-files/.config/rizin/rizinrc" + ''; +} From c9507a9aa5ed3a448c20be64198ad394d63795f4 Mon Sep 17 00:00:00 2001 From: rsahwe Date: Thu, 19 Feb 2026 22:04:44 +0100 Subject: [PATCH 81/83] rizin: use xdg.enable and home.preferXdgDirectories rizin reads configuration from both $HOME/.rizinrc and from $XDG_CONFIG_HOME/rizin/rizinrc and the module now uses the relevant options for choosing between them. --- modules/programs/rizin.nix | 18 +++++++++++++----- .../programs/rizin/basic-configuration.nix | 2 +- tests/modules/programs/rizin/default.nix | 1 + .../programs/rizin/disabled-configuration.nix | 1 + tests/modules/programs/rizin/prefer-xdg.nix | 17 +++++++++++++++++ 5 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 tests/modules/programs/rizin/prefer-xdg.nix diff --git a/modules/programs/rizin.nix b/modules/programs/rizin.nix index 43a6afab..595876ba 100644 --- a/modules/programs/rizin.nix +++ b/modules/programs/rizin.nix @@ -34,11 +34,19 @@ in }; }; - config = lib.mkIf cfg.enable { - home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + config = + let + configFile = + if config.xdg.enable && config.home.preferXdgDirectories then + "${config.xdg.configHome}/rizin/rizinrc" + else + ".rizinrc"; + in + lib.mkIf cfg.enable { + home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; - xdg.configFile."rizin/rizinrc" = lib.mkIf (cfg.extraConfig != "") { - text = cfg.extraConfig; + home.file.${configFile} = lib.mkIf (cfg.extraConfig != "") { + text = cfg.extraConfig; + }; }; - }; } diff --git a/tests/modules/programs/rizin/basic-configuration.nix b/tests/modules/programs/rizin/basic-configuration.nix index ca740885..80d0a8e8 100644 --- a/tests/modules/programs/rizin/basic-configuration.nix +++ b/tests/modules/programs/rizin/basic-configuration.nix @@ -8,6 +8,6 @@ }; nmt.script = '' - assertFileExists "home-files/.config/rizin/rizinrc" + assertFileExists "home-files/.rizinrc" ''; } diff --git a/tests/modules/programs/rizin/default.nix b/tests/modules/programs/rizin/default.nix index 6358b458..c40fe998 100644 --- a/tests/modules/programs/rizin/default.nix +++ b/tests/modules/programs/rizin/default.nix @@ -1,4 +1,5 @@ { rizin-basic-configuration = ./basic-configuration.nix; rizin-disabled-configuration = ./disabled-configuration.nix; + rizin-prefer-xdg = ./prefer-xdg.nix; } diff --git a/tests/modules/programs/rizin/disabled-configuration.nix b/tests/modules/programs/rizin/disabled-configuration.nix index 822d56a8..149ad957 100644 --- a/tests/modules/programs/rizin/disabled-configuration.nix +++ b/tests/modules/programs/rizin/disabled-configuration.nix @@ -8,6 +8,7 @@ }; nmt.script = '' + assertPathNotExists "home-files/.rizinrc" assertPathNotExists "home-files/.config/rizin/rizinrc" ''; } diff --git a/tests/modules/programs/rizin/prefer-xdg.nix b/tests/modules/programs/rizin/prefer-xdg.nix new file mode 100644 index 00000000..bde44a31 --- /dev/null +++ b/tests/modules/programs/rizin/prefer-xdg.nix @@ -0,0 +1,17 @@ +{ + xdg.enable = true; + + home.preferXdgDirectories = true; + + programs.rizin = { + enable = true; + extraConfig = '' + e asm.bytes=true + e asm.bytes.space=true + ''; + }; + + nmt.script = '' + assertFileExists "home-files/.config/rizin/rizinrc" + ''; +} From a913ae61bf3b9f4312f6097b68cdf0a0fa699279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kugland?= Date: Fri, 13 Feb 2026 18:40:30 -0300 Subject: [PATCH 82/83] firefox: add handlers.json configuration Adds support for configuring Firefox's handlers.json file to manage MIME type and URL scheme handlers declaratively (at `programs.firefox.profiles..handlers`). Handlers control how Firefox opens files and protocols (e.g., PDF viewers, `mailto:` handlers). --- .../misc/news/2025/12/2025-12-10_04-15-59.nix | 18 ++ modules/programs/firefox/mkFirefoxModule.nix | 24 ++ .../programs/firefox/profiles/handlers.nix | 209 ++++++++++++++++++ tests/modules/programs/firefox/common.nix | 1 + .../firefox/profiles/handlers/default.nix | 69 ++++++ .../profiles/handlers/expected-handlers.json | 43 ++++ 6 files changed, 364 insertions(+) create mode 100644 modules/misc/news/2025/12/2025-12-10_04-15-59.nix create mode 100644 modules/programs/firefox/profiles/handlers.nix create mode 100644 tests/modules/programs/firefox/profiles/handlers/default.nix create mode 100644 tests/modules/programs/firefox/profiles/handlers/expected-handlers.json diff --git a/modules/misc/news/2025/12/2025-12-10_04-15-59.nix b/modules/misc/news/2025/12/2025-12-10_04-15-59.nix new file mode 100644 index 00000000..256bc824 --- /dev/null +++ b/modules/misc/news/2025/12/2025-12-10_04-15-59.nix @@ -0,0 +1,18 @@ +{ config, ... }: +{ + time = "2025-12-10T07:15:59+00:00"; + condition = config.programs.firefox.enable; + message = '' + The Firefox module now provides a + 'programs.firefox.profiles..handlers' option. + + It allows declarative configuration of MIME type and URL scheme handlers + through Firefox's handlers.json file, controlling how Firefox opens files + and protocols (e.g., PDF viewers, mailto handlers). + + Configure handlers with: + + programs.firefox.profiles..handlers.mimeTypes + programs.firefox.profiles..handlers.schemes + ''; +} diff --git a/modules/programs/firefox/mkFirefoxModule.nix b/modules/programs/firefox/mkFirefoxModule.nix index 13e61c89..d5291e72 100644 --- a/modules/programs/firefox/mkFirefoxModule.nix +++ b/modules/programs/firefox/mkFirefoxModule.nix @@ -548,6 +548,25 @@ in description = "Declarative search engine configuration."; }; + handlers = mkOption { + type = types.submodule ( + args: + import ./profiles/handlers.nix { + inherit (args) config; + inherit lib pkgs appName; + package = cfg.finalPackage; + modulePath = modulePath ++ [ + "profiles" + name + "handlers" + ]; + profilePath = config.path; + } + ); + default = { }; + description = "Declarative handlers configuration for MIME types and URL schemes."; + }; + containersForce = mkOption { type = types.bool; default = false; @@ -1048,6 +1067,11 @@ in source = profile.search.file; }; + "${cfg.profilesPath}/${profile.path}/handlers.json" = mkIf (profile.handlers.enable) { + source = profile.handlers.configFile; + force = profile.handlers.force; + }; + "${cfg.profilesPath}/${profile.path}/extensions" = mkIf (profile.extensions.packages != [ ]) { source = let diff --git a/modules/programs/firefox/profiles/handlers.nix b/modules/programs/firefox/profiles/handlers.nix new file mode 100644 index 00000000..9bd13985 --- /dev/null +++ b/modules/programs/firefox/profiles/handlers.nix @@ -0,0 +1,209 @@ +{ + config, + lib, + pkgs, + appName, + ... +}: +let + jsonFormat = pkgs.formats.json { }; + + # Process configuration, remove null values and empty handlers arrays. + genCfg = + cfg: + lib.mapAttrs ( + _: item: + (removeAttrs item [ "handlers" ]) + // (lib.optionalAttrs (item.handlers != [ ]) { + handlers = map (handler: lib.filterAttrsRecursive (_: v: v != null) handler) item.handlers; + }) + ) cfg; + + # Common options shared between mimeTypes and schemes + commonHandlerOptions = { + action = lib.mkOption { + type = lib.types.enum [ + 0 + 1 + 2 + 3 + 4 + ]; + default = 1; + description = '' + The action to take for this MIME type / URL scheme. Possible values: + - 0: Save file + - 1: Always ask + - 2: Use helper app + - 3: Open in ${appName} + - 4: Use system default + ''; + }; + + ask = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + If true, the user is asked what they want to do with the file. + If false, the action is taken without user intervention. + ''; + }; + + handlers = lib.mkOption { + type = lib.types.listOf ( + lib.types.submodule { + options = { + name = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + Display name of the handler. + ''; + }; + + path = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + Path to the executable to be used. + + Only one of 'path' or 'uriTemplate' should be set. + ''; + }; + + uriTemplate = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + URI for the application handler. + + Only one of 'path' or 'uriTemplate' should be set. + ''; + }; + }; + } + ); + default = [ ]; + description = '' + An array of handlers with the first one being the default. + If you don't want to have a default handler, use an empty object for the first handler. + Only valid when action is set to 2 (Use helper app). + ''; + }; + }; +in +{ + imports = [ (pkgs.path + "/nixos/modules/misc/meta.nix") ]; + + meta.maintainers = with lib.maintainers; [ kugland ]; + + options = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.schemes != { } || config.mimeTypes != { }; + internal = true; + }; + + force = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to force replace the existing handlers configuration. + ''; + }; + + mimeTypes = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule { + options = commonHandlerOptions // { + extensions = lib.mkOption { + type = lib.types.listOf (lib.types.strMatching "^[^\\.].+$"); + default = [ ]; + example = [ + "jpg" + "jpeg" + ]; + description = '' + List of file extensions associated with this MIME type. + ''; + }; + }; + } + ); + default = { }; + example = lib.literalExpression '' + { + "application/pdf" = { + action = 2; + ask = false; + handlers = [ + { + name = "Okular"; + path = "''${pkgs.okular}/bin/okular"; + } + ]; + extensions = [ "pdf" ]; + }; + } + ''; + description = '' + Attribute set mapping MIME types to their handler configurations. + + For a configuration example, see [this file on Firefox’s source code](https://github.com/mozilla-firefox/firefox/blob/c3797cdebac1316dd7168e995e3468c5a597e8d1/uriloader/exthandler/tests/unit/handlers.json). + ''; + }; + + schemes = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule { + options = commonHandlerOptions; + } + ); + default = { }; + example = lib.literalExpression '' + { + mailto = { + action = 2; + ask = false; + handlers = [ + { + name = "Gmail"; + uriTemplate = "https://mail.google.com/mail/?extsrc=mailto&url=%s"; + } + ]; + }; + } + ''; + description = '' + Attribute set mapping URL schemes to their handler configurations. + + For a configuration example, see [this file on Firefox’s source code](https://github.com/mozilla-firefox/firefox/blob/c3797cdebac1316dd7168e995e3468c5a597e8d1/uriloader/exthandler/tests/unit/handlers.json). + ''; + }; + + finalSettings = lib.mkOption { + type = jsonFormat.type; + internal = true; + readOnly = true; + default = { + defaultHandlersVersion = { }; + isDownloadsImprovementsAlreadyMigrated = false; + mimeTypes = genCfg config.mimeTypes; + schemes = genCfg config.schemes; + }; + description = '' + Resulting handlers.json settings. + ''; + }; + + configFile = lib.mkOption { + type = lib.types.path; + internal = true; + readOnly = true; + default = jsonFormat.generate "handlers.json" config.finalSettings; + description = '' + JSON representation of the handlers configuration. + ''; + }; + }; +} diff --git a/tests/modules/programs/firefox/common.nix b/tests/modules/programs/firefox/common.nix index 14218705..fbd3637d 100644 --- a/tests/modules/programs/firefox/common.nix +++ b/tests/modules/programs/firefox/common.nix @@ -22,6 +22,7 @@ builtins.mapAttrs "${name}-profiles-extensions-assertions" = ./profiles/extensions/assertions.nix; "${name}-profiles-extensions-exhaustive" = ./profiles/extensions/exhaustive.nix; "${name}-profiles-extensions-exact" = ./profiles/extensions/exact.nix; + "${name}-profiles-handlers" = ./profiles/handlers; "${name}-profiles-overwrite" = ./profiles/overwrite; "${name}-profiles-search" = ./profiles/search; "${name}-profiles-settings" = ./profiles/settings; diff --git a/tests/modules/programs/firefox/profiles/handlers/default.nix b/tests/modules/programs/firefox/profiles/handlers/default.nix new file mode 100644 index 00000000..9ce5013f --- /dev/null +++ b/tests/modules/programs/firefox/profiles/handlers/default.nix @@ -0,0 +1,69 @@ +modulePath: +{ + config, + lib, + pkgs, + ... +}: +let + cfg = lib.getAttrFromPath modulePath config; + firefoxMockOverlay = import ../../setup-firefox-mock-overlay.nix modulePath; +in +{ + imports = [ firefoxMockOverlay ]; + + config = lib.mkIf config.test.enableBig ( + lib.setAttrByPath modulePath { + enable = true; + profiles.handlers = { + id = 0; + handlers = { + mimeTypes = { + "application/pdf" = { + action = 2; + ask = false; + handlers = [ + { + name = "Hello App"; + path = "${pkgs.hello}/bin/hello"; + } + ]; + extensions = [ "pdf" ]; + }; + "text/html" = { + action = 4; + ask = true; + extensions = [ + "html" + "htm" + ]; + }; + }; + schemes = { + mailto = { + action = 2; + ask = false; + handlers = [ + { + name = "Gmail"; + uriTemplate = "https://mail.google.com/mail/?extsrc=mailto&url=%s"; + } + ]; + }; + http = { + action = 3; + ask = true; + }; + }; + }; + }; + } + // { + nmt.script = '' + assertFileContent \ + home-files/${cfg.configPath}/handlers/handlers.json \ + ${./expected-handlers.json} + ''; + } + ); +} diff --git a/tests/modules/programs/firefox/profiles/handlers/expected-handlers.json b/tests/modules/programs/firefox/profiles/handlers/expected-handlers.json new file mode 100644 index 00000000..17c5303d --- /dev/null +++ b/tests/modules/programs/firefox/profiles/handlers/expected-handlers.json @@ -0,0 +1,43 @@ +{ + "defaultHandlersVersion": {}, + "isDownloadsImprovementsAlreadyMigrated": false, + "mimeTypes": { + "application/pdf": { + "action": 2, + "ask": false, + "extensions": [ + "pdf" + ], + "handlers": [ + { + "name": "Hello App", + "path": "@hello@/bin/hello" + } + ] + }, + "text/html": { + "action": 4, + "ask": true, + "extensions": [ + "html", + "htm" + ] + } + }, + "schemes": { + "http": { + "action": 3, + "ask": true + }, + "mailto": { + "action": 2, + "ask": false, + "handlers": [ + { + "name": "Gmail", + "uriTemplate": "https://mail.google.com/mail/?extsrc=mailto&url=%s" + } + ] + } + } +} From 436b27742c996b75e2baf8e835e3b3eae0c9fbd4 Mon Sep 17 00:00:00 2001 From: Gaetan Lepage Date: Tue, 17 Feb 2026 15:42:44 +0100 Subject: [PATCH 83/83] mistral-vibe: add module --- .../misc/news/2026/02/2026-02-17_15-42-25.nix | 10 +++ modules/programs/mistral-vibe.nix | 64 +++++++++++++++++++ tests/darwinScrublist.nix | 1 + .../modules/programs/mistral-vibe/default.nix | 1 + .../programs/mistral-vibe/expected.toml | 16 +++++ .../programs/mistral-vibe/mistral-vibe.nix | 34 ++++++++++ 6 files changed, 126 insertions(+) create mode 100644 modules/misc/news/2026/02/2026-02-17_15-42-25.nix create mode 100644 modules/programs/mistral-vibe.nix create mode 100644 tests/modules/programs/mistral-vibe/default.nix create mode 100644 tests/modules/programs/mistral-vibe/expected.toml create mode 100644 tests/modules/programs/mistral-vibe/mistral-vibe.nix diff --git a/modules/misc/news/2026/02/2026-02-17_15-42-25.nix b/modules/misc/news/2026/02/2026-02-17_15-42-25.nix new file mode 100644 index 00000000..e7fae04c --- /dev/null +++ b/modules/misc/news/2026/02/2026-02-17_15-42-25.nix @@ -0,0 +1,10 @@ +{ + time = "2026-02-17T14:42:25+00:00"; + condition = true; + message = '' + + A new module is available: 'programs.mistral-vibe'. + + mistral-vibe is Mistral's open-source CLI coding assistant. + ''; +} diff --git a/modules/programs/mistral-vibe.nix b/modules/programs/mistral-vibe.nix new file mode 100644 index 00000000..35053d3c --- /dev/null +++ b/modules/programs/mistral-vibe.nix @@ -0,0 +1,64 @@ +{ + config, + lib, + pkgs, + ... +}: +let + + cfg = config.programs.mistral-vibe; + + settingsFormat = pkgs.formats.toml { }; + +in +{ + meta.maintainers = [ lib.maintainers.GaetanLepage ]; + + options.programs.mistral-vibe = { + enable = lib.mkEnableOption "Mistral Vibe, Mistral's open-source CLI coding assistant"; + + package = lib.mkPackageOption pkgs "mistral-vibe" { nullable = true; }; + + settings = lib.mkOption { + type = settingsFormat.type; + example = lib.literalExpression '' + { + active_model = "devstral-latest"; + vim_keybindings = false; + tool_paths = []; + + providers = [ + { + name = "mistral"; + backend = "mistral"; + api_base = "https://api.mistral.ai/v1"; + api_key_env_var = "MISTRAL_API_KEY"; + api_style = "openai"; + } + ]; + + models = [ + { + name = "devstral-latest"; + provider = "mistral"; + alias = "devstral-latest"; + temperature = 0.1; + input_price = 0.4; + output_price = 2.0; + } + ]; + } + ''; + description = '' + Mistral Vibe configuration. + For available settings see . + ''; + }; + }; + + config = lib.mkIf cfg.enable { + home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + + home.file.".vibe/config.toml".source = settingsFormat.generate "config.toml" cfg.settings; + }; +} diff --git a/tests/darwinScrublist.nix b/tests/darwinScrublist.nix index a3a1fcf4..5f9771f9 100644 --- a/tests/darwinScrublist.nix +++ b/tests/darwinScrublist.nix @@ -104,6 +104,7 @@ let "mergiraf" "micro" "mise" + "mistral-vibe" "mpv" "msmtp" "mu" diff --git a/tests/modules/programs/mistral-vibe/default.nix b/tests/modules/programs/mistral-vibe/default.nix new file mode 100644 index 00000000..c75706cc --- /dev/null +++ b/tests/modules/programs/mistral-vibe/default.nix @@ -0,0 +1 @@ +{ mistral-vibe-program = ./mistral-vibe.nix; } diff --git a/tests/modules/programs/mistral-vibe/expected.toml b/tests/modules/programs/mistral-vibe/expected.toml new file mode 100644 index 00000000..52a87a91 --- /dev/null +++ b/tests/modules/programs/mistral-vibe/expected.toml @@ -0,0 +1,16 @@ +active_model = "tracer-vibe" + +[[models]] +alias = "tracer-vibe" +input_price = 0.0 +name = "mistralai/devstral-2512:free" +output_price = 0.0 +provider = "tracer-proxy" +temperature = 0.1 + +[[providers]] +api_base = "http://tracer:8081/proxy" +api_key_env_var = "OPENROUTER_API_KEY" +api_style = "openai" +backend = "generic" +name = "tracer-proxy" diff --git a/tests/modules/programs/mistral-vibe/mistral-vibe.nix b/tests/modules/programs/mistral-vibe/mistral-vibe.nix new file mode 100644 index 00000000..0f77eec8 --- /dev/null +++ b/tests/modules/programs/mistral-vibe/mistral-vibe.nix @@ -0,0 +1,34 @@ +{ + programs.mistral-vibe = { + enable = true; + + settings = { + active_model = "tracer-vibe"; + + providers = [ + { + name = "tracer-proxy"; + api_base = "http://tracer:8081/proxy"; + api_key_env_var = "OPENROUTER_API_KEY"; + api_style = "openai"; + backend = "generic"; + } + ]; + models = [ + { + name = "mistralai/devstral-2512:free"; + provider = "tracer-proxy"; + alias = "tracer-vibe"; + temperature = 0.1; + input_price = 0.0; + output_price = 0.0; + } + ]; + }; + }; + + nmt.script = '' + assertFileExists home-files/.vibe/config.toml + assertFileContent home-files/.vibe/config.toml ${./expected.toml} + ''; +}