Your one-stop-cake-shop for everything Freshly Baked has to offer

feat(ingredients): switch to home module

I've written another nilla module for homes to use sugared ingredients.

Much like the system module, portable submodules aren't suitable because
of a general lack of portability TwT...

Unlike the system module, homes can't currently have their modules
array removed as we seem to be using it to set the home directory and
state version (arguably these should be in a user module and common
respectively - or we could put them both in common and generate the
directory from the name of the home)

+386 -92
+72 -89
packetmix/homes/default.nix
··· 6 6 { config, ... }: 7 7 let 8 8 nixpkgs = config.inputs.nixpkgs.result; 9 - 10 - modules = config.lib.ingredients.collectIngredientsModules ./. { project = config; }; 11 9 in 12 10 { 13 11 config.homes."maya:x86_64-linux" = { ··· 16 14 home.stateVersion = "24.11"; 17 15 home.homeDirectory = "/home/maya"; 18 16 } 19 - { 20 - ingredient = { 21 - catppuccin.enable = true; 22 - collabora.enable = true; 23 - common.enable = true; 24 - development.enable = true; 25 - espanso.enable = true; 26 - freshlybakedcake.enable = true; 27 - gaming.enable = true; 28 - maya.enable = true; 29 - niri.enable = true; 30 - nix-index.enable = true; 31 - remote.enable = true; 32 - scriptfs.enable = true; 33 - }; 34 - } 35 - ] 36 - ++ modules; 17 + ]; 18 + ingredients = [ 19 + "catppuccin" 20 + "collabora" 21 + "common" 22 + "development" 23 + "espanso" 24 + "freshlybakedcake" 25 + "gaming" 26 + "maya" 27 + "niri" 28 + "nix-index" 29 + "remote" 30 + "scriptfs" 31 + ]; 37 32 args = { 38 33 system = "x86_64-linux"; 39 34 }; ··· 44 39 home.stateVersion = "24.11"; 45 40 home.homeDirectory = "/home/minion"; 46 41 } 47 - { 48 - ingredient = { 49 - catppuccin.enable = true; 50 - common.enable = true; 51 - development.enable = true; 52 - espanso.enable = true; 53 - freshlybakedcake.enable = true; 54 - gaming.enable = true; 55 - minion.enable = true; 56 - niri.enable = true; 57 - nix-index.enable = true; 58 - remote.enable = true; 59 - scriptfs.enable = true; 60 - }; 61 - } 62 - ] 63 - ++ modules; 42 + ]; 43 + ingredients = [ 44 + "catppuccin" 45 + "common" 46 + "development" 47 + "espanso" 48 + "freshlybakedcake" 49 + "gaming" 50 + "minion" 51 + "niri" 52 + "nix-index" 53 + "remote" 54 + "scriptfs" 55 + ]; 64 56 args = { 65 57 system = "x86_64-linux"; 66 58 }; ··· 71 63 home.stateVersion = "24.11"; 72 64 home.homeDirectory = "/home/minion"; 73 65 } 74 - { 75 - ingredient = { 76 - catppuccin.enable = true; 77 - collabora.enable = true; 78 - common.enable = true; 79 - development.enable = true; 80 - espanso.enable = true; 81 - freshlybakedcake.enable = true; 82 - gaming.enable = true; 83 - minion.enable = true; 84 - niri.enable = true; 85 - nix-index.enable = true; 86 - redhead.enable = true; 87 - remote.enable = true; 88 - scriptfs.enable = true; 89 - }; 90 - } 91 - ] 92 - ++ modules; 66 + ]; 67 + ingredients = [ 68 + "catppuccin" 69 + "collabora" 70 + "common" 71 + "development" 72 + "espanso" 73 + "freshlybakedcake" 74 + "gaming" 75 + "minion" 76 + "niri" 77 + "nix-index" 78 + "redhead" 79 + "remote" 80 + "scriptfs" 81 + ]; 93 82 args = { 94 83 system = "x86_64-linux"; 95 84 }; ··· 100 89 home.stateVersion = "25.05"; 101 90 home.homeDirectory = "/home/coded"; 102 91 } 103 - { 104 - ingredient = { 105 - catppuccin.enable = true; 106 - coded.enable = true; 107 - common.enable = true; 108 - development.enable = true; 109 - espanso.enable = true; 110 - freshlybakedcake.enable = true; 111 - gaming.enable = true; 112 - niri.enable = true; 113 - nix-index.enable = true; 114 - remote.enable = true; 115 - scriptfs.enable = true; 116 - }; 117 - } 118 - ] 119 - ++ modules; 92 + ]; 93 + ingredients = [ 94 + "catppuccin" 95 + "coded" 96 + "common" 97 + "development" 98 + "espanso" 99 + "freshlybakedcake" 100 + "gaming" 101 + "niri" 102 + "nix-index" 103 + "remote" 104 + "scriptfs" 105 + ]; 120 106 args = { 121 107 system = "x86_64-linux"; 122 108 }; ··· 127 113 home.stateVersion = "25.05"; 128 114 home.homeDirectory = "/home/pinea"; 129 115 } 130 - { 131 - ingredient = { 132 - catppuccin.enable = true; 133 - common.enable = true; 134 - development = true; 135 - espanso = true; 136 - freshlybakedcake.enable = true; 137 - gaming.enable = true; 138 - nix-index.enable = true; 139 - pinea.enable = true; 140 - remote.enable = true; 141 - scriptfs.enable = true; 142 - }; 143 - } 144 - ] 145 - ++ modules; 116 + ]; 117 + ingredients = [ 118 + "catppuccin" 119 + "common" 120 + "development" 121 + "espanso" 122 + "freshlybakedcake" 123 + "gaming" 124 + "nix-index" 125 + "pinea" 126 + "remote" 127 + "scriptfs" 128 + ]; 146 129 args = { 147 130 system = "x86_64-linux"; 148 131 };
+5 -1
packetmix/modules/default.nix
··· 3 3 # SPDX-License-Identifier: MIT 4 4 5 5 { 6 - includes = [ ./ingredients.nix ]; 6 + includes = [ 7 + ./ingredients.nix 8 + ./nilla-home/home.nix 9 + ./nilla-home/nixos.nix 10 + ]; 7 11 }
+2
packetmix/modules/ingredients.nix
··· 30 30 ) 31 31 ); 32 32 }; 33 + 34 + # The type for options.homes is defined in ./nilla-home/homes-type.nix - portable submodules are not mergeable 33 35 }
+23
packetmix/modules/nilla-home/home.nix
··· 1 + # SPDX-FileCopyrightText: 2025 Nilla Home contributors 2 + # 3 + # SPDX-License-Identifier: Apache-2.0 4 + 5 + { lib, config }: 6 + let 7 + inherit (config) inputs; 8 + homes-type = import ./homes-type.nix { inherit lib config; }; 9 + in 10 + { 11 + options.homes = lib.options.create { 12 + description = "Home-Manager homes to create."; 13 + default.value = { }; 14 + type = homes-type; 15 + }; 16 + 17 + config = { 18 + assertions = lib.attrs.mapToList (name: value: { 19 + assertion = !(builtins.isNull value.pkgs); 20 + message = "A Nixpkgs instance is required for the home-manager home \"${name}\", but none was provided and \"inputs.nixpkgs\" does not exist."; 21 + }) config.homes; 22 + }; 23 + }
+121
packetmix/modules/nilla-home/homes-type.nix
··· 1 + # SPDX-FileCopyrightText: 2025 Nilla Home contributors 2 + # 3 + # SPDX-License-Identifier: Apache-2.0 4 + 5 + { lib, config }@nilla: 6 + let 7 + inherit (config) inputs; 8 + 9 + ingredientModules = nilla.config.lib.ingredients.collectIngredientsModules ../../homes { 10 + project = nilla.config; 11 + }; 12 + ingredientExists = nilla.config.lib.ingredients.ingredientExists ../../homes; 13 + in 14 + lib.types.attrs.of ( 15 + lib.types.submodules.portable ({ 16 + name = "home"; 17 + description = "A home-manager home"; 18 + module = 19 + { config }@submodule: 20 + let 21 + home_name = config.__module__.args.dynamic.name; 22 + home_name_parts = builtins.match "([a-z][-a-z0-9]*)(@([-A-Za-z0-9]+))?(:([-_A-Za-z0-9]+))?" home_name; 23 + 24 + argsModule = { 25 + config._module.args = config.args; 26 + _file = "virtual:nilla-nix/home/${home_name}/args"; 27 + }; 28 + 29 + homeForSystem = 30 + system: 31 + config.home-manager.lib.homeManagerConfiguration { 32 + pkgs = config.pkgs.${system}; 33 + lib = config.pkgs.lib; 34 + modules = config.modules ++ [ argsModule ]; 35 + }; 36 + 37 + result = builtins.listToAttrs ( 38 + builtins.map (system: { 39 + name = system; 40 + value = homeForSystem system; 41 + }) config.systems 42 + ); 43 + 44 + username = builtins.elemAt home_name_parts 0; 45 + system = builtins.elemAt home_name_parts 4; 46 + 47 + systemProvided = system != null; 48 + 49 + defaultModules = [ 50 + ( 51 + { lib, ... }: 52 + { 53 + home.username = lib.modules.mkDefault username; 54 + } 55 + ) 56 + ]; 57 + in 58 + { 59 + options = { 60 + systems = 61 + lib.options.create { 62 + description = "The systems this home is valid on."; 63 + type = lib.types.list.of lib.types.string; 64 + } 65 + // ( 66 + if systemProvided then 67 + { 68 + default.value = [ system ]; 69 + writeable = false; 70 + } 71 + else 72 + { } 73 + ); 74 + 75 + args = lib.options.create { 76 + description = "Additional arguments to pass to home-manager modules."; 77 + type = lib.types.attrs.any; 78 + default.value = { }; 79 + }; 80 + 81 + home-manager = lib.options.create { 82 + description = "The home-manager input to use."; 83 + type = lib.types.raw; 84 + default.value = if inputs ? home-manager then inputs.home-manager.result else null; 85 + }; 86 + 87 + pkgs = lib.options.create { 88 + description = "The Nixpkgs instance to use."; 89 + type = lib.types.raw; 90 + default.value = if inputs ? nixpkgs then inputs.nixpkgs.result else null; 91 + }; 92 + 93 + modules = lib.options.create { 94 + description = "A list of modules to use for home-manager."; 95 + type = lib.types.list.of lib.types.raw; 96 + }; 97 + 98 + ingredients = nilla.lib.options.create { 99 + description = "Ingredients to activate for the home"; 100 + type = nilla.lib.types.list.of nilla.lib.types.string; 101 + }; 102 + 103 + result = lib.options.create { 104 + description = "The created Home Manager home for each of the systems."; 105 + type = lib.types.attrs.of lib.types.raw; 106 + writable = false; 107 + default.value = result; 108 + }; 109 + }; 110 + 111 + config = { 112 + modules = 113 + defaultModules 114 + ++ ingredientModules 115 + ++ (map (ingredient: { 116 + config.ingredient.${ingredient}.enable = true; 117 + }) submodule.config.ingredients); # Provided down here rather than as a default so they don't get overriden when a user specifies additional modules 118 + }; 119 + }; 120 + }) 121 + )
+163
packetmix/modules/nilla-home/nixos.nix
··· 1 + # SPDX-FileCopyrightText: 2025 Nilla Home contributors 2 + # 3 + # SPDX-License-Identifier: Apache-2.0 4 + 5 + { config, lib }@global: 6 + let 7 + inherit (global.config) inputs; 8 + homes-type = import ./homes-type.nix { inherit config lib; }; 9 + in 10 + { 11 + options.systems = { 12 + nixos = lib.options.create { 13 + type = lib.types.attrs.of ( 14 + lib.types.submodule ( 15 + { config, name, ... }@submodule: 16 + { 17 + options = { 18 + home-manager = lib.options.create { 19 + description = "The home-manager input to use."; 20 + type = lib.types.raw; 21 + default.value = if inputs ? home-manager then inputs.home-manager.result else null; 22 + }; 23 + 24 + homes = lib.options.create { 25 + description = "Homes to activate for the system, with the same naming scheme as nilla-home's config.homes option"; 26 + type = homes-type; 27 + default.value = { }; 28 + }; 29 + }; 30 + 31 + config.modules = 32 + let 33 + system = submodule.config.pkgs.system; 34 + warn' = builtins.warn or builtins.trace; # builtins.warn doesn't exist on some versions of nix/lix 35 + warnIf = 36 + condition: message: value: 37 + if condition then warn' message value else value; 38 + homeManager = submodule.config.home-manager; 39 + in 40 + (lib.fp.pipe [ 41 + (lib.attrs.mapToList ( 42 + homeName: home: 43 + let 44 + homeNameParts = builtins.match "([a-z][-a-z0-9]*)(@([-A-Za-z0-9]+))?(:([-_A-Za-z0-9]+))?" homeName; 45 + username = builtins.elemAt homeNameParts 0; 46 + in 47 + { 48 + inherit home homeName username; 49 + } 50 + )) 51 + (builtins.map ( 52 + { 53 + home, 54 + homeName, 55 + username, 56 + }@identity: 57 + warnIf (home.home-manager != homeManager) 58 + "The home \"${homeName}\" isn't using the same home-manager input as the NixOS system \"${name}\". This may work, but is not officially supported by the Nilla Home or Nilla NixOS maintainers. Please fix this before reporting any bugs you may find." 59 + identity 60 + )) 61 + (builtins.map ( 62 + { 63 + home, 64 + homeName, 65 + username, 66 + }: 67 + { lib, ... }: 68 + { 69 + _file = "virtual:nilla-nix/home/nixos/${homeName}/nixos"; 70 + config.home-manager.useGlobalPkgs = true; # Required or home modules will lose the nixpkgs config defined in nilla 71 + config.home-manager.users.${username} = 72 + { ... }: 73 + { 74 + _file = "virtual:nilla-nix/home/nixos/${homeName}/homeModule"; 75 + imports = home.modules ++ [ 76 + { 77 + config._module.args = home.args; 78 + _file = "virtual:nilla-nix/home/nixos/${homeName}/args"; 79 + } 80 + ]; 81 + }; 82 + config.users.users.${username}.isNormalUser = lib.modules.mkDefault true; 83 + } 84 + )) 85 + lib.lists.flatten 86 + ] submodule.config.homes) 87 + ++ ( 88 + if submodule.config.homes != [ ] then 89 + [ submodule.config.home-manager.nixosModules.default ] 90 + else 91 + [ ] 92 + ); 93 + } 94 + ) 95 + ); 96 + }; 97 + }; 98 + 99 + config = { 100 + assertions = lib.lists.flatten ( 101 + lib.attrs.mapToList ( 102 + name: value: 103 + let 104 + hasNixpkgs = !(builtins.isNull value.pkgs); 105 + requestedHomes = value.homes != [ ]; 106 + hasHomeManager = !(builtins.isNull value.home-manager); 107 + in 108 + [ 109 + { 110 + assertion = hasNixpkgs; 111 + message = "A Nixpkgs instance is required for the NixOS system \"${name}\", but none was provided and \"inputs.nixpkgs\" does not exist."; 112 + } 113 + { 114 + assertion = !requestedHomes || hasHomeManager; 115 + message = "A home-manager instance is required to enable homes for the NixOS system \"${name}\", but none was provided and \"inputs.home-manager\" does not exist."; 116 + } 117 + (lib.attrs.mapToList ( 118 + homeName: home: 119 + let 120 + homeHasHomeManager = !(builtins.isNull home.home-manager); 121 + homeIsValidForSystem = home ? result.${value.pkgs.system}; 122 + in 123 + [ 124 + { 125 + assertion = homeHasHomeManager; 126 + message = "You've asked for the home \"${homeName}\" to be activated in the NixOS system \"${name}\", but it needs a home-manager instance, none was provided and \"inputs.home-manager\" does not exist."; 127 + } 128 + { 129 + assertion = !homeHasHomeManager || !hasNixpkgs || homeIsValidForSystem; 130 + message = "You've asked for the home \"${homeName}\" to be activated in the NixOS system \"${name}\", but it isn't valid for \"${value.pkgs.system}\" systems."; 131 + } 132 + ] 133 + ) value.homes) 134 + ( 135 + let 136 + usernames = lib.attrs.mapToList ( 137 + homeName: home: 138 + let 139 + homeHasHomeManager = !(builtins.isNull home.home-manager); 140 + homeIsValidForSystem = home ? result.${value.pkgs.system}; 141 + in 142 + if homeHasHomeManager && hasNixpkgs && homeIsValidForSystem then 143 + let 144 + homeNameParts = builtins.match "([a-z][-a-z0-9]*)(@([-A-Za-z0-9]+))?(:([-_A-Za-z0-9]+))?" homeName; 145 + username = builtins.elemAt homeNameParts 0; 146 + in 147 + username 148 + else 149 + null 150 + ) value.homes; 151 + existingUsernames = builtins.filter (username: username != null) usernames; 152 + uniqueUsernames = lib.lists.unique existingUsernames; 153 + in 154 + { 155 + assertion = !hasNixpkgs || (existingUsernames == uniqueUsernames); 156 + message = "There are multiple homes for a single user in the NixOS system \"${name}\". Please make sure you've only enabled a single home per user."; 157 + } 158 + ) 159 + ] 160 + ) global.config.systems.nixos 161 + ); 162 + }; 163 + }
-2
packetmix/nilla.nix
··· 23 23 ./modules 24 24 ./packages 25 25 ./systems 26 - "${pins.nilla-home}/modules/home.nix" # We can't use config.inputs here without infinitely-recursing 27 - "${pins.nilla-home}/modules/nixos.nix" # We can't use config.inputs here without infinitely-recursing 28 26 "${pins.nilla-nixos}/modules/nixos.nix" # We can't use config.inputs here without infinitely-recursing 29 27 ]; 30 28