diff --git a/all-maintainers.nix b/all-maintainers.nix index 7eaca3ff..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"; @@ -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"; @@ -1545,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"; @@ -1634,6 +1652,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"; @@ -1814,13 +1839,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/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" diff --git a/docs/default.nix b/docs/default.nix index 0d8bc8c2..4098bcc0 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 ]; @@ -246,4 +298,7 @@ in in builtins.toJSON result.config.meta.maintainers ); + + # Unstable, for tests. + _internal = { inherit docsLib; }; } 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. 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` }; diff --git a/docs/release-notes/rl-2605.md b/docs/release-notes/rl-2605.md index bcc6c7c7..020e95f5 100644 --- a/docs/release-notes/rl-2605.md +++ b/docs/release-notes/rl-2605.md @@ -23,3 +23,22 @@ 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-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`. + +- 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. + +- 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/flake.lock b/flake.lock index 22a49923..1b8772e3 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1769018530, - "narHash": "sha256-MJ27Cy2NtBEV5tsK+YraYr2g851f3Fl1LpNHDzDX15c=", + "lastModified": 1770841267, + "narHash": "sha256-9xejG0KoqsoKEGp2kVbXRlEYtFFcDTHjidiuX8hGO44=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "88d3861acdd3d2f0e361767018218e51810df8a1", + "rev": "ec7c70d12ce2fc37cb92aff673dcdca89d187bae", "type": "github" }, "original": { 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/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")" 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/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/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/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. + ''; }; }; diff --git a/modules/lib/maintainers.nix b/modules/lib/maintainers.nix index fae1f15f..b1f8842f 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"; @@ -488,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"; 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/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/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. + ''; +} 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. + ''; +} 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. + ''; +} 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. + ''; +} 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 + . + ''; +} 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/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. + "; +} 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/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/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; 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/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 { diff --git a/modules/misc/qt.nix b/modules/misc/qt.nix index 6bee4217..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 = [ @@ -302,7 +301,7 @@ in Appearance = { style = "kvantum"; icon_theme = "Papirus-Dark"; - standar_dialogs = "xdgdesktopportal"; + standard_dialogs = "xdgdesktopportal"; }; Fonts = { fixed = "\"DejaVuSansM Nerd Font Mono,12\""; diff --git a/modules/misc/xdg-user-dirs.nix b/modules/misc/xdg-user-dirs.nix index e3d3db54..b5050394 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 @@ -101,45 +103,88 @@ 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"; + + 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 = 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 { - 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. - wrapped = lib.mapAttrs (_: value: ''"${value}"'') directories; + wrapped = lib.mapAttrs (_: value: ''"${value}"'') bindings; in lib.generators.toKeyValue { } wrapped; xdg.configFile."user-dirs.conf".text = "enabled=False"; - home.sessionVariables = directories; + home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + + home.sessionVariables = lib.mkIf cfg.setSessionVariables bindings; home.activation.createXdgUserDirectories = lib.mkIf cfg.createDirectories ( let 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/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 = { 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/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/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..d5291e72 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, ... }: @@ -555,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; @@ -913,7 +925,6 @@ in }; enableGnomeExtensions = mkOption { - inherit visible; type = types.bool; default = false; description = '' @@ -1056,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/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/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/git.nix b/modules/programs/git.nix index b28380f6..039d4afd 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 = { @@ -380,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}>"; } 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. ''; }; 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"; 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 ]; 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/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/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"; 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/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/modules/programs/mpv.nix b/modules/programs/mpv.nix index a9b6f8d8..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 ]; @@ -139,11 +138,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 { diff --git a/modules/programs/nnn.nix b/modules/programs/nnn.nix index 57434bf0..5032c2ab 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 = { @@ -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 + ); }; } 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" diff --git a/modules/programs/pay-respects.nix b/modules/programs/pay-respects.nix index 53877c39..6cb79874 100644 --- a/modules/programs/pay-respects.nix +++ b/modules/programs/pay-respects.nix @@ -6,8 +6,8 @@ }: 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 +29,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,26 +84,38 @@ in config = lib.mkIf cfg.enable { home.packages = [ cfg.package ]; - programs = { - bash.initExtra = lib.mkIf cfg.enableBashIntegration '' - eval "$(${payRespectsCmd} bash ${cfgOptions})" - ''; + xdg.configFile = lib.mapAttrs' ( + name: rule: + lib.nameValuePair "pay-respects/rules/${name}.toml" { + source = tomlFormat.generate "pay-respects-rule-${name}.toml" rule; + } + ) cfg.rules; - zsh.initContent = lib.mkIf cfg.enableZshIntegration '' - eval "$(${payRespectsCmd} zsh ${cfgOptions})" - ''; + programs = + let + payRespectsCmd = lib.getExe cfg.package; + cfgOptions = lib.concatStringsSep " " cfg.options; + in + { + bash.initExtra = lib.mkIf cfg.enableBashIntegration '' + eval "$(${payRespectsCmd} bash ${cfgOptions})" + ''; - fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration '' - ${payRespectsCmd} fish ${cfgOptions} | source - ''; + zsh.initContent = lib.mkIf cfg.enableZshIntegration '' + eval "$(${payRespectsCmd} zsh ${cfgOptions})" + ''; - nushell.extraConfig = lib.mkIf cfg.enableNushellIntegration '' - source ${ - pkgs.runCommand "pay-respects-nushell-config.nu" { } '' - ${payRespectsCmd} nushell ${cfgOptions} >> "$out" - '' - } - ''; - }; + 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" + '' + } + ''; + }; }; } diff --git a/modules/programs/prismlauncher.nix b/modules/programs/prismlauncher.nix new file mode 100644 index 00000000..decc3e21 --- /dev/null +++ b/modules/programs/prismlauncher.nix @@ -0,0 +1,112 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + inherit (pkgs.stdenv.hostPlatform) isDarwin; + + inherit (lib) + escapeShellArg + listToAttrs + literalExpression + mkIf + mkOption + types + ; + + cfg = config.programs.prismlauncher; + + iniFormat = pkgs.formats.ini { }; +in + +{ + meta.maintainers = with lib.hm.maintainers; [ + mikaeladev + ]; + + options.programs.prismlauncher = { + enable = lib.mkEnableOption "Prism Launcher"; + + package = lib.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 = + let + dataDir = + if (isDarwin && !config.xdg.enable) then + "Library/Application Support/PrismLauncher" + else + "${config.xdg.dataHome}/PrismLauncher"; + + 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 = lib.mkIf (cfg.settings != { }) { + 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 + ) + ); + }; + }; +} 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"; }; diff --git a/modules/programs/rizin.nix b/modules/programs/rizin.nix new file mode 100644 index 00000000..595876ba --- /dev/null +++ b/modules/programs/rizin.nix @@ -0,0 +1,52 @@ +{ + 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 = + 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 ]; + + home.file.${configFile} = lib.mkIf (cfg.extraConfig != "") { + text = cfg.extraConfig; + }; + }; +} 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."; + }; }; }) ); 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/modules/programs/tirith.nix b/modules/programs/tirith.nix new file mode 100644 index 00000000..c0fa9688 --- /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/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/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; + }; }; } 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. ''; 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 != [ ]) { 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. 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/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; + }; + }; }; } 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/services/syncthing.nix b/modules/services/syncthing.nix index a924caaf..a8b38318 100644 --- a/modules/services/syncthing.nix +++ b/modules/services/syncthing.nix @@ -825,40 +825,37 @@ 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"; + StandardOutPath = "${config.home.homeDirectory}/Library/Logs/Syncthing/syncthing-stdout.log"; + StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/Syncthing/syncthing-stderr.log"; }; }; + + syncthing-init = { + enable = cleanedConfig != { }; + config = { + 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"; + }; + }; + }; }) (lib.mkIf cfg.tray.enable { 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/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 = { 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/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/darwinScrublist.nix b/tests/darwinScrublist.nix index 9487b456..5f9771f9 100644 --- a/tests/darwinScrublist.nix +++ b/tests/darwinScrublist.nix @@ -50,6 +50,7 @@ let "eza" "fastfetch" "feh" + "flameshot" "fzf" "gallery-dl" "getconf" @@ -103,6 +104,7 @@ let "mergiraf" "micro" "mise" + "mistral-vibe" "mpv" "msmtp" "mu" 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 = 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/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/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."; + } + ]; +} 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/misc/xdg/default.nix b/tests/modules/misc/xdg/default.nix index 9212694f..53ab57cd 100644 --- a/tests/modules/misc/xdg/default.nix +++ b/tests/modules/misc/xdg/default.nix @@ -4,4 +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/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; 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" + ''} + ''; + }; +} 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/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 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 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" + } + ] + } + } +} 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" + ''; + }; +} 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' + '' + ]; +} 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; } 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} + ''; +} 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 + ''; + }; +} 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} + ''; +} 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} + ''; +} 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}" + ''; +} diff --git a/tests/modules/programs/rizin/basic-configuration.nix b/tests/modules/programs/rizin/basic-configuration.nix new file mode 100644 index 00000000..80d0a8e8 --- /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/.rizinrc" + ''; +} diff --git a/tests/modules/programs/rizin/default.nix b/tests/modules/programs/rizin/default.nix new file mode 100644 index 00000000..c40fe998 --- /dev/null +++ b/tests/modules/programs/rizin/default.nix @@ -0,0 +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 new file mode 100644 index 00000000..149ad957 --- /dev/null +++ b/tests/modules/programs/rizin/disabled-configuration.nix @@ -0,0 +1,14 @@ +{ + programs.rizin = { + enable = false; + extraConfig = '' + e asm.bytes=true + e asm.bytes.space=true + ''; + }; + + 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" + ''; +} 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 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; +} 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 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 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 + ''} + ''; +} 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 = { }; }; 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 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 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"' + ''; + }; +} 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)