From f8b7cca02b40afea163cc0d05bf581a8f631e73a Mon Sep 17 00:00:00 2001 From: yunfachi Date: Fri, 29 Aug 2025 11:26:46 +0300 Subject: [PATCH 01/23] init: full rewrite --- .github/workflows/flake-check.yml | 24 ++ README.md | 50 ---- examples/module/flake.lock | 170 +++++++++++ examples/module/flake.nix | 27 ++ examples/module/module.nix | 32 +++ examples/system/flake.lock | 180 ++++++++++++ examples/system/flake.nix | 32 +++ examples/system/host1.nix | 5 + examples/system/host2.nix | 6 + examples/system/module1.nix | 10 + examples/system/module2.nix | 10 + flake-module.nix | 40 +++ flake.lock | 102 ++++--- flake.nix | 93 +++--- lib/attrset.nix | 27 +- lib/configurations/apply.nix | 62 ---- lib/configurations/default.nix | 268 ------------------ lib/configurations/host.nix | 123 -------- lib/configurations/module.nix | 72 ----- lib/configurations/rice.nix | 68 ----- lib/default.nix | 254 +++++++++++++---- lib/extension.nix | 183 ------------ lib/extensions/args.nix | 51 ---- lib/extensions/base/default.nix | 20 -- lib/extensions/base/hosts.nix | 170 ----------- lib/extensions/base/rices.nix | 73 ----- lib/functions.nix | 42 +++ lib/maintainers.nix | 30 -- lib/modules/default.nix | 216 ++++++++++++++ lib/modules/denixArgs.nix | 25 ++ lib/modules/helpers.nix | 81 ++++++ lib/modules/options.nix | 257 +++++++++++++++++ lib/modules/wrappers.nix | 19 ++ lib/options.nix | 178 +++++------- lib/{ => toplevel}/fixed-points.nix | 22 +- lib/toplevel/lib.nix | 138 +++++++++ lib/types.nix | 79 ++++++ modules/betterHosts/default.nix | 6 + modules/betterHosts/features.nix | 41 +++ modules/betterHosts/type.nix | 43 +++ modules/denix/abstractions/default.nix | 6 + modules/denix/abstractions/hosts.nix | 50 ++++ modules/denix/abstractions/modules.nix | 99 +++++++ modules/denix/default.nix | 20 ++ modules/denix/moduleSystems/darwin.nix | 13 + modules/denix/moduleSystems/default.nix | 57 ++++ modules/denix/moduleSystems/home.nix | 13 + modules/denix/moduleSystems/myconfig.nix | 26 ++ modules/denix/moduleSystems/nixos.nix | 13 + .../extensions/extension.nix | 41 --- templates/extensions-collection/flake.nix | 13 - templates/minimal-no-rices/flake.nix | 56 ---- .../hosts/desktop/default.nix | 6 - .../hosts/desktop/hardware.nix | 23 -- .../modules/config/constants.nix | 11 - .../minimal-no-rices/modules/config/home.nix | 22 -- .../minimal-no-rices/modules/config/user.nix | 35 --- templates/minimal/flake.nix | 56 ---- templates/minimal/hosts/desktop/default.nix | 7 - templates/minimal/hosts/desktop/hardware.nix | 23 -- .../minimal/modules/config/constants.nix | 11 - templates/minimal/modules/config/home.nix | 22 -- templates/minimal/modules/config/user.nix | 35 --- templates/minimal/rices/dark/default.nix | 4 - 64 files changed, 2198 insertions(+), 1793 deletions(-) create mode 100644 .github/workflows/flake-check.yml delete mode 100644 README.md create mode 100644 examples/module/flake.lock create mode 100644 examples/module/flake.nix create mode 100644 examples/module/module.nix create mode 100644 examples/system/flake.lock create mode 100644 examples/system/flake.nix create mode 100644 examples/system/host1.nix create mode 100644 examples/system/host2.nix create mode 100644 examples/system/module1.nix create mode 100644 examples/system/module2.nix create mode 100644 flake-module.nix delete mode 100644 lib/configurations/apply.nix delete mode 100644 lib/configurations/default.nix delete mode 100644 lib/configurations/host.nix delete mode 100644 lib/configurations/module.nix delete mode 100644 lib/configurations/rice.nix delete mode 100644 lib/extension.nix delete mode 100644 lib/extensions/args.nix delete mode 100644 lib/extensions/base/default.nix delete mode 100644 lib/extensions/base/hosts.nix delete mode 100644 lib/extensions/base/rices.nix create mode 100644 lib/functions.nix delete mode 100644 lib/maintainers.nix create mode 100644 lib/modules/default.nix create mode 100644 lib/modules/denixArgs.nix create mode 100644 lib/modules/helpers.nix create mode 100644 lib/modules/options.nix create mode 100644 lib/modules/wrappers.nix rename lib/{ => toplevel}/fixed-points.nix (73%) create mode 100644 lib/toplevel/lib.nix create mode 100644 lib/types.nix create mode 100644 modules/betterHosts/default.nix create mode 100644 modules/betterHosts/features.nix create mode 100644 modules/betterHosts/type.nix create mode 100644 modules/denix/abstractions/default.nix create mode 100644 modules/denix/abstractions/hosts.nix create mode 100644 modules/denix/abstractions/modules.nix create mode 100644 modules/denix/default.nix create mode 100644 modules/denix/moduleSystems/darwin.nix create mode 100644 modules/denix/moduleSystems/default.nix create mode 100644 modules/denix/moduleSystems/home.nix create mode 100644 modules/denix/moduleSystems/myconfig.nix create mode 100644 modules/denix/moduleSystems/nixos.nix delete mode 100644 templates/extensions-collection/extensions/extension.nix delete mode 100644 templates/extensions-collection/flake.nix delete mode 100644 templates/minimal-no-rices/flake.nix delete mode 100644 templates/minimal-no-rices/hosts/desktop/default.nix delete mode 100644 templates/minimal-no-rices/hosts/desktop/hardware.nix delete mode 100644 templates/minimal-no-rices/modules/config/constants.nix delete mode 100644 templates/minimal-no-rices/modules/config/home.nix delete mode 100644 templates/minimal-no-rices/modules/config/user.nix delete mode 100644 templates/minimal/flake.nix delete mode 100644 templates/minimal/hosts/desktop/default.nix delete mode 100644 templates/minimal/hosts/desktop/hardware.nix delete mode 100644 templates/minimal/modules/config/constants.nix delete mode 100644 templates/minimal/modules/config/home.nix delete mode 100644 templates/minimal/modules/config/user.nix delete mode 100644 templates/minimal/rices/dark/default.nix diff --git a/.github/workflows/flake-check.yml b/.github/workflows/flake-check.yml new file mode 100644 index 0000000..7c4651e --- /dev/null +++ b/.github/workflows/flake-check.yml @@ -0,0 +1,24 @@ +name: Nix Flake Check + +on: + pull_request: + push: + branches: master + workflow_dispatch: + +permissions: read-all + +jobs: + nix-flake-check: + name: Nix Flake Check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + + - name: Check Flake + run: nix flake check + diff --git a/README.md b/README.md deleted file mode 100644 index 98b4991..0000000 --- a/README.md +++ /dev/null @@ -1,50 +0,0 @@ -

- - - - - Denix - - -

- -Denix is a Nix library designed to help you build scalable configurations for [NixOS](https://nixos.org/), [Home Manager](https://github.com/nix-community/home-manager), and [Nix-Darwin](https://github.com/nix-darwin/nix-darwin). - -## Documentation - -You can find the documentation here: [Denix Documentation](https://yunfachi.github.io/denix/getting_started/introduction) - -## Key Features - -### Modular System -Custom modules allow you to define options and related configurations in a flexible way, simplifying the management of your entire system. - -### Hosts and Rices -* **Hosts**: Unique configurations tailored for each machine. -* **Rices**: Customizations that can be applied to all hosts. - -### Extensions -Write your own extensions for the Denix or use existing ones that add new functions and modules. - -### Unified NixOS, Home Manager, and Nix-Darwin Configurations -Write your NixOS, Home Manager, and Nix-Darwin configurations in a single file*, and Denix will automatically handle the separation for you. - -## Templates - -### [minimal](./templates/minimal/) (recommended) -Hosts, rices, and initial modules for quick setup: -```sh -nix flake init -t github:yunfachi/denix#minimal -``` - -### [minimal-no-rices](./templates/minimal-no-rices/) -Hosts and initial modules without rices: -```sh -nix flake init -t github:yunfachi/denix#minimal-no-rices -``` - -### [extensions-collection](./templates/extensions-collection/) -Flake for creating your own collection of Denix extensions: -```sh -nix flake init -t github:yunfachi/denix#extensions-collection -``` diff --git a/examples/module/flake.lock b/examples/module/flake.lock new file mode 100644 index 0000000..2471ab5 --- /dev/null +++ b/examples/module/flake.lock @@ -0,0 +1,170 @@ +{ + "nodes": { + "denix": { + "inputs": { + "git-hooks": "git-hooks", + "home-manager": "home-manager", + "nix-darwin": "nix-darwin", + "nixpkgs": "nixpkgs_2", + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "path": "../../.", + "type": "path" + }, + "original": { + "path": "../../.", + "type": "path" + }, + "parent": [] + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1759523803, + "narHash": "sha256-PTod9NG+i3XbbnBKMl/e5uHDBYpwIWivQ3gOWSEuIEM=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "cfc9f7bb163ad8542029d303e599c0f7eee09835", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "denix", + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "denix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759573136, + "narHash": "sha256-ILSPD0Dm8p0w0fCVzOx98ZH8yFDrR75GmwmH3fS2VnE=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "5f06ceafc6c9b773a776b9195c3f47bbe1defa43", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "home-manager", + "type": "github" + } + }, + "nix-darwin": { + "inputs": { + "nixpkgs": [ + "denix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1758805352, + "narHash": "sha256-BHdc43Lkayd+72W/NXRKHzX5AZ+28F3xaUs3a88/Uew=", + "owner": "nix-darwin", + "repo": "nix-darwin", + "rev": "c48e963a5558eb1c3827d59d21c5193622a1477c", + "type": "github" + }, + "original": { + "owner": "nix-darwin", + "repo": "nix-darwin", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1759070547, + "narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "647e5c14cbd5067f44ac86b74f014962df460840", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1759652833, + "narHash": "sha256-wTPGkIxVbJrZobKNCNhC5a0XB8Lknuta340ShA00LV4=", + "path": "/home/yunfachi/files/desktop/git/nixpkgs/lib", + "type": "path" + }, + "original": { + "path": "/home/yunfachi/files/desktop/git/nixpkgs/lib", + "type": "path" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1759652833, + "narHash": "sha256-GUDAlrlI/3RXDqIrwI2JFzczNNOpsxvKTIECS+OE/0I=", + "path": "/home/yunfachi/files/desktop/git/nixpkgs", + "type": "path" + }, + "original": { + "path": "/home/yunfachi/files/desktop/git/nixpkgs", + "type": "path" + } + }, + "root": { + "inputs": { + "denix": "denix" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/examples/module/flake.nix b/examples/module/flake.nix new file mode 100644 index 0000000..2575758 --- /dev/null +++ b/examples/module/flake.nix @@ -0,0 +1,27 @@ +{ + inputs = { + denix.url = "../../."; + }; + + outputs = + { + denix, + self, + ... + }: + let + delib = denix.lib; + modules = delib.genModules { + configuration = self.denixConfiguration; + }; + in + { + denixConfiguration = delib.denixConfiguration { + modules = [ ./module.nix ]; + }; + + nixosModules.default = modules.nixos; + + homeModules.default = modules.home; + }; +} diff --git a/examples/module/module.nix b/examples/module/module.nix new file mode 100644 index 0000000..ea39b66 --- /dev/null +++ b/examples/module/module.nix @@ -0,0 +1,32 @@ +{ delib, ... }: +{ + modules."programs.module" = { + options = with delib; { + enable = boolOption true; + test = intOption 0; + asd = intOption 1; + }; + + myconfig.always = + { config, ... }: + { + #programs.module.test = config.myconfig.programs.module.asd; + }; + + #myconfig.ifEnabled.programs.module.test = 999; + + nixos.ifEnabled = + { test, ... }: + { + #nixpkgs.hostPlatform = "x86_64-linux"; + _module.args.test = 123; + myconfig.programs.module.test = test; + }; + }; + + hosts."keka" = { + myconfig.ifDisabled = { + programs.module.enable = false; + }; + }; +} diff --git a/examples/system/flake.lock b/examples/system/flake.lock new file mode 100644 index 0000000..1257c5d --- /dev/null +++ b/examples/system/flake.lock @@ -0,0 +1,180 @@ +{ + "nodes": { + "denix": { + "inputs": { + "git-hooks": "git-hooks", + "home-manager": "home-manager", + "nix-darwin": "nix-darwin", + "nixpkgs": "nixpkgs_2", + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "path": "../../.", + "type": "path" + }, + "original": { + "path": "../../.", + "type": "path" + }, + "parent": [] + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1757239681, + "narHash": "sha256-E9spYi9lxm2f1zWQLQ7xQt8Xs2nWgr1T4QM7ZjLFphM=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "ab82ab08d6bf74085bd328de2a8722c12d97bd9d", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "denix", + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "denix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1757075491, + "narHash": "sha256-a+NMGl5tcvm+hyfSG2DlVPa8nZLpsumuRj1FfcKb2mQ=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "f56bf065f9abedc7bc15e1f2454aa5c8edabaacf", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "home-manager", + "type": "github" + } + }, + "nix-darwin": { + "inputs": { + "nixpkgs": [ + "denix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1757130842, + "narHash": "sha256-4i7KKuXesSZGUv0cLPLfxbmF1S72Gf/3aSypgvVkwuA=", + "owner": "nix-darwin", + "repo": "nix-darwin", + "rev": "15f067638e2887c58c4b6ba1bdb65a0b61dc58c5", + "type": "github" + }, + "original": { + "owner": "nix-darwin", + "repo": "nix-darwin", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1754340878, + "narHash": "sha256-lgmUyVQL9tSnvvIvBp7x1euhkkCho7n3TMzgjdvgPoU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "cab778239e705082fe97bb4990e0d24c50924c04", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1759431872, + "narHash": "sha256-udV7H1CXdx7xqTheYsFblnBmaBDF3SrFhXrfHK3/pFI=", + "owner": "yunfachi", + "repo": "nixpkgs", + "rev": "0e623418081b847aaad7b975fcadc2ea4c04369c", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "yunfachi", + "ref": "feat/extensible-option-types", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1756478791, + "narHash": "sha256-F2fHTBUA/S99Kwv99k51Xb990LIihlK2Ys29rrTfMoE=", + "owner": "yunfachi", + "repo": "nixpkgs", + "rev": "da2a71835653a4d11bbe3738830d90f048ae9d30", + "type": "github" + }, + "original": { + "owner": "yunfachi", + "ref": "patch-2", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "denix": "denix" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/examples/system/flake.nix b/examples/system/flake.nix new file mode 100644 index 0000000..c074601 --- /dev/null +++ b/examples/system/flake.nix @@ -0,0 +1,32 @@ +{ + inputs = { + #nixpkgs.url = "/home/yunfachi/files/desktop/git/nixpkgs"; + denix.url = "../../."; + #denix.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = + { + denix, + self, + ... + }: + let + delib = denix.lib; + in + { + denixConfiguration = delib.denixConfiguration { + modules = [ + ./module1.nix + ./module2.nix + ./host1.nix + ./host2.nix + ]; + }; + + nixosConfigurations = self.denixConfiguration.genSystem { + moduleSystem = "nixos"; + forEachHost = true; + }; + }; +} diff --git a/examples/system/host1.nix b/examples/system/host1.nix new file mode 100644 index 0000000..840beec --- /dev/null +++ b/examples/system/host1.nix @@ -0,0 +1,5 @@ +{ + hosts.host1 = { + nixos.ifEnabled.nixpkgs.hostPlatform = "x86_64-linux"; + }; +} diff --git a/examples/system/host2.nix b/examples/system/host2.nix new file mode 100644 index 0000000..659afe0 --- /dev/null +++ b/examples/system/host2.nix @@ -0,0 +1,6 @@ +{ delib, ... }: +delib.host { + name = "host2"; + + nixos.ifEnabled.nixpkgs.hostPlatform = "x86_64-linux"; +} diff --git a/examples/system/module1.nix b/examples/system/module1.nix new file mode 100644 index 0000000..9d06c0a --- /dev/null +++ b/examples/system/module1.nix @@ -0,0 +1,10 @@ +{ delib, ... }: +{ + modules."programs.git" = { + options = { + enable = delib.boolOption false; + }; + + nixos.ifEnabled.programs.git.enable = true; + }; +} diff --git a/examples/system/module2.nix b/examples/system/module2.nix new file mode 100644 index 0000000..ab52ff6 --- /dev/null +++ b/examples/system/module2.nix @@ -0,0 +1,10 @@ +{ delib, ... }: +delib.module { + name = "programs.bash"; + options = { + enable = delib.boolOption false; + }; + + nixos.ifEnabled.programs.bash.enable = true; + nixos.ifDisabled.programs.bash.enable = false; +} diff --git a/flake-module.nix b/flake-module.nix new file mode 100644 index 0000000..a863225 --- /dev/null +++ b/flake-module.nix @@ -0,0 +1,40 @@ +self: +{ + lib, + delib, + config, + options, + ... +}: +{ + config._module.args.delib = self.lib; + + options = with delib; { + denixSettings = { + denixConfigurationExtraArgs = attrsOption { }; + generateSystems = boolOption true; + generateSystemsArgs = attrsOption { + forEachModuleSystem = true; + forEachHost = true; + }; + generateModules = boolOption true; + generateModulesArgs = attrsOption { + forEachModuleSystem = true; + forEachHost = true; + }; + }; + + denixConfiguration = attrsOption options.denix.valueMeta.configuration; + # denixConfiguration.config + denix = modules.denixConfigurationSubmoduleOption config.denixSettings.denixConfigurationExtraArgs; + }; + + config.flake = lib.mkMerge ( + (lib.optional config.denixSettings.generateModules ( + config.denixConfiguration.genModules config.denixSettings.generateModulesArgs + )) + ++ (lib.optional config.denixSettings.generateSystems ( + config.denixConfiguration.genSystems config.denixSettings.generateSystemsArgs + )) + ); +} diff --git a/flake.lock b/flake.lock index 8ea9049..297ed32 100644 --- a/flake.lock +++ b/flake.lock @@ -16,10 +16,30 @@ "type": "github" } }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1759523803, + "narHash": "sha256-PTod9NG+i3XbbnBKMl/e5uHDBYpwIWivQ3gOWSEuIEM=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "cfc9f7bb163ad8542029d303e599c0f7eee09835", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ - "pre-commit-hooks", + "git-hooks", "nixpkgs" ] }, @@ -44,11 +64,11 @@ ] }, "locked": { - "lastModified": 1754613544, - "narHash": "sha256-ueR1mGX4I4DWfDRRxxMphbKDNisDeMPMusN72VV1+cc=", + "lastModified": 1759573136, + "narHash": "sha256-ILSPD0Dm8p0w0fCVzOx98ZH8yFDrR75GmwmH3fS2VnE=", "owner": "nix-community", "repo": "home-manager", - "rev": "cc2fa2331aebf9661d22bb507d362b39852ac73f", + "rev": "5f06ceafc6c9b773a776b9195c3f47bbe1defa43", "type": "github" }, "original": { @@ -65,11 +85,11 @@ ] }, "locked": { - "lastModified": 1751313918, - "narHash": "sha256-HsJM3XLa43WpG+665aGEh8iS8AfEwOIQWk3Mke3e7nk=", + "lastModified": 1758805352, + "narHash": "sha256-BHdc43Lkayd+72W/NXRKHzX5AZ+28F3xaUs3a88/Uew=", "owner": "nix-darwin", "repo": "nix-darwin", - "rev": "e04a388232d9a6ba56967ce5b53a8a6f713cdfcf", + "rev": "c48e963a5558eb1c3827d59d21c5193622a1477c", "type": "github" }, "original": { @@ -80,27 +100,27 @@ }, "nixpkgs": { "locked": { - "lastModified": 1754498491, - "narHash": "sha256-erbiH2agUTD0Z30xcVSFcDHzkRvkRXOQ3lb887bcVrs=", - "owner": "nixos", + "lastModified": 1759070547, + "narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "c2ae88e026f9525daf89587f3cbee584b92b6134", + "rev": "647e5c14cbd5067f44ac86b74f014962df460840", "type": "github" }, "original": { - "owner": "nixos", - "ref": "nixos-unstable", + "owner": "NixOS", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-lib": { "locked": { - "lastModified": 1754184128, - "narHash": "sha256-AjhoyBL4eSyXf01Bmc6DiuaMrJRNdWopmdnMY0Pa/M0=", + "lastModified": 1762650614, + "narHash": "sha256-tPIUJjNeNs3LMWH8w2nHLx0trZJdOJ54mN2UQhcjZ9g=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "02e72200e6d56494f4a7c0da8118760736e41b60", + "rev": "91ea24e62ff55f95939f32432fa5def2d6d24d2a", "type": "github" }, "original": { @@ -111,48 +131,44 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1754340878, - "narHash": "sha256-lgmUyVQL9tSnvvIvBp7x1euhkkCho7n3TMzgjdvgPoU=", - "owner": "NixOS", + "lastModified": 1761114652, + "narHash": "sha256-f/QCJM/YhrV/lavyCVz8iU3rlZun6d+dAiC3H+CDle4=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "cab778239e705082fe97bb4990e0d24c50924c04", + "rev": "01f116e4df6a15f4ccdffb1bcd41096869fb385c", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", + "owner": "nixos", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, - "pre-commit-hooks": { + "root": { "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": "nixpkgs_2" - }, + "git-hooks": "git-hooks", + "home-manager": "home-manager", + "nix-darwin": "nix-darwin", + "nixpkgs": "nixpkgs_2", + "nixpkgs-lib": "nixpkgs-lib", + "systems": "systems" + } + }, + "systems": { "locked": { - "lastModified": 1754416808, - "narHash": "sha256-c6yg0EQ9xVESx6HGDOCMcyRSjaTpNJP10ef+6fRcofA=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "9c52372878df6911f9afc1e2a1391f55e4dfc864", + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "type": "github" }, "original": { - "owner": "cachix", - "repo": "git-hooks.nix", + "owner": "nix-systems", + "repo": "default", "type": "github" } - }, - "root": { - "inputs": { - "home-manager": "home-manager", - "nix-darwin": "nix-darwin", - "nixpkgs": "nixpkgs", - "nixpkgs-lib": "nixpkgs-lib", - "pre-commit-hooks": "pre-commit-hooks" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 61df5ba..ab8dc0b 100644 --- a/flake.nix +++ b/flake.nix @@ -1,12 +1,18 @@ { - description = "Nix library for creating scalable NixOS, Home Manager, and Nix-Darwin configurations with modules, hosts, and rices."; + description = "Nix framework for creating scalable configurations, modules, and libraries."; inputs = { nixpkgs-lib.url = "github:nix-community/nixpkgs.lib"; - pre-commit-hooks = { - url = "github:cachix/git-hooks.nix"; - }; + git-hooks.url = "github:cachix/git-hooks.nix"; + systems.url = "github:nix-systems/default"; + /** + The reason for separating nixpkgs and nixpkgs-lib is that nixpkgs, home-manager, + and nix-darwin are inputs used exclusively for creating the system configuration + (e.g., lib.nixosSystem, ...). nixpkgs is an input, which implies user overrides + to their own channel, while nixpkgs-lib is a library used by Denix, and it should + not be overridden by the user without a special reason. + */ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; home-manager = { url = "github:nix-community/home-manager/master"; @@ -22,55 +28,62 @@ { self, nixpkgs-lib, - pre-commit-hooks, nixpkgs, - home-manager, - nix-darwin, + git-hooks, + systems, ... }: let - supportedSystems = [ - "x86_64-linux" - "aarch64-linux" - "x86_64-darwin" - "aarch64-darwin" - ]; - - forAllSystems = nixpkgs-lib.lib.genAttrs supportedSystems; + forAllSystems = nixpkgs-lib.lib.genAttrs (import systems); in { + denixModules = { + default = self.denixModules.denix; + denix = ./modules/denix; + + betterHosts = ./modules/betterHosts; + }; lib = import ./lib { inherit (nixpkgs-lib) lib; - inherit home-manager nix-darwin nixpkgs; + inherit (self) inputs denixModules; }; - templates = { - minimal = { - description = '' - Minimal configuration with hosts, rices, constants, home manager, and user config. - It is not recommended to use if this is your first time or if you haven't read or don't plan to read the documentation. - ''; - path = ./templates/minimal; - }; - minimal-no-rices = { - description = '' - Minimal configuration with hosts, constants, home manager, and user config. - It is not recommended to use if this is your first time or if you haven't read or don't plan to read the documentation. - ''; - path = ./templates/minimal-no-rices; - }; - extensions-collection = { - description = '' - Flake for creating your own collection of Denix extensions. - ''; - path = ./templates/extensions-collection; - }; - }; + flakeModules.default = import ./flake-module.nix self; + flakeModule = self.flakeModules.default; checks = forAllSystems (system: { - pre-commit-check = pre-commit-hooks.lib.${system}.run { + pre-commit-check = git-hooks.lib.${system}.run { src = ./.; - hooks.nixfmt-rfc-style.enable = true; + hooks = { + nixfmt-rfc-style.enable = true; + keep-sorted = { + enable = true; + name = "keep-sorted"; + language = "system"; + entry = "${nixpkgs.legacyPackages.${system}.keep-sorted}/bin/keep-sorted"; + }; + cog = { + enable = true; + name = "cog"; + language = "system"; + entry = nixpkgs.lib.getExe ( + nixpkgs.legacyPackages.${system}.writeShellApplication { + name = "denix-cog"; + + runtimeInputs = with nixpkgs.legacyPackages.${system}; [ + python313Packages.cogapp + nix + ]; + + text = '' + export pre_evaled_options='${builtins.toJSON (builtins.attrNames self.lib.options)}' + export pre_evaled_types='${builtins.toJSON (builtins.attrNames self.lib.types)}' + cog -r "$@" + ''; + } + ); + }; + }; }; }); diff --git a/lib/attrset.nix b/lib/attrset.nix index bb79604..b05ab3c 100644 --- a/lib/attrset.nix +++ b/lib/attrset.nix @@ -1,12 +1,29 @@ { lib, ... }: { getAttrByStrPath = - strPath: attrset: default: - lib.attrByPath (lib.splitString "." strPath) default attrset; + set: strPath: default: + lib.attrByPath (lib.splitString "." strPath) default set; - setAttrByStrPath = strPath: value: lib.setAttrByPath (lib.splitString "." strPath) value; + setAttrByStrPath = + value: strPath: + if strPath != null then lib.setAttrByPath (lib.splitString "." strPath) value else value; hasAttrs = - attrs: attrset: - if attrs != [ ] then builtins.any (attr: builtins.hasAttr attr attrset) attrs else true; + set: attrs: if attrs != [ ] then builtins.any (attr: builtins.hasAttr attr set) attrs else true; + + removeAttrs = removeAttrs; + + keepAttrs = + attrs: names: + removeAttrs attrs (builtins.filter (name: !builtins.elem name names) (builtins.attrNames attrs)); + + strictMergeAttrs = + left: right: + let + conflicts = builtins.attrNames (builtins.intersectAttrs left right); + in + if conflicts != [ ] then + lib.throw "strictMergeAttrs: conflicting keys: ${builtins.concatStringsSep ", " conflicts}" + else + left // right; } diff --git a/lib/configurations/apply.nix b/lib/configurations/apply.nix deleted file mode 100644 index 40c022e..0000000 --- a/lib/configurations/apply.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ - useHomeManagerModule, - homeManagerUser, - moduleSystem, - myconfigName, - ... -}: -rec { - configForModuleSystem = - { - nixos ? { }, - home ? { }, - darwin ? { }, - }: - { - inherit nixos home darwin; - } - .${moduleSystem}; - - myconfig = _myconfig: { ${myconfigName} = _myconfig; }; - - nixos = - _nixos: - configForModuleSystem { - nixos = _nixos; - home = { }; - darwin = { }; - }; - home = - _home: - configForModuleSystem { - nixos = - if useHomeManagerModule then - { - home-manager.users.${homeManagerUser} = _home; - } - else - { }; - home = _home; - darwin = - if useHomeManagerModule then - { - home-manager.users.${homeManagerUser} = _home; - } - else - { }; - }; - darwin = - _darwin: - configForModuleSystem { - nixos = { }; - home = { }; - darwin = _darwin; - }; - - listOfEverything = _myconfig: _nixos: _home: _darwin: [ - (myconfig _myconfig) - (nixos _nixos) - (home _home) - (darwin _darwin) - ]; -} diff --git a/lib/configurations/default.nix b/lib/configurations/default.nix deleted file mode 100644 index be482f6..0000000 --- a/lib/configurations/default.nix +++ /dev/null @@ -1,268 +0,0 @@ -{ - delib, - lib, - nixpkgs, - home-manager, - nix-darwin, - ... -}@args: -{ - configurations = - { - # Denix - myconfigName ? "myconfig", - denixLibName ? "delib", - moduleSystem ? null, # TODO: remove the default value once the deprecated isHomeManager is removed - extensions ? [ ], - # Umport - paths ? [ ], - exclude ? [ ], - recursive ? true, - # System - specialArgs ? { }, - extraModules ? [ ], - # Inputs - nixpkgs ? args.nixpkgs, - home-manager ? args.home-manager, - nix-darwin ? args.nix-darwin, - # Home Manager - homeManagerNixpkgs ? nixpkgs, - homeManagerUser ? null, # not default value! - useHomeManagerModule ? true, - isHomeManager ? null, # TODO: DEPRECATED since 2025/05/05 - # Dev - mkConfigurationsSystemExtraModule ? { - nixpkgs.hostPlatform = "x86_64-linux"; - }, # just a plug; FIXME - }@topArgs: - ( - if topArgs ? isHomeManager then - builtins.trace "'delib.configurations :: isHomeManager' is deprecated, use 'delib.configurations :: moduleSystem' with values \"nixos\", \"home\", or \"darwin\" instead." - else - _: _ - ) - ( - let - moduleSystem = - topArgs.moduleSystem or ( - if isHomeManager == null then - builtins.abort "Please specify 'delib.configurations :: moduleSystem'. Valid values are \"nixos\", \"home\", or \"darwin\"." - else - (if isHomeManager then "home" else "nixos") - ); - - files = delib.umport { inherit paths exclude recursive; }; - - mkApply = - moduleSystem: useHomeManagerModule: - import ./apply.nix { - inherit - useHomeManagerModule - homeManagerUser - moduleSystem - myconfigName - ; - }; - mkDenixLib = - { - config, - moduleSystem, - useHomeManagerModule, - homeManagerUser, - currentHostName ? null, - }: - let - extendedDelib = delib.recursivelyExtend ( - final: prev: - let - apply = mkApply moduleSystem useHomeManagerModule; - inherit (final) _callLib; - in - { - _callLibArgs = prev._callLibArgs // { - inherit - apply - config - myconfigName - currentHostName - useHomeManagerModule - homeManagerUser - ; - }; - - inherit (_callLib ./host.nix) - host - hostSubmoduleOptions - hostOption - hostsOption - hostNamesAssertions - ; - - inherit (_callLib ./module.nix) module; - - inherit (_callLib ./rice.nix) - rice - riceSubmoduleOptions - riceOption - ricesOption - riceNamesAssertions - ; - } - ); - in - extendedDelib.withExtensions extensions; - - mkSystem = - { - moduleSystem, - useHomeManagerModule, - homeManagerUser, - homeManagerSystem, - currentHostName, - isInternal ? false, - internalExtraModules ? (moduleSystem_: [ ]), - }@args: - if homeManagerUser == null && (useHomeManagerModule || moduleSystem == "home") then - abort "Please specify 'delib.configurations :: homeManagerUser' or 'delib.host :: homeManagerUser'." - else - let - useHomeManagerModule = - if isInternal then topArgs.useHomeManagerModule or true else args.useHomeManagerModule; - homeManagerUser = if isInternal then topArgs.homeManagerUser or null else args.homeManagerUser; - - extensionsModules = builtins.concatMap (extension: extension.modules) extensions; - nixosSystem = nixpkgs.lib.nixosSystem { - specialArgs = specialArgs // { - ${denixLibName} = mkDenixLib { - config = nixosSystem.config; - moduleSystem = "nixos"; - inherit currentHostName useHomeManagerModule homeManagerUser; - }; - inherit useHomeManagerModule homeManagerUser; # otherwise it's impossible to make config.home-manager optional when not useHomeManagerModule. - }; - modules = - (internalExtraModules "nixos") - ++ extraModules - ++ files - ++ (lib.optionals useHomeManagerModule [ home-manager.nixosModules.home-manager ]) - ++ extensionsModules; - }; - homeSystem = home-manager.lib.homeManagerConfiguration { - extraSpecialArgs = specialArgs // { - ${denixLibName} = mkDenixLib { - config = homeSystem.config; - moduleSystem = "home"; - inherit currentHostName useHomeManagerModule homeManagerUser; - }; - inherit useHomeManagerModule homeManagerUser; # otherwise it's impossible to make config.home-manager optional when not useHomeManagerModule. - }; - pkgs = homeManagerNixpkgs.legacyPackages.${homeManagerSystem}; - modules = (internalExtraModules "home") ++ extraModules ++ files ++ extensionsModules; - }; - darwinSystem = nix-darwin.lib.darwinSystem { - specialArgs = specialArgs // { - ${denixLibName} = mkDenixLib { - config = darwinSystem.config; - moduleSystem = "darwin"; - inherit currentHostName useHomeManagerModule homeManagerUser; - }; - inherit useHomeManagerModule homeManagerUser; # otherwise it's impossible to make config.home-manager optional when not useHomeManagerModule. - }; - # FIXME: is this really necessary? - # pkgs = ...; - modules = - (internalExtraModules "darwin") - ++ extraModules - ++ files - ++ (lib.optionals useHomeManagerModule [ home-manager.darwinModules.home-manager ]) - ++ extensionsModules; - }; - in - { - nixos = nixosSystem; - home = homeSystem; - darwin = darwinSystem; - } - .${moduleSystem}; - - inherit - ( - { - rices = { }; - } - // (mkSystem { - # TODO: maybe add moduleSystem = "empty", which "apply" would skip entirely*? - moduleSystem = "nixos"; - useHomeManagerModule = topArgs.useHomeManagerModule; # FIXME - homeManagerUser = "user"; - homeManagerSystem = "x86_64-linux"; # just a plug; FIXME - currentHostName = null; - isInternal = true; - internalExtraModules = _: [ mkConfigurationsSystemExtraModule ]; - }).config.${myconfigName} - ) - hosts - rices - ; - - mkHost = - { - host, - rice ? null, - }: - let - myconfig = system.config.${myconfigName}; - - system = mkSystem { - inherit moduleSystem; - inherit (host) useHomeManagerModule homeManagerUser homeManagerSystem; - currentHostName = host.name; - internalExtraModules = - moduleSystem: - let - apply = mkApply moduleSystem host.useHomeManagerModule; - in - [ - ( - { options, ... }: - { - config.${myconfigName} = { - inherit host; - } - // lib.optionalAttrs (options.${myconfigName} ? rice) { inherit rice; }; - } - ) - ] - ++ (lib.optionals (rice != null) ( - apply.listOfEverything rice.myconfig rice.nixos rice.home rice.darwin - )) - ++ builtins.concatMap ( - riceName: - (apply.listOfEverything rices.${riceName}.myconfig rices.${riceName}.nixos rices.${riceName}.home) - rices.${riceName}.darwin - ) (rice.inherits or [ ]); - }; - in - system; - - configurations = - let - mkHostAttrs = - riceName: rice: hostName: host: - lib.optionalAttrs (!rice.inheritanceOnly or false) { - "${lib.optionalString (moduleSystem == "home") "${host.homeManagerUser}@"}${hostName}${ - lib.optionalString (riceName != null) "-${riceName}" - }" = - mkHost { - inherit host; - rice = - if rice == null then (if (host.rice or null) == null then null else rices.${host.rice}) else rice; - }; - }; - in - lib.concatMapAttrs (mkHostAttrs null null) hosts - // lib.concatMapAttrs (riceName: rice: lib.concatMapAttrs (mkHostAttrs riceName rice) hosts) rices; - in - configurations - ); -} diff --git a/lib/configurations/host.nix b/lib/configurations/host.nix deleted file mode 100644 index 3afeaa8..0000000 --- a/lib/configurations/host.nix +++ /dev/null @@ -1,123 +0,0 @@ -{ - delib, - apply, - myconfigName, - config, - currentHostName, - useHomeManagerModule, - homeManagerUser, - ... -}: -{ - host = - { - name, - myconfig ? { }, - nixos ? { }, - home ? { }, - darwin ? { }, - shared ? { }, - # to avoid overwriting - # useHomeManagerModule ? null, - # homeManagerUser ? null, - # homeManagerSystem ? null, - # rice ? null, - ... - }@args: - { - imports = [ - ( - { config, ... }: - let - sharedDefaults = - { - myconfig ? { }, - nixos ? { }, - home ? { }, - darwin ? { }, - }: - { - inherit - myconfig - nixos - home - darwin - ; - }; - _shared = sharedDefaults shared; - - wrap = - x: - if builtins.typeOf x == "lambda" then - x { - inherit name; - cfg = config.${myconfigName}.hosts.${name}; - myconfig = config.${myconfigName}; - } - else - x; - in - { - config.${myconfigName}.hosts.${name} = args // { - myconfig = wrap myconfig; - nixos = wrap nixos; - home = wrap home; - darwin = wrap darwin; - shared = { - myconfig = wrap _shared.myconfig; - nixos = wrap _shared.nixos; - home = wrap _shared.home; - darwin = wrap _shared.darwin; - }; - }; - - imports = - (apply.listOfEverything (wrap _shared.myconfig) (wrap _shared.nixos) (wrap _shared.home) ( - wrap _shared.darwin - )) - ++ ( - if currentHostName == name then - apply.listOfEverything (wrap myconfig) (wrap nixos) (wrap home) (wrap darwin) - else - [ ] - ); - } - ) - ]; - }; - - # TODO: get config through `hostSubmoduleOption = config: ...` - hostSubmoduleOptions = with delib.options; { - name = noDefault (strOption null); - - useHomeManagerModule = boolOption useHomeManagerModule; - homeManagerUser = noNullDefault (strOption homeManagerUser); - homeManagerSystem = description (noDefault (strOption null)) "Passed to the `homeManagerConfiguration` as `nixpkgs.legacyPackages.`"; - - myconfig = attrsOption { }; - nixos = attrsOption { }; - home = attrsOption { }; - darwin = attrsOption { }; - - shared = { - myconfig = attrsOption { }; - nixos = attrsOption { }; - home = attrsOption { }; - darwin = attrsOption { }; - }; - - rice = allowNull (enumOption (builtins.attrNames (config.${myconfigName}.rices or { })) null); - }; - - hostOption = host: with delib.options; noDefault (submoduleOption host null); - hostsOption = host: with delib.options; attrsOfOption (submodule host) { }; - - hostNamesAssertions = - hosts: - builtins.attrValues ( - builtins.mapAttrs (name: value: { - assertion = (builtins.hashString "sha256" name) == (builtins.hashString "sha256" value.name); - message = "The hosts attribute '${name}' does not match the value of hosts.'${name}'.name (${value.name})"; - }) hosts - ); -} diff --git a/lib/configurations/module.nix b/lib/configurations/module.nix deleted file mode 100644 index aa683bb..0000000 --- a/lib/configurations/module.nix +++ /dev/null @@ -1,72 +0,0 @@ -{ - delib, - lib, - apply, - myconfigName, - ... -}: -{ - module = - { - name, - options ? { }, - myconfig ? { }, - nixos ? { }, - home ? { }, - darwin ? { }, - }: - { - imports = [ - ( - { config, ... }: - let - cfg = delib.attrset.getAttrByStrPath name config.${myconfigName} { }; - - wrap = - object: - if builtins.typeOf object == "lambda" then - object { - inherit name cfg; - myconfig = config.${myconfigName}; - } - else - object; - - defaults = - { - ifEnabled ? { }, - ifDisabled ? { }, - always ? { }, - }: - { - inherit ifEnabled ifDisabled always; - }; - - _myconfig = defaults myconfig; - _nixos = defaults nixos; - _home = defaults home; - _darwin = defaults darwin; - - # If the `cfg.enable` option is missing, do not import ifEnabled or ifDisabled. - enabled = delib.attrset.getAttrByStrPath "enable" cfg false; - # Not `disabled = !enabled`, because it behaves differently when the 'enable' option is missing. - disabled = !(delib.attrset.getAttrByStrPath "enable" cfg true); - in - { - options.${myconfigName} = wrap options; - - imports = - (apply.listOfEverything (wrap _myconfig.always) (wrap _nixos.always) (wrap _home.always) ( - wrap _darwin.always - )) - ++ (apply.listOfEverything (lib.mkIf enabled (wrap _myconfig.ifEnabled)) (lib.mkIf enabled ( - wrap _nixos.ifEnabled - )) (lib.mkIf enabled (wrap _home.ifEnabled)) (lib.mkIf enabled (wrap _darwin.ifEnabled))) - ++ (apply.listOfEverything (lib.mkIf disabled (wrap _myconfig.ifDisabled)) (lib.mkIf disabled ( - wrap _nixos.ifDisabled - )) (lib.mkIf disabled (wrap _home.ifDisabled)) (lib.mkIf disabled (wrap _darwin.ifDisabled))); - } - ) - ]; - }; -} diff --git a/lib/configurations/rice.nix b/lib/configurations/rice.nix deleted file mode 100644 index a702442..0000000 --- a/lib/configurations/rice.nix +++ /dev/null @@ -1,68 +0,0 @@ -{ - delib, - myconfigName, - ... -}: -{ - rice = - { - name, - inherits ? [ ], - inheritanceOnly ? false, - myconfig ? { }, - nixos ? { }, - home ? { }, - darwin ? { }, - ... - }@args: - { - imports = [ - ( - { config, ... }: - let - wrap = - x: - if builtins.typeOf x == "lambda" then - x { - inherit name; - cfg = config.${myconfigName}.rices.${name}; - myconfig = config.${myconfigName}; - } - else - x; - in - { - config.${myconfigName}.rices.${name} = args // { - myconfig = wrap myconfig; - nixos = wrap nixos; - home = wrap home; - darwin = wrap darwin; - }; - } - ) - ]; - }; - - riceSubmoduleOptions = with delib.options; { - name = strOption null; - inherits = listOfOption str [ ]; - inheritanceOnly = boolOption false; - - myconfig = attrsOption { }; - nixos = attrsOption { }; - home = attrsOption { }; - darwin = attrsOption { }; - }; - - riceOption = rice: with delib.options; allowNull (submoduleOption rice null); - ricesOption = rice: with delib.options; attrsOfOption (submodule rice) { }; - - riceNamesAssertions = - rices: - builtins.attrValues ( - builtins.mapAttrs (name: value: { - assertion = (builtins.hashString "sha256" name) == (builtins.hashString "sha256" value.name); - message = "The rices attribute '${name}' does not match the value of rices.'${name}'.name (${value.name})"; - }) rices - ); -} diff --git a/lib/default.nix b/lib/default.nix index 54f154d..0376db1 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -1,63 +1,205 @@ { lib, - nixpkgs, - home-manager, - nix-darwin, + inputs, + denixModules, ... }: let - inherit (lib.fix (delib: import ./fixed-points.nix { inherit delib lib; })) + inherit (import ./toplevel/lib.nix { inherit lib; }) mkLib; +in +mkLib "delib" (delib: { + _callLibArgs = { + inherit inputs denixModules; + }; + + fixedPoints = delib._callLib ./toplevel/fixed-points.nix; + inherit (delib.fixedPoints) + fix + fixWithUnfix + recursivelyExtends + recursivelyComposeExtensions + recursivelyComposeManyExtensions makeRecursivelyExtensible + makeRecursivelyExtensibleWithCustomName ; -in -makeRecursivelyExtensible ( - delib: - let - inherit (delib) _callLib; - in - { - _callLib = file: import file delib._callLibArgs; - - _callLibArgs = { - inherit - delib - lib - nixpkgs - home-manager - nix-darwin - ; - }; - - attrset = _callLib ./attrset.nix; - inherit (delib.attrset) getAttrByStrPath setAttrByStrPath hasAttrs; - - inherit (_callLib ./configurations) configurations; - - inherit (_callLib ./fixed-points.nix) - fix - fixWithUnfix - recursivelyExtends - recursivelyComposeExtensions - recursivelyComposeManyExtensions - makeRecursivelyExtensible - makeRecursivelyExtensibleWithCustomName - ; - - inherit (_callLib ./maintainers.nix) maintainers; - - options = _callLib ./options.nix; - - inherit (_callLib ./extension.nix) - extension - extensions - callExtension - callExtensions - withExtensions - mergeExtensions - ; - - inherit (_callLib ./umport.nix) umport; - } - // (import ./options.nix { inherit delib lib; }) - # After implementing https://github.com/NixOS/nix/issues/4090 it will be possible to use `// callLib` (to inherit all) -) + + inherit (delib._callLib ./toplevel/lib.nix) mkLib; + + modules = delib._callLib ./modules; + inherit (delib.modules) + denixConfiguration + genModule + genModules + genSystem + genSystems + module + host + toDenixArgs + isDenixArgs + callIfDenixArgs + callWithMocksIfDenixArgs + processModule + processModuleWithDenixArgs + setDefaultModuleLocation + setDefaultModuleLocationWithDenixArgs + processModuleAndGenerateDenixArgs + ; + + attrset = delib._callLib ./attrset.nix; + inherit (delib.attrset) + getAttrByStrPath + setAttrByStrPath + hasAttrs + keepAttrs + strictMergeAttrs + removeAttrs + ; + + functions = delib._callLib ./functions.nix; + inherit (delib.functions) + functionArgs + setFunctionArgs + mirrorFunctionArgs + inheritFunctionArgs + callWithMocks + ; + + options = delib._callLib ./options.nix; + + types = delib._callLib ./types.nix; + + inherit (delib._callLib ./umport.nix) umport; + + # Generated inherits. + # After implementing https://github.com/NixOS/nix/issues/4090 it will be possible to use `// delib.options` (to inherit all) + + #[[[cog + # groups = ["options", "types"] + # + # import cog + # import subprocess + # import json + # import os + # + # def nix_attr_names(attr): + # try: + # out = subprocess.run( + # ["nix", "eval", f".#lib.{attr}", + # "--apply", "builtins.attrNames", + # "--quiet", "--json", "--no-pretty"], + # capture_output=True, text=True, check=True + # ).stdout + # except subprocess.CalledProcessError: + # out = os.environ[f"pre_evaled_{attr}"] + # return json.loads(out) + # + # for group in groups: + # cog.outl(f"inherit (delib.{group})") + # for name in nix_attr_names(group): + # cog.outl(f" {name}") + # cog.outl(" ;") + #]]] + inherit (delib.options) + allowAnything + allowAttrs + allowAttrsLegacy + allowAttrsOf + allowBool + allowCoercedTo + allowEither + allowEnum + allowFloat + allowFunction + allowFunctionTo + allowInt + allowIntBetween + allowLazyAttrs + allowLazyAttrsOf + allowList + allowListOf + allowNull + allowNumber + allowOneOf + allowPackage + allowPath + allowPort + allowSingleLineStr + allowSteppedInt + allowSteppedIntBetween + allowStr + allowSubmodule + allowSubmoduleWith + allowUnspecified + anythingOption + apply + attrsLegacyOption + attrsOfOption + attrsOption + boolOption + coercedToOption + defaultText + description + eitherOption + enumOption + example + floatOption + functionOption + functionToOption + intBetweenOption + intOption + internal + lazyAttrsOfOption + lazyAttrsOption + listOfOption + listOption + nullOption + numberOption + oneOfOption + packageOption + pathOption + portOption + readOnly + relatedPackages + singleLineStrOption + steppedIntBetweenOption + steppedIntOption + strOption + submoduleOption + submoduleWithOption + unspecifiedOption + visible + ; + inherit (delib.types) + anything + attrs + attrsLegacy + attrsOf + bool + coercedTo + either + enum + float + function + functionTo + int + intBetween + lazyAttrs + lazyAttrsOf + list + listOf + null + number + oneOf + package + path + port + singleLineStr + steppedInt + steppedIntBetween + str + submodule + submoduleWith + unspecified + ; + #[[[end]]] +}) diff --git a/lib/extension.nix b/lib/extension.nix deleted file mode 100644 index eb8d539..0000000 --- a/lib/extension.nix +++ /dev/null @@ -1,183 +0,0 @@ -{ - delib, - lib, - ... -}: -let - pretty = - value: - (lib.generators.toPretty { } ( - lib.generators.withRecursion { - depthLimit = 10; - throwOnDepthLimit = false; - } value - )); - toExtensionWithConfig = - f: - if lib.isFunction f then - config: final: prev: - let - fConfig = f config; - in - if lib.isFunction fConfig then - let - fConfigPrev = f config prev; - in - if lib.isFunction fConfigPrev then - # f is (config: final: prev: { ... }) - f config final prev - else - # f is (config: prev: { ... }) - fConfigPrev - else - # f is (config: { ... }) - fConfig - else - # f is not a function; probably { ... } - config: final: prev: - f; -in -{ - extension = - { - # Meta - name, - description ? null, - maintainers ? [ ], - # Extension - config ? final: prev: { }, - initialConfig ? null, - configOrder ? 0, # Used in mergeExtensions. Lower values mean earlier execution. - # Configuration - libExtension ? - config: final: prev: - { }, - libExtensionOrder ? 0, # Used in mergeExtensions. Lower values mean earlier execution. - modules ? config: [ ], - }: - let - _initialConfig = initialConfig; - f = - _config: - let - config = lib.toExtension _config; - initialConfig = - if _initialConfig == null || lib.isFunction _initialConfig then - _initialConfig - else - _: _initialConfig; - fixedConfig = delib.fix ( - delib.recursivelyExtends config (if initialConfig != null then initialConfig else _: { }) - ); - in - { - inherit - name - description - maintainers - initialConfig - configOrder - libExtensionOrder - ; - - libExtension = (toExtensionWithConfig libExtension) fixedConfig; - modules = if lib.isFunction modules then modules fixedConfig else modules; - - __unfix__ = f; - __unfixConfig__ = config; - config = fixedConfig; - withConfig = - configOverlay: f (delib.recursivelyComposeExtensions config (lib.toExtension configOverlay)); - }; - in - f config; - - extensions = delib.callExtensions { paths = [ ./extensions ]; }; - - callExtension = file: delib._callLib file // { _file = file; }; - - callExtensions = - { - # Umport - paths ? [ ], - exclude ? [ ], - recursive ? true, - }: - let - allExtensions = map delib.callExtension (delib.umport { inherit paths exclude recursive; }); - groupedByName = builtins.groupBy (extension: extension.name) allExtensions; - in - lib.mapAttrs delib.mergeExtensions groupedByName; - - withExtensions = lib.foldl (acc: extension: acc.recursivelyExtend extension.libExtension) delib; - - mergeExtensions = - name: extensions: - let - totalExtensions = builtins.length extensions; - in - if totalExtensions == 0 then - delib.extension { inherit name; } - else if totalExtensions == 1 then - builtins.removeAttrs (builtins.elemAt extensions 0) [ "_file" ] - else - let - config = - let - sorted = builtins.sort (q: p: q.configOrder < p.configOrder) extensions; - configs = builtins.map (x: x.__unfixConfig__) sorted; - in - delib.recursivelyComposeManyExtensions configs; - in - delib.extension { - inherit name; - description = - let - withDescription = builtins.filter (extension: extension.description != null) extensions; - withUniqueDescription = lib.foldl' ( - acc: e: - if builtins.any (extension: e.description == extension.description) acc then acc else acc ++ [ e ] - ) [ ] withDescription; - totalUnique = builtins.length withUniqueDescription; - in - if totalUnique == 0 then - null - else if totalUnique == 1 then - (builtins.head withDescription).description - else - lib.warn ( - "Denix extension with the name '${name}' has conflicting 'description' values:\n" - + (lib.concatMapStringsSep "\n" ( - extension: "- ${extension._file or extension.name}: ${pretty extension.description}" - ) withDescription) - ) null; - - inherit config; - initialConfig = - let - withInitialConfig = builtins.filter (extension: extension.initialConfig != null) extensions; - total = builtins.length withInitialConfig; - in - if total == 0 then - null - else if total == 1 then - (builtins.head withInitialConfig).initialConfig - else - lib.warn ( - "Denix extension with the name '${name}' has conflicting 'initialConfig' values:\n" - + (lib.concatMapStringsSep "\n" ( - extension: "- ${extension._file or extension.name}: ${pretty extension.initialConfig}" - ) withInitialConfig) - ) null; - - maintainers = lib.unique (builtins.concatMap (extension: extension.maintainers) extensions); - - libExtension = - let - sorted = builtins.sort (q: p: q.libExtensionOrder < p.libExtensionOrder) extensions; - libExtensions = config: builtins.map (extension: (extension.__unfix__ config).libExtension) sorted; - in - config: delib.recursivelyComposeManyExtensions (libExtensions config); - modules = config: builtins.concatMap (extension: (extension.__unfix__ config).modules) extensions; - }; -} diff --git a/lib/extensions/args.nix b/lib/extensions/args.nix deleted file mode 100644 index ff646fe..0000000 --- a/lib/extensions/args.nix +++ /dev/null @@ -1,51 +0,0 @@ -{ delib, ... }: -delib.extension { - name = "args"; - description = "More convenient way to configure `_module.args` via `myconfig`"; - maintainers = with delib.maintainers; [ yunfachi ]; - - config.path = "args"; - - modules = config: [ - ( - { delib, ... }: - delib.module { - name = config.path; - - options = - with delib; - setAttrByStrPath config.path { - shared = attrsLegacyOption { }; - nixos = attrsLegacyOption { }; - home = attrsLegacyOption { }; - darwin = attrsLegacyOption { }; - }; - - nixos.always = - { cfg, ... }: - { - imports = [ - { _module.args = cfg.shared; } - { _module.args = cfg.nixos; } - ]; - }; - home.always = - { cfg, ... }: - { - imports = [ - { _module.args = cfg.shared; } - { _module.args = cfg.home; } - ]; - }; - darwin.always = - { cfg, ... }: - { - imports = [ - { _module.args = cfg.shared; } - { _module.args = cfg.darwin; } - ]; - }; - } - ) - ]; -} diff --git a/lib/extensions/base/default.nix b/lib/extensions/base/default.nix deleted file mode 100644 index 3c3ceaa..0000000 --- a/lib/extensions/base/default.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ delib, ... }: -delib.extension { - name = "base"; - description = "Implement feature-rich and fine-tunable modules for hosts and rices with minimal effort"; - maintainers = with delib.maintainers; [ yunfachi ]; - - initialConfig = { - enableAll = true; - - args = { - enable = false; - path = "args"; - }; - - assertions = { - enable = true; - moduleSystem = "home-manager"; - }; - }; -} diff --git a/lib/extensions/base/hosts.nix b/lib/extensions/base/hosts.nix deleted file mode 100644 index 6b70e0a..0000000 --- a/lib/extensions/base/hosts.nix +++ /dev/null @@ -1,170 +0,0 @@ -{ - delib, - lib, - ... -}: -delib.extension { - name = "base"; - - config = final: prev: { - hosts = { - enable = final.enableAll; - inherit (final) args assertions; - - type = { - enable = true; - generateIsType = true; - types = [ - "desktop" - "server" - ]; - }; - - features = { - enable = true; - generateIsFeatured = true; - features = [ ]; - default = [ ]; - defaultByHostType = { }; - }; - - displays = { - enable = true; - # TODO: hyprland = {enable = false; moduleSystem = "home";}; ... - }; - - extraSubmodules = [ ]; - }; - }; - - libExtension = - extensionConfig: final: prev: with final; { - generateHostType = - { - hostConfig, - generateIsType ? extensionConfig.hosts.type.generateIsType, - types ? extensionConfig.hosts.type.types, - }: - { - type = noDefault (enumOption types null); - } - // lib.optionalAttrs generateIsType ( - builtins.listToAttrs ( - map (type: { - name = - let - chars = lib.stringToCharacters type; - in - "is${lib.toUpper (lib.head chars) + lib.concatStrings (lib.tail chars)}"; - value = boolOption (hostConfig.type == type); - }) types - ) - ); - - generateHostFeatures = - { - hostConfig, - generateIsFeatured ? extensionConfig.hosts.features.generateIsFeatured, - features ? extensionConfig.hosts.features.features, - default ? extensionConfig.hosts.features.default, - defaultByHostType ? extensionConfig.hosts.features.defaultByHostType, - }: - { - defaultFeatures = listOfOption (enum features) ( - default ++ (defaultByHostType.${hostConfig.type} or [ ]) - ); - features = listOfOption (enum features) [ ]; - } - // lib.optionalAttrs generateIsFeatured ( - builtins.listToAttrs ( - map (feature: { - name = "${feature}Featured"; - value = boolOption (builtins.elem feature (hostConfig.features ++ hostConfig.defaultFeatures)); - }) features - ) - ); - - generateHostDisplays = - { hostConfig }: - { - displays = listOfOption (submodule { - options = { - enable = boolOption true; - - name = noDefault (strOption null); - primary = boolOption (builtins.length hostConfig.displays == 1); - touchscreen = boolOption false; - - refreshRate = intOption 60; - width = intOption 1920; - height = intOption 1080; - x = intOption 0; - y = intOption 0; - }; - }) [ ]; - }; - }; - - modules = - extensionConfig: - lib.optionals extensionConfig.hosts.enable [ - ( - { delib, ... }: - let - assertionsConfig = - { myconfig, ... }: - { - assertions = delib.hostNamesAssertions myconfig.hosts; - }; - assertionsModuleSystem = - { - nixos = "nixos"; - home-manager = "home"; - nix-darwin = "darwin"; - } - .${extensionConfig.hosts.assertions.moduleSystem} or extensionConfig.hosts.assertions.moduleSystem; - in - delib.module ( - { - name = "hosts"; - - options = - with delib; - let - host = - lib.singleton ( - { config, ... }: - { - options = - hostSubmoduleOptions - // delib.generateHostType { hostConfig = config; } - // delib.generateHostFeatures { hostConfig = config; } - // delib.generateHostDisplays { hostConfig = config; }; - } - ) - ++ extensionConfig.hosts.extraSubmodules; - in - { - host = hostOption host; - hosts = hostsOption host; - }; - - myconfig.always = - { myconfig, ... }: - lib.optionalAttrs extensionConfig.hosts.args.enable ( - delib.setAttrByStrPath extensionConfig.hosts.args.path { - shared = { inherit (myconfig) host hosts; }; - } - ) - // lib.optionalAttrs (assertionsModuleSystem == "myconfig") ( - lib.optionalAttrs extensionConfig.hosts.assertions.enable assertionsConfig - ); - } - // (lib.optionalAttrs (assertionsModuleSystem != "myconfig") { - ${assertionsModuleSystem}.always = - lib.optionalAttrs extensionConfig.hosts.assertions.enable assertionsConfig; - }) - ) - ) - ]; -} diff --git a/lib/extensions/base/rices.nix b/lib/extensions/base/rices.nix deleted file mode 100644 index 87c96d5..0000000 --- a/lib/extensions/base/rices.nix +++ /dev/null @@ -1,73 +0,0 @@ -{ - delib, - lib, - ... -}: -delib.extension { - name = "base"; - - config = final: prev: { - rices = { - enable = final.enableAll; - inherit (final) args assertions; - - extraSubmodules = [ ]; - }; - }; - - modules = - extensionConfig: - lib.optionals extensionConfig.rices.enable [ - ( - { delib, ... }: - let - assertionsConfig = - { myconfig, ... }: - { - assertions = delib.riceNamesAssertions myconfig.rices; - }; - assertionsModuleSystem = - { - nixos = "nixos"; - home-manager = "home"; - nix-darwin = "darwin"; - } - .${extensionConfig.rices.assertions.moduleSystem} or extensionConfig.rices.assertions.moduleSystem; - in - delib.module ( - { - name = "rices"; - - options = - with delib; - let - rice = - lib.singleton { - options = riceSubmoduleOptions; - } - ++ extensionConfig.rices.extraSubmodules; - in - { - rice = riceOption rice; - rices = ricesOption rice; - }; - - myconfig.always = - { myconfig, ... }: - lib.optionalAttrs extensionConfig.rices.args.enable ( - delib.setAttrByStrPath extensionConfig.rices.args.path { - shared = { inherit (myconfig) rice rices; }; - } - ) - // lib.optionalAttrs (assertionsModuleSystem == "myconfig") ( - lib.optionalAttrs extensionConfig.rices.assertions.enable assertionsConfig - ); - } - // (lib.optionalAttrs (assertionsModuleSystem != "myconfig") { - ${assertionsModuleSystem}.always = - lib.optionalAttrs extensionConfig.rices.assertions.enable assertionsConfig; - }) - ) - ) - ]; -} diff --git a/lib/functions.nix b/lib/functions.nix new file mode 100644 index 0000000..3abc842 --- /dev/null +++ b/lib/functions.nix @@ -0,0 +1,42 @@ +{ delib, lib, ... }: +{ + functionArgs = + f: + if f ? __functionArgs then + f.__functionArgs + else if f ? __functor then + delib.functionArgs (f.__functor f) + else + builtins.functionArgs f; + + setFunctionArgs = + f: args: + if lib.isAttrs f then + f + // { + __functionArgs = args; + } + else + { + __functor = self: f; + __functionArgs = args; + }; + + mirrorFunctionArgs = + f: + let + fArgs = delib.functionArgs f; + in + g: delib.setFunctionArgs g fArgs; + + # TODO: https://github.com/NixOS/nixpkgs/pull/453578 + inheritFunctionArgs = + f: g: + let + fArgs = delib.functionArgs f; + gArgs = delib.functionArgs g; + in + delib.setFunctionArgs g (fArgs // gArgs); + + callWithMocks = f: f (delib.functionArgs f); +} diff --git a/lib/maintainers.nix b/lib/maintainers.nix deleted file mode 100644 index 2287ba4..0000000 --- a/lib/maintainers.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ - delib, - lib, - ... -}: -let - maintainer = - { - name, - email ? null, - github ? null, - githubId ? null, - telegram ? null, - telegramId ? null, - }@args: - lib.warnIf (!delib.hasAttrs [ "email" "githubId" "telegramId" ] args) - "Maintainer '${name}' is missing contact info: expected at least one of email, githubId, or telegramId'" - args; -in -{ - maintainers = { - yunfachi = maintainer { - name = "yunfachi"; - github = "yunfachi"; - githubId = 73419713; - telegram = "yunfachi"; - telegramId = 1349897307; - }; - }; -} diff --git a/lib/modules/default.nix b/lib/modules/default.nix new file mode 100644 index 0000000..c04c4f3 --- /dev/null +++ b/lib/modules/default.nix @@ -0,0 +1,216 @@ +{ + delib, + lib, + inputs, + denixModules, + ... +}: +delib._callLib ./denixArgs.nix +// delib._callLib ./helpers.nix +// delib._callLib ./options.nix +// delib._callLib ./helpers.nix +// { + denixConfiguration = + { + modules ? [ ], + specialArgs ? { }, + extraInputs ? { }, + }: + let + evaledModules = lib.evalModules { + modules = [ denixModules.default ] ++ modules; + + specialArgs = { + inputs = inputs // extraInputs; + # `specialArgs.modulesPath` is used as the base path for `disabledModules`. + modulesPath = denixModules.default; + } + // lib.recursiveUpdate { + inherit delib; + } specialArgs; + }; + + withExtraAttrs = + configuration: + configuration + // { + genModule = args: delib.genModule (delib.strictMergeAttrs { inherit configuration; } args); + genSystem = args: delib.genSystem (delib.strictMergeAttrs { inherit configuration; } args); + genModules = args: delib.genModules (delib.strictMergeAttrs { inherit configuration; } args); + genSystems = args: delib.genSystems (delib.strictMergeAttrs { inherit configuration; } args); + + extendModules = args: withExtraAttrs (configuration.extendModules args); + }; + in + withExtraAttrs evaledModules; + + genModule = + { + configuration, + + moduleSystem ? null, + host ? null, + }: + let + configurationWithModules = configuration.extendModules { + modules = lib.singleton { + config = { + inherit moduleSystem host; + }; + }; + }; + in + { + key = "denix.genModule"; + + imports = lib.concatLists ( + lib.mapAttrsToList ( + moduleSystemName: rawModules: + lib.concatMap + configurationWithModules.config.moduleSystems.${moduleSystemName}.applyConfigForModuleSystem + rawModules + ) configurationWithModules.config.rawModules + ); + }; + + genSystem = + { + configuration, + + moduleSystem, + host ? null, + + extraArgs ? { }, + extraModules ? [ ], + }: + let + makeSystem = configuration.config.moduleSystems.${moduleSystem}.makeSystem; + in + assert lib.assertMsg (makeSystem != null) + "The selected module system '${moduleSystem}' does not support making systems. See its 'makeSystem' option."; + makeSystem { + inherit extraArgs; + modules = extraModules ++ [ + (delib.modules.genModule { + inherit + configuration + moduleSystem + host + ; + }) + ]; + }; + + genModules = + { + configuration, + + moduleSystem ? null, + host ? null, + + forEachModuleSystem ? false, + moduleSystems ? builtins.attrNames ( + lib.filterAttrs (_: value: value.flakeOutputs.modules != null) configuration.config.moduleSystems + ), + forEachHost ? false, + hosts ? builtins.attrNames configuration.config.hosts, + }: + assert lib.assertMsg ( + forEachModuleSystem != (moduleSystem != null) + ) "'forEachModuleSystem' must be true or 'moduleSystem' must be set, but not both."; + assert lib.assertMsg ( + !(forEachHost && host != null) + ) "'forEachHost' must not be true when 'host' is set."; + let + genSingle = + moduleSystem: host: + delib.modules.genModule { + inherit + configuration + moduleSystem + host + ; + }; + + processModuleSystems = + if forEachModuleSystem then + lib.genAttrs' moduleSystems (moduleSystem: { + name = + let + flakeOutput = configuration.config.moduleSystems.${moduleSystem}.flakeOutputs.modules; + in + lib.throwIf (flakeOutput == null) + "The selected module system '${moduleSystem}' option 'flakeOutputs.modules' cannot be null." + flakeOutput; + value = processHosts moduleSystem; + }) + else + processHosts moduleSystem; + + processHosts = + moduleSystem: + if forEachHost then lib.genAttrs hosts (genSingle moduleSystem) else genSingle moduleSystem host; + in + processModuleSystems; + + genSystems = + { + configuration, + + moduleSystem ? null, + host ? null, + + forEachModuleSystem ? false, + moduleSystems ? builtins.attrNames ( + lib.filterAttrs ( + _: value: value.flakeOutputs.systems != null && value.makeSystem != null + ) configuration.config.moduleSystems + ), + forEachHost ? false, + hosts ? builtins.attrNames configuration.config.hosts, + + extraArgs ? { }, + extraArgsByModuleSystem ? { }, + extraModules ? [ ], + extraModulesByModuleSystem ? { }, + }: + assert lib.assertMsg ( + forEachModuleSystem != (moduleSystem != null) + ) "'forEachModuleSystem' must be true or 'moduleSystem' must be set, but not both."; + assert lib.assertMsg ( + !(forEachHost && host != null) + ) "'forEachHost' must not be true when 'host' is set."; + let + genSingle = + moduleSystem: host: + delib.modules.genSystem { + inherit + configuration + moduleSystem + host + ; + extraArgs = extraArgs // extraArgsByModuleSystem.${moduleSystem} or { }; + extraModules = extraModules ++ extraModulesByModuleSystem.${moduleSystem} or [ ]; + }; + + processModuleSystems = + if forEachModuleSystem then + lib.genAttrs' moduleSystems (moduleSystem: { + name = + let + flakeOutput = configuration.config.moduleSystems.${moduleSystem}.flakeOutputs.systems; + in + lib.throwIf (flakeOutput == null) + "The selected module system '${moduleSystem}' option 'flakeOutputs.systems' cannot be null." + flakeOutput; + value = processHosts moduleSystem; + }) + else + processHosts moduleSystem; + + processHosts = + moduleSystem: + if forEachHost then lib.genAttrs hosts (genSingle moduleSystem) else genSingle moduleSystem host; + in + processModuleSystems; +} diff --git a/lib/modules/denixArgs.nix b/lib/modules/denixArgs.nix new file mode 100644 index 0000000..998f865 --- /dev/null +++ b/lib/modules/denixArgs.nix @@ -0,0 +1,25 @@ +{ delib, lib, ... }: +{ + toDenixArgs = + function: + if !delib.isDenixArgs function then + if lib.isAttrs function then + function + // { + _type = "denixAbstractionArgsFunctor"; + } + else + { + _type = "denixAbstractionArgsFunctor"; + __functor = self: function; + # __functionArgs = delib.functionArgs function; + } + else + function; + + isDenixArgs = x: x._type or null == "denixAbstractionArgsFunctor"; + + callIfDenixArgs = x: denixArgs: if delib.isDenixArgs x then x denixArgs else x; + + callWithMocksIfDenixArgs = x: if delib.isDenixArgs x then delib.callWithMocks x else x; +} diff --git a/lib/modules/helpers.nix b/lib/modules/helpers.nix new file mode 100644 index 0000000..43c2d27 --- /dev/null +++ b/lib/modules/helpers.nix @@ -0,0 +1,81 @@ +{ delib, lib, ... }: +{ + processModule = + f: m: + if !lib.isFunction m then + f m + else if !lib.isFunction (delib.callWithMocks f) then + delib.mirrorFunctionArgs m (args: f (m args)) + else + delib.inheritFunctionArgs m ( + delib.mirrorFunctionArgs (delib.callWithMocks f) (args: f (m args) args) + ); + + processModuleAndGenerateDenixArgs = + f: m: denixArgs: + if !delib.isDenixArgs m then + delib.processModule f m + else + delib.processModule f ( + if !lib.isFunction denixArgs then + m denixArgs + else if !lib.isFunction (delib.callWithMocks m) then + delib.mirrorFunctionArgs denixArgs (args: m (denixArgs args)) + else + delib.inheritFunctionArgs (delib.callWithMocks m) ( + delib.mirrorFunctionArgs denixArgs (args: m (denixArgs args) args) + ) + ); + + processModuleWithDenixArgs = + f: m: + delib.mirrorFunctionArgs m (delib.toDenixArgs (denixArgs: delib.processModule f (m denixArgs))); + + setDefaultModuleLocation = file: m: delib.processModule (module: { _file = file; } // module) m; + + setDefaultModuleLocationWithDenixArgs = + file: m: delib.processModuleWithDenixArgs (module: { _file = file; } // module) m; + + addPrefixToModule = + prefix: m: + let + type = m._type or "module"; + addPrefix = lib.setAttrByPath prefix; + in + if type == "module" && (m ? config || m ? options) then + m + // lib.optionalAttrs ((m.options or { }) != { }) { + options = addPrefix m.options; + } + // lib.optionalAttrs ((m.config or { }) != { }) { + config = addPrefix m.config; + } + else if type == "module" then + let + names = [ + "_class" + "_file" + "key" + "disabledModules" + "require" + "imports" + "freeformType" + ]; + in + delib.keepAttrs m names + // lib.optionalAttrs (builtins.removeAttrs m names != { }) { + config = addPrefix (builtins.removeAttrs m names); + } + else if type == "merge" then + m + // lib.optionalAttrs ((m.contents or [ ]) != [ ]) { + contents = map (delib.modules.addPrefixToModule prefix) m.contents; + } + else if type == "if" || type == "override" then + m + // lib.optionalAttrs ((m.content or { }) != { }) { + content = addPrefix m.content; + } + else + throw "denix.lib.modules.addPrefixToModule: passed module does not look like a module."; +} diff --git a/lib/modules/options.nix b/lib/modules/options.nix new file mode 100644 index 0000000..4970808 --- /dev/null +++ b/lib/modules/options.nix @@ -0,0 +1,257 @@ +{ delib, lib, ... }: +{ + coercedListOfModules = lib.mkOptionType { + name = "listOfModules"; + check = x: lib.isList x || lib.isAttrs x || lib.isFunction x || lib.path.check x; + merge = + loc: defs: + lib.concatMap ( + def: + map (delib.setDefaultModuleLocation "${def.file}, via option ${lib.showOption loc}") ( + lib.toList def.value + ) + ) defs; + emptyValue.value = [ ]; + }; + + coercedListOfModulesWithDenixArgs = lib.mkOptionType { + name = "listOfModulesWithDenixArgs"; + check = x: lib.isList x || lib.isAttrs x || lib.isFunction x || lib.path.check x; + merge = + loc: defs: + lib.concatMap ( + def: + map ( + ( + if delib.isDenixArgs def.value then + delib.setDefaultModuleLocationWithDenixArgs + else + delib.setDefaultModuleLocation + ) + "${def.file}, via option ${lib.showOption loc}" + ) (lib.toList def.value) + ) defs; + emptyValue.value = [ ]; + }; + + coercedListOfModulesOption = lib.mkOption { + type = delib.modules.coercedListOfModules; + default = [ ]; + }; + + coercedListOfModulesWithDenixArgsOption = lib.mkOption { + type = delib.modules.coercedListOfModulesWithDenixArgs; + default = [ ]; + }; + + denixAbstractionType = + { + moduleSystems, + withOptions ? false, + extraModules ? [ ], + }: + with delib; + coercedTo (either (functionTo attrs) attrs) + (module: { + config = + if !lib.isFunction module then + module + else + let + calledWithMocks = delib.callWithMocks module; + + definedModuleSystems = lib.intersectLists (builtins.attrNames calledWithMocks) ( + builtins.attrNames moduleSystems + ); + + eachToDenixArgs = + fn: + if builtins.isList (fn calledWithMocks) then + lib.imap0 ( + index: value: + delib.toDenixArgs ( + if delib.isDenixArgs (fn calledWithMocks) then + delib.inheritFunctionArgs (fn calledWithMocks) ( + delib.mirrorFunctionArgs module ( + denixArgs: (builtins.elemAt (fn (module denixArgs)) index) denixArgs + ) + ) + else + delib.mirrorFunctionArgs module (denixArgs: builtins.elemAt (fn (module denixArgs)) index) + ) + ) (fn calledWithMocks) + else + delib.toDenixArgs ( + if delib.isDenixArgs (fn calledWithMocks) then + delib.inheritFunctionArgs (fn calledWithMocks) ( + delib.mirrorFunctionArgs module (denixArgs: fn (module denixArgs) denixArgs) + ) + else + delib.mirrorFunctionArgs module (denixArgs: fn (module denixArgs)) + ); + + in + calledWithMocks + // lib.genAttrs definedModuleSystems ( + moduleSystem: + calledWithMocks.${moduleSystem} + // lib.genAttrs (lib.intersectLists (builtins.attrNames calledWithMocks.${moduleSystem}) [ + "always" + "ifEnabled" + "ifDisabled" + ]) (condition: eachToDenixArgs (x: x.${moduleSystem}.${condition})) + ) + // lib.optionalAttrs (withOptions && calledWithMocks ? options) { + options = eachToDenixArgs (x: x.options); + }; + }) + (submoduleWith { + modules = extraModules ++ [ + ( + { name, ... }: + { + options = + builtins.mapAttrs (name: value: { + always = delib.modules.coercedListOfModulesWithDenixArgsOption; + ifEnabled = delib.modules.coercedListOfModulesWithDenixArgsOption; + ifDisabled = delib.modules.coercedListOfModulesWithDenixArgsOption; + }) moduleSystems + // lib.optionalAttrs withOptions { + options = delib.modules.coercedListOfModulesWithDenixArgsOption; + } + // { + name = readOnly (strOption name); + __toString = readOnly (functionToOption str (self: self.name)); + }; + } + ) + ]; + }); + + denixConfigurationSubmodule = + { + modules ? [ ], + ... + }@attrs: + let + noCheckForDocsModule = { + # When generating documentation, our goal isn't to check anything. + # Quite the opposite in fact. Generating docs is somewhat of a + # challenge, evaluating modules in a *lacking* context. Anything + # that makes the docs avoid an error is a win. + config._module.check = lib.mkForce false; + _file = ""; + }; + checkDefsForError = + check: loc: defs: + let + invalidDefs = lib.filter (def: !check def.value) defs; + in + if invalidDefs != [ ] then + { message = "Definition values: ${lib.options.showDefs invalidDefs}"; } + else + null; + + base = delib.denixConfiguration ( + attrs + // { + inherit modules; + } + ); + + freeformType = base._module.freeformType; + + name = "denixConfiguration"; + + check = { + __functor = _self: x: lib.isAttrs x || lib.isFunction x || lib.path.check x; + isV2MergeCoherent = true; + }; + in + lib.mkOptionType { + inherit name; + description = + let + docsEval = base.extendModules { modules = [ noCheckForDocsModule ]; }; + in + if docsEval._module.freeformType ? description then + "open ${name} of ${ + lib.types.optionDescriptionPhrase ( + class: class == "noun" || class == "composite" + ) docsEval._module.freeformType + }" + else + name; + inherit check; + merge = { + __functor = + self: loc: defs: + (self.v2 { inherit loc defs; }).value; + v2 = + { loc, defs }: + let + configuration = base.extendModules { + modules = map ( + { value, file }: + { + _file = file; + imports = [ value ]; + } + ) defs; + prefix = loc; + }; + in + { + headError = checkDefsForError check loc defs; + value = configuration.config; + valueMeta = { inherit configuration; }; + }; + }; + emptyValue = { + value = { }; + }; + getSubOptions = + prefix: + let + docsEval = ( + base.extendModules { + inherit prefix; + modules = [ noCheckForDocsModule ]; + } + ); + # Intentionally shadow the freeformType from the possibly *checked* + # configuration. See `noCheckForDocsModule` comment. + inherit (docsEval._module) freeformType; + in + docsEval.options + // lib.optionalAttrs (freeformType != null) { + # Expose the sub options of the freeform type. Note that the option + # discovery doesn't care about the attribute name used here, so this + # is just to avoid conflicts with potential options from the submodule + _freeformOptions = freeformType.getSubOptions prefix; + }; + getSubModules = modules; + substSubModules = + m: + delib.modules.denixConfigurationSubmodule ( + attrs + // { + modules = m; + } + ); + nestedTypes = lib.optionalAttrs (freeformType != null) { + freeformType = freeformType; + }; + functor = lib.defaultFunctor name // { + type = delib.modules.denixConfigurationSubmodule; + payload = attrs; + binOp = lhs: rhs: throw "ты долбаеб"; + }; + }; + + denixConfigurationSubmoduleOption = + args: + lib.mkOption { + type = delib.modules.denixConfigurationSubmodule args; + }; +} diff --git a/lib/modules/wrappers.nix b/lib/modules/wrappers.nix new file mode 100644 index 0000000..b0875f1 --- /dev/null +++ b/lib/modules/wrappers.nix @@ -0,0 +1,19 @@ +{ delib, lib, ... }: +let + mkWrapper = + field: obj: + if lib.isFunction obj then + { + config.${field}.${(delib.callWithMocks obj).name} = delib.mirrorFunctionArgs obj ( + args: builtins.removeAttrs (obj args) [ "name" ] + ); + } + else + { + config.${field}.${obj.name} = builtins.removeAttrs obj [ "name" ]; + }; +in +{ + module = mkWrapper "modules"; + host = mkWrapper "hosts"; +} diff --git a/lib/options.nix b/lib/options.nix index eef8586..2607eee 100644 --- a/lib/options.nix +++ b/lib/options.nix @@ -1,117 +1,73 @@ -{ - delib, - lib, - ... -}: +{ delib, lib, ... }: let - mkOption = - default: types: - lib.mkOption { - type = lib.types.oneOf (lib.toList types); - inherit default; - }; + genOptions = f: lib.listToAttrs (map f (lib.attrsToList delib.types)); - addTypeToOption = - type: option: - option - // { - type = lib.types.oneOf [ - option.type - type - ]; - }; - addTypeWithParameterToOption = - type: elemType: option: - addTypeToOption (type elemType) option; + functorForType = type: { + __functor = + option: + if lib.isFunction type then + typeArg: option // { type = type typeArg; } // functorForType (type typeArg) + else + default: option // { inherit default; }; + }; - simpleOption = type: default: mkOption default type; - simpleOptionWithParameter = - type: elemType: default: - simpleOption (type elemType) default; -in -rec { - # Types - inherit (lib.types) - anything - attrsOf - bool - coercedTo - enum - float - int - listOf - number - package - path - port - singleLineStr - str - submodule - ; - attrs = attrsOf anything; - attrsLegacy = lib.types.attrs; - lambda = lib.types.functionTo anything; - lambdaTo = lib.types.functionTo; - list = listOf anything; - - # Options - anythingOption = simpleOption anything; - attrsLegacyOption = simpleOption attrsLegacy; - attrsOfOption = simpleOptionWithParameter attrsOf; - attrsOption = simpleOption attrs; - boolOption = simpleOption bool; - coercedToOption = - coercedType: coerceFunc: finalType: - simpleOption (coercedTo coercedType coerceFunc finalType); - enumOption = simpleOptionWithParameter enum; - floatOption = simpleOption float; - intOption = simpleOption int; - lambdaOption = simpleOption lambda; - lambdaToOption = simpleOptionWithParameter lambdaTo; - listOfOption = simpleOptionWithParameter listOf; - listOption = simpleOption list; - numberOption = simpleOption number; - packageOption = simpleOption package; - pathOption = simpleOption path; - portOption = simpleOption port; - singleLineStrOption = simpleOption singleLineStr; - strOption = simpleOption str; - submoduleOption = simpleOptionWithParameter submodule; + typeOptions = genOptions ( + { name, value }: + { + name = "${name}Option"; + value = + lib.mkOption { + type = value; + } + // functorForType value; + } + ); - # Option type extensions - allowAnything = addTypeToOption anything; - allowAttrs = addTypeToOption attrs; - allowAttrsLegacy = addTypeToOption attrsLegacy; - allowAttrsOf = addTypeWithParameterToOption attrsOf; - allowBool = addTypeToOption bool; - allowCoercedToOption = - coercedType: coerceFunc: finalType: - addTypeToOption (coercedTo coercedType coerceFunc finalType); - allowEnum = addTypeWithParameterToOption enum; - allowFloat = addTypeToOption float; - allowInt = addTypeToOption int; - allowLambda = addTypeToOption lambda; - allowLambdaTo = addTypeWithParameterToOption lambdaTo; - allowList = addTypeToOption list; - allowListOf = addTypeWithParameterToOption listOf; - allowNull = option: option // { type = lib.types.nullOr option.type; }; - allowNumber = addTypeToOption number; - allowPackage = addTypeToOption package; - allowPath = addTypeToOption path; - allowPortOption = addTypeToOption port; - allowSingleLineStrOption = addTypeToOption singleLineStr; - allowStr = addTypeToOption str; + allowTypeOptions = genOptions ( + { name, value }: + { + name = "allow${ + lib.toUpper (builtins.substring 0 1 name) + (builtins.substring 1 (builtins.stringLength name) name) + }"; + value = + option: + option + // { + type = lib.types.oneOf [ + value + option.type + ]; + }; + } + ); - # Option modifiers - noDefault = option: builtins.removeAttrs option [ "default" ]; - # This is a more convenient way to handle the presence of a default value in the option based on the condition (#18) - noNullDefault = - option: if option.default == null then builtins.removeAttrs option [ "default" ] else option; - readOnly = option: option // { readOnly = true; }; - apply = option: apply: option // { inherit apply; }; - description = option: description: option // { inherit description; }; + optionModifiers = { + readOnly = + option: + option + // { + readOnly = true; + __functor = self: readOnly: self // { inherit readOnly; }; + }; + internal = + option: + option + // { + internal = true; + __functor = self: internal: self // { inherit internal; }; + }; + visible = + option: visible: + option + // { + inherit visible; + }; - # Predefined option patterns - singleEnableOption = - default: { name, ... }: delib.attrset.setAttrByStrPath "${name}.enable" (boolOption default); -} + defaultText = option: defaultText: option // { inherit defaultText; }; + example = option: example: option // { inherit example; }; + description = option: description: option // { inherit description; }; + relatedPackages = option: relatedPackages: option // { inherit relatedPackages; }; + apply = option: apply: option // { inherit apply; }; + }; +in +typeOptions // allowTypeOptions // optionModifiers diff --git a/lib/fixed-points.nix b/lib/toplevel/fixed-points.nix similarity index 73% rename from lib/fixed-points.nix rename to lib/toplevel/fixed-points.nix index dabffd1..c9fab09 100644 --- a/lib/fixed-points.nix +++ b/lib/toplevel/fixed-points.nix @@ -1,9 +1,8 @@ { - delib, lib, ... }: -{ +lib.fix (delib: { fix = f: let @@ -42,6 +41,23 @@ final: prev: { } ); + toExtensionWithFinalFirst = + f: + if lib.isFunction f then + final: prev: + let + fFinal = f final; + in + if lib.isFunction fFinal then + # f is (final: prev: { ... }) + fFinal prev + else + # f is (final: { ... }) + fFinal + else + # f is not a function; probably { ... } + final: prev: f; + makeRecursivelyExtensible = delib.makeRecursivelyExtensibleWithCustomName "recursivelyExtend"; makeRecursivelyExtensibleWithCustomName = @@ -56,4 +72,4 @@ } ) ); -} +}) diff --git a/lib/toplevel/lib.nix b/lib/toplevel/lib.nix new file mode 100644 index 0000000..5722c90 --- /dev/null +++ b/lib/toplevel/lib.nix @@ -0,0 +1,138 @@ +{ lib, ... }: +let + fixedPoints = import ./fixed-points.nix { inherit lib; }; +in +{ + /** + Creates a recursively extensible library with additional `_callLib` and `_callLibArgs`. + + # Inputs + + `libName` + + : 1\. The attribute name that will represent the "self" of the library in the arguments of library parts called via `_callLib` + + `ext` + + : 2\. The initial extension for the library. It is transformed using `toExtensionWithFinalFirst` + + # Type + + ``` + mkLib :: string -> ((attrset -> attrset -> attrset) | (attrset -> attrset) | attrset) -> attrset + ``` + + # Example + + ```nix + customLib = mkLib "customLib" (customLib: prev: { + # No need to do `_callLibArgs = prev.callLibArgs // { foo = "foo"; };`, + # because mkLib uses recursive updating of attrsets. + _callLibArgs.foo = "foo"; + + function = x: x + 1 + antiFunction = x: customLib.function x - 1 + }) + ``` + */ + mkLib = + libName: ext: + let + extensible = fixedPoints.makeRecursivelyExtensible (self: { + /** + Arguments passed to library parts called via `_callLib`. + + # Type + + ``` + attrset + ``` + + # Example + + ```nix + customLib = mkLib "customLib" {} + + customLib = customLib.recursivelyExtend ( + final: prev: { + _callLibArgs = { + denixNamePrefix = "de"; + }; + + inherit + (final._callLib ( + { denixNamePrefix, ... }: { denixName = "${denixNamePrefix}nix"; } + )) + denixName; + } + ) + + customLib.denixNamePrefix + => error: attribute 'denixNamePrefix' missing + + customLib.denixName + => "denix" + + customLib._callLibArgs + => + { + customLib = { ... }; + denixNamePrefix = "de"; + lib = { ... }; + } + ``` + */ + _callLibArgs = { + inherit lib; + ${libName} = self; + }; + + /** + Сall a library part `target` using `_callLibArgs`. + + The `target` may be: + + * a `path` - import it, then if it's a function call it with `_callLibArgs`; + * a function - call it with `_callLibArgs`; + * otherwise - returned unchanged. + + # Inputs + + `target` + + : 1\. Path, function, or anything else + + # Type + + ``` + _callLib :: (path | (attrset -> any) | any) -> any + ``` + + # Example + + ```nix + # ./mylib.nix => args: { greeting = "hi"; } + + _callLib ./mylib.nix + => { greeting = "hi"; } + + _callLib ({ ... }: { greeting = "hi"; }) + => { greeting = "hi"; } + + _callLib { greeting = "hi"; } + => { greeting = "hi"; } + + _callLib "hi" + => "hi" + ``` + */ + _callLib = + target: + let + importedTarget = if builtins.isPath target then import target else target; + in + (lib.toFunction importedTarget) self._callLibArgs; + }); + in + extensible.recursivelyExtend (fixedPoints.toExtensionWithFinalFirst ext); +} diff --git a/lib/types.nix b/lib/types.nix new file mode 100644 index 0000000..82d46bc --- /dev/null +++ b/lib/types.nix @@ -0,0 +1,79 @@ +{ delib, lib, ... }: +{ + inherit (lib.types) + # keep-sorted start case=no + anything + attrsOf + bool + coercedTo + either + enum + float + functionTo + int + lazyAttrsOf + listOf + number + oneOf + package + path + port + singleLineStr + str + submodule + submoduleWith + unspecified + # keep-sorted end + ; + + # keep-sorted start case=no block=yes newline_separated=yes + attrs = delib.types.attrsOf delib.types.unspecified; + + attrsLegacy = lib.types.attrs; + + function = delib.types.functionTo delib.types.unspecified; + + intBetween = + lowest: highest: + assert lib.assertMsg (lowest <= highest) "intBetween: lowest must be smaller than highest"; + lib.types.addCheck delib.types.int (x: x >= lowest && x <= highest) + // { + name = "intBetween"; + description = "integer between ${toString lowest} and ${toString highest} (both inclusive)"; + }; + + lazyAttrs = delib.types.lazyAttrsOf delib.types.unspecified; + + list = delib.types.listOf delib.types.unspecified; + + null = lib.mkOptionType { + name = "null"; + description = "null"; + descriptionClass = "noun"; + check = x: x == null; + merge = lib.mergeEqualOption; + emptyValue = { + value = null; + }; + }; + + steppedInt = + step: + assert lib.assertMsg (builtins.isInt step) "steppedInt: step must be an integer"; + lib.types.addCheck delib.types.int (x: x == x / step * step) + // { + name = "steppedInt"; + description = "integer that is a multiple of ${step}"; + }; + + steppedIntBetween = + lowest: highest: step: + assert lib.assertMsg (builtins.isInt step) "steppedIntBetween: step must be an integer"; + assert lib.assertMsg (lowest <= highest) "steppedIntBetween: lowest must be smaller than highest"; + lib.types.addCheck delib.types.int (x: x >= lowest && x <= highest && x == x / step * step) + // { + name = "steppedIntBetween"; + description = "integer between ${toString lowest} and ${toString highest} (inclusive) that is a multiple of ${step}"; + }; + # keep-sorted end +} diff --git a/modules/betterHosts/default.nix b/modules/betterHosts/default.nix new file mode 100644 index 0000000..b9d93d4 --- /dev/null +++ b/modules/betterHosts/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./features.nix + ./type.nix + ]; +} diff --git a/modules/betterHosts/features.nix b/modules/betterHosts/features.nix new file mode 100644 index 0000000..27d283a --- /dev/null +++ b/modules/betterHosts/features.nix @@ -0,0 +1,41 @@ +{ + delib, + lib, + config, + ... +}: +let + cfg = config.betterHosts.features; +in +with delib; +{ + options.betterHosts.features = { + enable = boolOption true; + generateIsFeatured = boolOption true; + features = listOfOption str [ ]; + default = listOfOption str [ ]; + defaultByHostType = attrsOfOption (listOf str) { }; + }; + + config.extraHostSubmodules = lib.mkIf cfg.enable ( + { config, ... }: + { + options = + delib.strictMergeAttrs + { + features = listOfOption (enum cfg.features) [ ]; + defaultFeatures = listOfOption (enum cfg.features) ( + cfg.default ++ (if config.type or null != null then cfg.defaultByHostType.${config.type} or [ ] else [ ]) + ); + } + ( + lib.optionalAttrs cfg.generateIsFeatured ( + lib.genAttrs' cfg.features (feature: { + name = "${feature}Featured"; + value = boolOption (builtins.elem feature (config.features ++ config.defaultFeatures)); + }) + ) + ); + } + ); +} diff --git a/modules/betterHosts/type.nix b/modules/betterHosts/type.nix new file mode 100644 index 0000000..681fc9f --- /dev/null +++ b/modules/betterHosts/type.nix @@ -0,0 +1,43 @@ +{ + delib, + lib, + config, + ... +}: +let + cfg = config.betterHosts.type; +in +with delib; +{ + options.betterHosts.type = { + enable = boolOption true; + generateIsType = boolOption true; + types = listOfOption str [ + "desktop" + "server" + ]; + }; + + config.extraHostSubmodules = lib.mkIf cfg.enable ( + { config, ... }: + { + options = + delib.strictMergeAttrs + { + type = allowNull (enumOption cfg.types null); + } + ( + lib.optionalAttrs cfg.generateIsType ( + lib.genAttrs' cfg.types (type: { + name = + let + chars = lib.stringToCharacters type; + in + "is${lib.toUpper (lib.head chars) + lib.concatStrings (lib.tail chars)}"; + value = boolOption (config.type == type); + }) + ) + ); + } + ); +} diff --git a/modules/denix/abstractions/default.nix b/modules/denix/abstractions/default.nix new file mode 100644 index 0000000..4680d73 --- /dev/null +++ b/modules/denix/abstractions/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./hosts.nix + ./modules.nix + ]; +} diff --git a/modules/denix/abstractions/hosts.nix b/modules/denix/abstractions/hosts.nix new file mode 100644 index 0000000..1469eca --- /dev/null +++ b/modules/denix/abstractions/hosts.nix @@ -0,0 +1,50 @@ +{ + delib, + lib, + config, + ... +}: +let + inherit (lib.strings) escapeNixIdentifier; + + mapItem = + hostName: moduleSystemName: pathSuffix: index: entry: + let + keyAttrs.key = "denix.hosts.${escapeNixIdentifier hostName}.${escapeNixIdentifier moduleSystemName}${pathSuffix}:${toString index}"; + in + delib.processModuleAndGenerateDenixArgs (module: keyAttrs // module) entry { + name = hostName; + }; +in +{ + options = with delib; { + host = allowNull ( + coercedToOption (enum (builtins.attrNames config.hosts)) ( + hostName: if hostName == null then null else config.hosts.${hostName} + ) (enum (builtins.attrValues config.hosts)) null + ); + + extraHostSubmodules = modules.coercedListOfModulesOption; + + hosts = attrsOfOption (delib.modules.denixAbstractionType { + moduleSystems = config.moduleSystems; + extraModules = config.extraHostSubmodules; + }) { }; + }; + + config.rawModules = lib.mapAttrs ( + moduleSystemName: moduleSystem: + lib.concatLists ( + lib.mapAttrsToList ( + hostName: host: + lib.imap1 (mapItem hostName moduleSystemName ".always") host.${moduleSystemName}.always + ++ lib.optionals (config.host.name or null == hostName) ( + lib.imap1 (mapItem hostName moduleSystemName ".ifEnabled") host.${moduleSystemName}.ifEnabled + ) + ++ lib.optionals (config.host.name or null != hostName) ( + lib.imap1 (mapItem hostName moduleSystemName ".ifDisabled") host.${moduleSystemName}.ifDisabled + ) + ) config.hosts + ) + ) config.moduleSystems; +} diff --git a/modules/denix/abstractions/modules.nix b/modules/denix/abstractions/modules.nix new file mode 100644 index 0000000..696e518 --- /dev/null +++ b/modules/denix/abstractions/modules.nix @@ -0,0 +1,99 @@ +{ + delib, + lib, + config, + ... +}: +let + inherit (lib.strings) escapeNixIdentifier; + + mapItem = + moduleName: moduleSystemName: myconfigPrefix: pathSuffix: contentFn: index: entry: + let + myconfigPrefixWithDot = lib.optionalString (myconfigPrefix != null) "${myconfigPrefix}."; + keyAttrs.key = "denix.modules.${escapeNixIdentifier moduleName}.${escapeNixIdentifier moduleSystemName}${pathSuffix}:${toString index}"; + in + delib.processModuleAndGenerateDenixArgs + ( + module: + if !lib.isFunction (contentFn module) then + keyAttrs // contentFn module + else + { config, ... }: keyAttrs // contentFn module config + ) + entry + ( + { config, options, ... }: + { + name = moduleName; + myconfig = delib.getAttrByStrPath config myconfigPrefix { }; + myoptions = delib.getAttrByStrPath options myconfigPrefix { }; + cfg = delib.getAttrByStrPath config "${myconfigPrefixWithDot}${moduleName}" { }; + opt = delib.getAttrByStrPath options "${myconfigPrefixWithDot}${moduleName}" { }; + } + ); +in +{ + options = with delib; { + extraModuleSubmodules = modules.coercedListOfModulesOption; + + modules = attrsOfOption (delib.modules.denixAbstractionType { + moduleSystems = config.moduleSystems; + withOptions = true; + extraModules = config.extraModuleSubmodules; + }) { }; + }; + + config.rawModules = lib.mapAttrs ( + moduleSystemName: moduleSystem: + let + myconfigPrefixWithDot = lib.optionalString ( + moduleSystem.myconfigPrefix != null + ) "${moduleSystem.myconfigPrefix}."; + in + lib.concatLists ( + lib.mapAttrsToList ( + moduleName: module: + let + specialAttrs = x: delib.keepAttrs x [ "_file" ]; + nonSpecialAttrs = x: delib.removeAttrs x [ "_file" ]; + in + let + contentOptions = + entry: + (specialAttrs entry) + // { + options = delib.setAttrByStrPath (nonSpecialAttrs entry) moduleName; + }; + contentAlways = entry: entry; + contentIfEnabled = + entry: config: + (specialAttrs entry) + // lib.mkIf (delib.getAttrByStrPath config "${myconfigPrefixWithDot}${moduleName}.enable" false) ( + nonSpecialAttrs entry + ); + contentIfDisabled = + entry: config: + (specialAttrs entry) + // lib.mkIf (!delib.getAttrByStrPath config "${myconfigPrefixWithDot}${moduleName}.enable" true) ( + nonSpecialAttrs entry + ); + in + if moduleSystemName == "myconfig" then + lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix "" + contentOptions + ) module.options + else + lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix ".always" + contentAlways + ) module.${moduleSystemName}.always + ++ lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix ".ifEnabled" + contentIfEnabled + ) module.${moduleSystemName}.ifEnabled + ++ lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix ".ifDisabled" + contentIfDisabled + ) module.${moduleSystemName}.ifDisabled + ) config.modules + ) + ) config.moduleSystems; +} diff --git a/modules/denix/default.nix b/modules/denix/default.nix new file mode 100644 index 0000000..bbd8a14 --- /dev/null +++ b/modules/denix/default.nix @@ -0,0 +1,20 @@ +{ + delib, + lib, + config, + ... +}: +{ + imports = [ + ./abstractions/default.nix + ./moduleSystems/default.nix + ]; + + options = with delib; { + myconfigPrefix = allowNull (strOption "myconfig"); + + rawModules = lib.mapAttrs ( + moduleSystemName: moduleSystem: modules.coercedListOfModulesOption + ) config.moduleSystems; + }; +} diff --git a/modules/denix/moduleSystems/darwin.nix b/modules/denix/moduleSystems/darwin.nix new file mode 100644 index 0000000..0e2dd71 --- /dev/null +++ b/modules/denix/moduleSystems/darwin.nix @@ -0,0 +1,13 @@ +{ inputs, ... }: +{ + moduleSystems.darwin = { + makeSystem = + { modules, extraArgs, ... }: + inputs.nix-darwin.lib.darwinSystem ( + { + inherit modules; + } + // extraArgs + ); + }; +} diff --git a/modules/denix/moduleSystems/default.nix b/modules/denix/moduleSystems/default.nix new file mode 100644 index 0000000..c90c2c7 --- /dev/null +++ b/modules/denix/moduleSystems/default.nix @@ -0,0 +1,57 @@ +{ + delib, + lib, + config, + ... +}: +let + defaultMyconfigPrefix = config.myconfigPrefix; + selectedModuleSystem = config.moduleSystem; +in +{ + # TODO: system-manager, nvf, nix on droid + imports = [ + ./darwin.nix + ./home.nix + ./myconfig.nix + ./nixos.nix + ]; + + options = with delib; { + moduleSystem = allowNull ( + coercedToOption (enum (builtins.attrNames config.moduleSystems)) ( + moduleSystemName: + if moduleSystemName == null then null else config.moduleSystems.${moduleSystemName} + ) (enum (builtins.attrValues config.moduleSystems)) null + ); + + moduleSystems = lazyAttrsOfOption (submoduleWith { + modules = [ + ( + { name, config, ... }: + { + options = { + name = readOnly (strOption name); + __toString = readOnly (functionToOption str (self: self.name)); + + enable = boolOption false; # enable in gen modules + + myconfigPrefix = allowNull (strOption defaultMyconfigPrefix); + + flakeOutputs = { + modules = allowNull (strOption "${config.name}Modules"); + systems = allowNull (strOption "${config.name}Configurations"); + }; + + makeSystem = allowNull (functionOption null); + + applyConfigForModuleSystem = functionToOption list ( + value: lib.optional (selectedModuleSystem.name == config.name) value + ); + }; + } + ) + ]; + }) { }; + }; +} diff --git a/modules/denix/moduleSystems/home.nix b/modules/denix/moduleSystems/home.nix new file mode 100644 index 0000000..ec98760 --- /dev/null +++ b/modules/denix/moduleSystems/home.nix @@ -0,0 +1,13 @@ +{ inputs, ... }: +{ + moduleSystems.home = { + makeSystem = + { modules, extraArgs, ... }: + inputs.home-manager.lib.homeManagerConfiguration ( + { + inherit modules; + } + // extraArgs + ); + }; +} diff --git a/modules/denix/moduleSystems/myconfig.nix b/modules/denix/moduleSystems/myconfig.nix new file mode 100644 index 0000000..330931f --- /dev/null +++ b/modules/denix/moduleSystems/myconfig.nix @@ -0,0 +1,26 @@ +{ delib, lib, ... }: +{ + moduleSystems.myconfig = + { config, ... }: + { + enable = false; + + flakeOutputs = { + modules = null; + systems = null; + }; + + applyConfigForModuleSystem = + value: + let + addPrefixToModule = + if config.myconfigPrefix != null then + delib.modules.addPrefixToModule (lib.splitString "." config.myconfigPrefix) + else + lib.id; + in + [ + (delib.processModule addPrefixToModule value) + ]; + }; +} diff --git a/modules/denix/moduleSystems/nixos.nix b/modules/denix/moduleSystems/nixos.nix new file mode 100644 index 0000000..921a9e3 --- /dev/null +++ b/modules/denix/moduleSystems/nixos.nix @@ -0,0 +1,13 @@ +{ inputs, ... }: +{ + moduleSystems.nixos = { + makeSystem = + { modules, extraArgs, ... }: + inputs.nixpkgs.lib.nixosSystem ( + { + inherit modules; + } + // extraArgs + ); + }; +} diff --git a/templates/extensions-collection/extensions/extension.nix b/templates/extensions-collection/extensions/extension.nix deleted file mode 100644 index 07fb212..0000000 --- a/templates/extensions-collection/extensions/extension.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ - lib, - delib, - ... -}: -delib.extension { - name = "awesomeExtension"; - description = "..."; - - config = final: prev: { - path = "sequence"; - fun1 = n: n / 2; - fun2 = n: 3 * n + 1; - }; - - libExtension = config: final: prev: { - genSequence = - n: - if n <= 0 then - [ ] - else if n == 1 then - [ 1 ] - else - [ n ] ++ final.genSequence (config."fun${toString (n - n / 2 * 2 + 1)}" n); - }; - - modules = config: [ - ( - { delib, ... }: - delib.module { - name = config.path; - - options = - with delib; - setAttrByStrPath config.path { - first100Sequences = listOption (lib.genList delib.genSequence 100); - }; - } - ) - ]; -} diff --git a/templates/extensions-collection/flake.nix b/templates/extensions-collection/flake.nix deleted file mode 100644 index edf66fb..0000000 --- a/templates/extensions-collection/flake.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ - description = "Denix extensions collection"; - - inputs.denix.url = "github:yunfachi/denix"; - - outputs = - { denix, ... }: - { - denixExtensions = denix.lib.callExtensions { - paths = [ ./extensions ]; - }; - }; -} diff --git a/templates/minimal-no-rices/flake.nix b/templates/minimal-no-rices/flake.nix deleted file mode 100644 index e1d2195..0000000 --- a/templates/minimal-no-rices/flake.nix +++ /dev/null @@ -1,56 +0,0 @@ -{ - description = "Modular configuration of NixOS, Home Manager, and Nix-Darwin with Denix"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - home-manager = { - url = "github:nix-community/home-manager/master"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - nix-darwin = { - url = "github:nix-darwin/nix-darwin/master"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - denix = { - url = "github:yunfachi/denix"; - inputs.nixpkgs.follows = "nixpkgs"; - inputs.home-manager.follows = "home-manager"; - inputs.nix-darwin.follows = "nix-darwin"; - }; - }; - - outputs = - { denix, ... }@inputs: - let - mkConfigurations = - moduleSystem: - denix.lib.configurations { - inherit moduleSystem; - homeManagerUser = "sjohn"; # !!! REPLACEME - - paths = [ - ./hosts - ./modules - ]; - - extensions = with denix.lib.extensions; [ - args - (base.withConfig { - args.enable = true; - rices.enable = false; - }) - ]; - - specialArgs = { - inherit inputs; - }; - }; - in - { - # If you're not using NixOS, Home Manager, or Nix-Darwin, - # you can safely remove the corresponding lines below. - nixosConfigurations = mkConfigurations "nixos"; - homeConfigurations = mkConfigurations "home"; - darwinConfigurations = mkConfigurations "darwin"; - }; -} diff --git a/templates/minimal-no-rices/hosts/desktop/default.nix b/templates/minimal-no-rices/hosts/desktop/default.nix deleted file mode 100644 index 70ce254..0000000 --- a/templates/minimal-no-rices/hosts/desktop/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ delib, ... }: -delib.host { - name = "desktop"; - - type = "desktop"; -} diff --git a/templates/minimal-no-rices/hosts/desktop/hardware.nix b/templates/minimal-no-rices/hosts/desktop/hardware.nix deleted file mode 100644 index 4323d74..0000000 --- a/templates/minimal-no-rices/hosts/desktop/hardware.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ delib, ... }: -delib.host { - name = "desktop"; - - # useHomeManagerModule = false; - homeManagerSystem = "x86_64-linux"; # !!! REPLACEME - home.home.stateVersion = "24.05"; # !!! REPLACEME - - # If you're not using NixOS, you can remove this entire block. - nixos = { - nixpkgs.hostPlatform = "x86_64-linux"; # !!! REPLACEME - system.stateVersion = "24.05"; # !!! REPLACEME - - # nixos-generate-config --show-hardware-config - # other generated code here... - }; - - # If you're not using Nix-Darwin, you can remove this entire block. - darwin = { - nixpkgs.hostPlatform = "aarch64-darwin"; # !!! REPLACEME - system.stateVersion = 6; # !!! REPLACEME - }; -} diff --git a/templates/minimal-no-rices/modules/config/constants.nix b/templates/minimal-no-rices/modules/config/constants.nix deleted file mode 100644 index 55332cf..0000000 --- a/templates/minimal-no-rices/modules/config/constants.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ delib, ... }: -delib.module { - name = "constants"; - - options.constants = with delib; { - #!!! REPLACEME - username = readOnly (strOption "sjohn"); - userfullname = readOnly (strOption "John Smith"); - useremail = readOnly (strOption "johnsmith@example.com"); - }; -} diff --git a/templates/minimal-no-rices/modules/config/home.nix b/templates/minimal-no-rices/modules/config/home.nix deleted file mode 100644 index a9ff355..0000000 --- a/templates/minimal-no-rices/modules/config/home.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ - delib, - pkgs, - ... -}: -delib.module { - name = "home"; - - home.always = - { myconfig, ... }: - let - inherit (myconfig.constants) username; - in - { - home = { - inherit username; - # If you don't need Nix-Darwin, or if you're using it exclusively, - # you can keep the string here instead of the condition. - homeDirectory = if pkgs.stdenv.isDarwin then "/Users/${username}" else "/home/${username}"; - }; - }; -} diff --git a/templates/minimal-no-rices/modules/config/user.nix b/templates/minimal-no-rices/modules/config/user.nix deleted file mode 100644 index 466939a..0000000 --- a/templates/minimal-no-rices/modules/config/user.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ delib, ... }: -delib.module { - name = "user"; - - # If you're not using NixOS, you can remove this entire block. - nixos.always = - { myconfig, ... }: - let - inherit (myconfig.constants) username; - in - { - users = { - groups.${username} = { }; - - users.${username} = { - isNormalUser = true; - initialPassword = username; - extraGroups = [ "wheel" ]; - }; - }; - }; - - # If you're not using Nix-Darwin, you can remove this entire block. - darwin.always = - { myconfig, ... }: - let - inherit (myconfig.constants) username; - in - { - users.users.${username} = { - name = username; - home = "/Users/${username}"; - }; - }; -} diff --git a/templates/minimal/flake.nix b/templates/minimal/flake.nix deleted file mode 100644 index d30874b..0000000 --- a/templates/minimal/flake.nix +++ /dev/null @@ -1,56 +0,0 @@ -{ - description = "Modular configuration of NixOS, Home Manager, and Nix-Darwin with Denix"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - home-manager = { - url = "github:nix-community/home-manager/master"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - nix-darwin = { - url = "github:nix-darwin/nix-darwin/master"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - denix = { - url = "github:yunfachi/denix"; - inputs.nixpkgs.follows = "nixpkgs"; - inputs.home-manager.follows = "home-manager"; - inputs.nix-darwin.follows = "nix-darwin"; - }; - }; - - outputs = - { denix, ... }@inputs: - let - mkConfigurations = - moduleSystem: - denix.lib.configurations { - inherit moduleSystem; - homeManagerUser = "sjohn"; # !!! REPLACEME - - paths = [ - ./hosts - ./modules - ./rices - ]; - - extensions = with denix.lib.extensions; [ - args - (base.withConfig { - args.enable = true; - }) - ]; - - specialArgs = { - inherit inputs; - }; - }; - in - { - # If you're not using NixOS, Home Manager, or Nix-Darwin, - # you can safely remove the corresponding lines below. - nixosConfigurations = mkConfigurations "nixos"; - homeConfigurations = mkConfigurations "home"; - darwinConfigurations = mkConfigurations "darwin"; - }; -} diff --git a/templates/minimal/hosts/desktop/default.nix b/templates/minimal/hosts/desktop/default.nix deleted file mode 100644 index cd2008d..0000000 --- a/templates/minimal/hosts/desktop/default.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ delib, ... }: -delib.host { - name = "desktop"; - - rice = "dark"; - type = "desktop"; -} diff --git a/templates/minimal/hosts/desktop/hardware.nix b/templates/minimal/hosts/desktop/hardware.nix deleted file mode 100644 index 4323d74..0000000 --- a/templates/minimal/hosts/desktop/hardware.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ delib, ... }: -delib.host { - name = "desktop"; - - # useHomeManagerModule = false; - homeManagerSystem = "x86_64-linux"; # !!! REPLACEME - home.home.stateVersion = "24.05"; # !!! REPLACEME - - # If you're not using NixOS, you can remove this entire block. - nixos = { - nixpkgs.hostPlatform = "x86_64-linux"; # !!! REPLACEME - system.stateVersion = "24.05"; # !!! REPLACEME - - # nixos-generate-config --show-hardware-config - # other generated code here... - }; - - # If you're not using Nix-Darwin, you can remove this entire block. - darwin = { - nixpkgs.hostPlatform = "aarch64-darwin"; # !!! REPLACEME - system.stateVersion = 6; # !!! REPLACEME - }; -} diff --git a/templates/minimal/modules/config/constants.nix b/templates/minimal/modules/config/constants.nix deleted file mode 100644 index 55332cf..0000000 --- a/templates/minimal/modules/config/constants.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ delib, ... }: -delib.module { - name = "constants"; - - options.constants = with delib; { - #!!! REPLACEME - username = readOnly (strOption "sjohn"); - userfullname = readOnly (strOption "John Smith"); - useremail = readOnly (strOption "johnsmith@example.com"); - }; -} diff --git a/templates/minimal/modules/config/home.nix b/templates/minimal/modules/config/home.nix deleted file mode 100644 index a9ff355..0000000 --- a/templates/minimal/modules/config/home.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ - delib, - pkgs, - ... -}: -delib.module { - name = "home"; - - home.always = - { myconfig, ... }: - let - inherit (myconfig.constants) username; - in - { - home = { - inherit username; - # If you don't need Nix-Darwin, or if you're using it exclusively, - # you can keep the string here instead of the condition. - homeDirectory = if pkgs.stdenv.isDarwin then "/Users/${username}" else "/home/${username}"; - }; - }; -} diff --git a/templates/minimal/modules/config/user.nix b/templates/minimal/modules/config/user.nix deleted file mode 100644 index 466939a..0000000 --- a/templates/minimal/modules/config/user.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ delib, ... }: -delib.module { - name = "user"; - - # If you're not using NixOS, you can remove this entire block. - nixos.always = - { myconfig, ... }: - let - inherit (myconfig.constants) username; - in - { - users = { - groups.${username} = { }; - - users.${username} = { - isNormalUser = true; - initialPassword = username; - extraGroups = [ "wheel" ]; - }; - }; - }; - - # If you're not using Nix-Darwin, you can remove this entire block. - darwin.always = - { myconfig, ... }: - let - inherit (myconfig.constants) username; - in - { - users.users.${username} = { - name = username; - home = "/Users/${username}"; - }; - }; -} diff --git a/templates/minimal/rices/dark/default.nix b/templates/minimal/rices/dark/default.nix deleted file mode 100644 index 2dffcc6..0000000 --- a/templates/minimal/rices/dark/default.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ delib, ... }: -delib.rice { - name = "dark"; -} From d1282d47f1e06dfd0b4c6f7a54fc7a0d2f83fa78 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Thu, 27 Nov 2025 20:32:15 +0300 Subject: [PATCH 02/23] lib/modules/helpers: avoid using processModule in setDefaultModuleLocation This change improves readability and makes path handling clearer. Previously, paths were not supported in this part of the logic. While it's easy to add path support through `import`, supplying paths to an option that relies on `setDefaultModuleLocation` and then receiving imported attribute sets in the final config is confusing. --- lib/modules/helpers.nix | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/modules/helpers.nix b/lib/modules/helpers.nix index 43c2d27..72b0dbb 100644 --- a/lib/modules/helpers.nix +++ b/lib/modules/helpers.nix @@ -31,10 +31,17 @@ f: m: delib.mirrorFunctionArgs m (delib.toDenixArgs (denixArgs: delib.processModule f (m denixArgs))); - setDefaultModuleLocation = file: m: delib.processModule (module: { _file = file; } // module) m; + setDefaultModuleLocation = file: m: { + _file = file; + imports = [ m ]; + }; setDefaultModuleLocationWithDenixArgs = - file: m: delib.processModuleWithDenixArgs (module: { _file = file; } // module) m; + file: m: + delib.mirrorFunctionArgs m (denixArgs: { + _file = file; + imports = [ (m denixArgs) ]; + }); addPrefixToModule = prefix: m: From 0d863fb6a04033034454d444fb2bc57542c3df09 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Sun, 30 Nov 2025 11:16:30 +0300 Subject: [PATCH 03/23] modules/denix/moduleSystems/default: add processHosts option --- lib/modules/default.nix | 24 ++++++++++++++++++++---- modules/denix/moduleSystems/default.nix | 2 ++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/modules/default.nix b/lib/modules/default.nix index c04c4f3..9cb0bd0 100644 --- a/lib/modules/default.nix +++ b/lib/modules/default.nix @@ -123,7 +123,7 @@ delib._callLib ./denixArgs.nix ) "'forEachHost' must not be true when 'host' is set."; let genSingle = - moduleSystem: host: + moduleSystem: configuration: host: delib.modules.genModule { inherit configuration @@ -149,7 +149,15 @@ delib._callLib ./denixArgs.nix processHosts = moduleSystem: - if forEachHost then lib.genAttrs hosts (genSingle moduleSystem) else genSingle moduleSystem host; + configuration.config.moduleSystems.${moduleSystem}.processHosts { + inherit + configuration + forEachHost + hosts + host + ; + fn = genSingle moduleSystem; + }; in processModuleSystems; @@ -182,7 +190,7 @@ delib._callLib ./denixArgs.nix ) "'forEachHost' must not be true when 'host' is set."; let genSingle = - moduleSystem: host: + moduleSystem: configuration: host: delib.modules.genSystem { inherit configuration @@ -210,7 +218,15 @@ delib._callLib ./denixArgs.nix processHosts = moduleSystem: - if forEachHost then lib.genAttrs hosts (genSingle moduleSystem) else genSingle moduleSystem host; + configuration.config.moduleSystems.${moduleSystem}.processHosts { + inherit + configuration + forEachHost + hosts + host + ; + fn = genSingle moduleSystem; + }; in processModuleSystems; } diff --git a/modules/denix/moduleSystems/default.nix b/modules/denix/moduleSystems/default.nix index c90c2c7..c188d78 100644 --- a/modules/denix/moduleSystems/default.nix +++ b/modules/denix/moduleSystems/default.nix @@ -48,6 +48,8 @@ in applyConfigForModuleSystem = functionToOption list ( value: lib.optional (selectedModuleSystem.name == config.name) value ); + + processHosts = functionOption ({configuration, forEachHost, hosts, host, fn}: if forEachHost then lib.genAttrs hosts (fn configuration) else fn configuration host); }; } ) From 8fcba7cb1af3a737d652bbd41d088d6673ffd7c1 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Sun, 30 Nov 2025 18:28:08 +0300 Subject: [PATCH 04/23] Revert "lib/modules/helpers: avoid using processModule in setDefaultModuleLocation" This reverts commit d1282d47f1e06dfd0b4c6f7a54fc7a0d2f83fa78. --- lib/modules/helpers.nix | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/modules/helpers.nix b/lib/modules/helpers.nix index 72b0dbb..43c2d27 100644 --- a/lib/modules/helpers.nix +++ b/lib/modules/helpers.nix @@ -31,17 +31,10 @@ f: m: delib.mirrorFunctionArgs m (delib.toDenixArgs (denixArgs: delib.processModule f (m denixArgs))); - setDefaultModuleLocation = file: m: { - _file = file; - imports = [ m ]; - }; + setDefaultModuleLocation = file: m: delib.processModule (module: { _file = file; } // module) m; setDefaultModuleLocationWithDenixArgs = - file: m: - delib.mirrorFunctionArgs m (denixArgs: { - _file = file; - imports = [ (m denixArgs) ]; - }); + file: m: delib.processModuleWithDenixArgs (module: { _file = file; } // module) m; addPrefixToModule = prefix: m: From e49102c90d28d83e437626d0ba4a7a74aa6e32b4 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Sun, 30 Nov 2025 18:34:17 +0300 Subject: [PATCH 05/23] lib/modules/helpers: fix setDefaultModuleLocation for `path` --- lib/modules/helpers.nix | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/modules/helpers.nix b/lib/modules/helpers.nix index 43c2d27..f49507c 100644 --- a/lib/modules/helpers.nix +++ b/lib/modules/helpers.nix @@ -31,10 +31,19 @@ f: m: delib.mirrorFunctionArgs m (delib.toDenixArgs (denixArgs: delib.processModule f (m denixArgs))); - setDefaultModuleLocation = file: m: delib.processModule (module: { _file = file; } // module) m; + setDefaultModuleLocation = + file: m': + let + m = if builtins.isPath m' then import m' else m'; + in + delib.processModule (module: { _file = file; } // module) m; setDefaultModuleLocationWithDenixArgs = - file: m: delib.processModuleWithDenixArgs (module: { _file = file; } // module) m; + file: m': + let + m = if builtins.isPath m' then import m' else m'; + in + delib.processModuleWithDenixArgs (module: { _file = file; } // module) m; addPrefixToModule = prefix: m: From 05d385f6bc09ca0cce8073a2a57fe3811d90d09d Mon Sep 17 00:00:00 2001 From: yunfachi Date: Sun, 30 Nov 2025 18:34:52 +0300 Subject: [PATCH 06/23] modules/nixDarwin: init --- flake.nix | 2 ++ modules/denix/moduleSystems/default.nix | 1 - .../{denix/moduleSystems/darwin.nix => nixDarwin/default.nix} | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename modules/{denix/moduleSystems/darwin.nix => nixDarwin/default.nix} (100%) diff --git a/flake.nix b/flake.nix index ab8dc0b..ca16b8f 100644 --- a/flake.nix +++ b/flake.nix @@ -42,7 +42,9 @@ denix = ./modules/denix; betterHosts = ./modules/betterHosts; + nixDarwin = ./modules/nixDarwin; }; + lib = import ./lib { inherit (nixpkgs-lib) lib; inherit (self) inputs denixModules; diff --git a/modules/denix/moduleSystems/default.nix b/modules/denix/moduleSystems/default.nix index c188d78..4029797 100644 --- a/modules/denix/moduleSystems/default.nix +++ b/modules/denix/moduleSystems/default.nix @@ -11,7 +11,6 @@ in { # TODO: system-manager, nvf, nix on droid imports = [ - ./darwin.nix ./home.nix ./myconfig.nix ./nixos.nix diff --git a/modules/denix/moduleSystems/darwin.nix b/modules/nixDarwin/default.nix similarity index 100% rename from modules/denix/moduleSystems/darwin.nix rename to modules/nixDarwin/default.nix From eedb481b54c5224db358c8db888a5d8615c6bb98 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Sun, 30 Nov 2025 18:36:51 +0300 Subject: [PATCH 07/23] lib/modules/default: add configurationWithModule to genModules and genSystems --- lib/modules/default.nix | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/modules/default.nix b/lib/modules/default.nix index 9cb0bd0..e9aa61b 100644 --- a/lib/modules/default.nix +++ b/lib/modules/default.nix @@ -50,13 +50,13 @@ delib._callLib ./denixArgs.nix moduleSystem ? null, host ? null, - }: + }@args: let configurationWithModules = configuration.extendModules { modules = lib.singleton { - config = { - inherit moduleSystem host; - }; + config = + lib.optionalAttrs (args ? moduleSystem) { inherit moduleSystem; } + // lib.optionalAttrs (args ? host) { inherit host; }; }; }; in @@ -82,9 +82,18 @@ delib._callLib ./denixArgs.nix extraArgs ? { }, extraModules ? [ ], - }: + }@args: let - makeSystem = configuration.config.moduleSystems.${moduleSystem}.makeSystem; + configurationWithModules = configuration.extendModules { + modules = lib.singleton { + config = { + inherit moduleSystem; + } + // lib.optionalAttrs (args ? host) { inherit host; }; + }; + }; + + makeSystem = configurationWithModules.config.moduleSystems.${moduleSystem}.makeSystem; in assert lib.assertMsg (makeSystem != null) "The selected module system '${moduleSystem}' does not support making systems. See its 'makeSystem' option."; @@ -92,11 +101,7 @@ delib._callLib ./denixArgs.nix inherit extraArgs; modules = extraModules ++ [ (delib.modules.genModule { - inherit - configuration - moduleSystem - host - ; + configuration = configurationWithModules; }) ]; }; From 767125bead7ec60e1808dfbdde2ed593f0de29a9 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Tue, 2 Dec 2025 17:30:15 +0300 Subject: [PATCH 08/23] modules/homeManager: init --- flake.nix | 1 + modules/denix/moduleSystems/default.nix | 12 +++++- modules/denix/moduleSystems/home.nix | 13 ------ modules/homeManager/default.nix | 37 +++++++++++++++++ modules/homeManager/moduleSystem.nix | 53 +++++++++++++++++++++++++ modules/homeManager/nonStandalone.nix | 50 +++++++++++++++++++++++ modules/homeManager/standalone.nix | 47 ++++++++++++++++++++++ 7 files changed, 198 insertions(+), 15 deletions(-) delete mode 100644 modules/denix/moduleSystems/home.nix create mode 100644 modules/homeManager/default.nix create mode 100644 modules/homeManager/moduleSystem.nix create mode 100644 modules/homeManager/nonStandalone.nix create mode 100644 modules/homeManager/standalone.nix diff --git a/flake.nix b/flake.nix index ca16b8f..66f4095 100644 --- a/flake.nix +++ b/flake.nix @@ -43,6 +43,7 @@ betterHosts = ./modules/betterHosts; nixDarwin = ./modules/nixDarwin; + homeManager = ./modules/homeManager; }; lib = import ./lib { diff --git a/modules/denix/moduleSystems/default.nix b/modules/denix/moduleSystems/default.nix index 4029797..494e731 100644 --- a/modules/denix/moduleSystems/default.nix +++ b/modules/denix/moduleSystems/default.nix @@ -11,7 +11,6 @@ in { # TODO: system-manager, nvf, nix on droid imports = [ - ./home.nix ./myconfig.nix ./nixos.nix ]; @@ -48,7 +47,16 @@ in value: lib.optional (selectedModuleSystem.name == config.name) value ); - processHosts = functionOption ({configuration, forEachHost, hosts, host, fn}: if forEachHost then lib.genAttrs hosts (fn configuration) else fn configuration host); + processHosts = functionOption ( + { + configuration, + forEachHost, + hosts, + host, + fn, + }: + if forEachHost then lib.genAttrs hosts (fn configuration) else fn configuration host + ); }; } ) diff --git a/modules/denix/moduleSystems/home.nix b/modules/denix/moduleSystems/home.nix deleted file mode 100644 index ec98760..0000000 --- a/modules/denix/moduleSystems/home.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ inputs, ... }: -{ - moduleSystems.home = { - makeSystem = - { modules, extraArgs, ... }: - inputs.home-manager.lib.homeManagerConfiguration ( - { - inherit modules; - } - // extraArgs - ); - }; -} diff --git a/modules/homeManager/default.nix b/modules/homeManager/default.nix new file mode 100644 index 0000000..acd976f --- /dev/null +++ b/modules/homeManager/default.nix @@ -0,0 +1,37 @@ +{ + delib, + lib, + config, + ... +}: +let + cfg = config.homeManager; +in +{ + imports = [ + ./moduleSystem.nix + ./nonStandalone.nix + ./standalone.nix + ]; + + options.homeManager = with delib; { + users = attrsOfOption (listOf (enum (builtins.attrNames config.moduleSystems))) { }; + }; + + config.modules."home-manager".home.always = + let + username = cfg.standalone.user or null; + in + lib.optional (username != null) ( + { pkgs, ... }@inputs: + { + home = { + username = lib.mkDefault (inputs.osConfig.users.users.${username}.name or username); + homeDirectory = lib.mkDefault ( + inputs.osConfig.users.users.${username}.home + or (if pkgs.stdenv.isDarwin then "/Users/${username}" else "/home/${username}") + ); + }; + } + ); +} diff --git a/modules/homeManager/moduleSystem.nix b/modules/homeManager/moduleSystem.nix new file mode 100644 index 0000000..65770c4 --- /dev/null +++ b/modules/homeManager/moduleSystem.nix @@ -0,0 +1,53 @@ +{ + inputs, + delib, + lib, + config, + ... +}: +let + cfg = config.homeManager; +in +{ + moduleSystems.home = { + makeSystem = + { modules, extraArgs, ... }: + inputs.home-manager.lib.homeManagerConfiguration ( + { + inherit modules; + pkgs = inputs.home-manager.inputs.nixpkgs.legacyPackages.${cfg.standalone.system}; + } + // extraArgs + ); + + processHosts = + { + configuration, + forEachHost, + hosts, + host, + fn, + }: + if forEachHost then + let + baseHosts = lib.genAttrs (lib.filter ( + host: configuration.config.hosts.${host}.standaloneHomeManager.user != null + ) hosts) (fn configuration); + + hostsForEachUser = lib.foldl' delib.strictMergeAttrs { } ( + map ( + host: + lib.genAttrs' configuration.config.hosts.${host}.standaloneHomeManager.users (user: { + name = "${host}@${user}"; + value = fn (configuration.extendModules { + modules = [ { hosts.${host}.standaloneHomeManager.user = lib.mkVMOverride user; } ]; + }) host; + }) + ) hosts + ); + in + delib.strictMergeAttrs baseHosts hostsForEachUser + else + fn configuration host; + }; +} diff --git a/modules/homeManager/nonStandalone.nix b/modules/homeManager/nonStandalone.nix new file mode 100644 index 0000000..94ab1db --- /dev/null +++ b/modules/homeManager/nonStandalone.nix @@ -0,0 +1,50 @@ +{ + delib, + lib, + config, + inputs, + ... +}: +let + cfg = config.homeManager; + + usersConfig = { + home-manager.users = lib.mapAttrs (user: moduleSystems: { + imports = builtins.concatMap (moduleSystem: config.rawModules.${moduleSystem}) ( + lib.optionals ( + # FIXME: does not duplicate rawModules.home for the current user, but duplicates all modules of the current user for all other users. + # Causes issues in a rather unusual use case: using `rawModules.nixos` or `rawModules.home` when `moduleSystem = "home"` (standalone Home Manager). + config.moduleSystem.name != "home" && cfg.standalone.user != user + ) moduleSystems + ++ [ "home" ] + ); + }) cfg.users; + }; +in +{ + options.homeManager = with delib; { + nixos = { + enable = boolOption (config.moduleSystems ? nixos); + homeManagerModule = anythingOption inputs.home-manager.nixosModules.default; + }; + + darwin = { + enable = boolOption (config.moduleSystems ? darwin); + homeManagerModule = anythingOption inputs.home-manager.darwinModules.default; + }; + }; + + config.modules."home-manager" = + lib.optionalAttrs cfg.nixos.enable { + nixos.always = [ + cfg.nixos.homeManagerModule + usersConfig + ]; + } + // lib.optionalAttrs cfg.darwin.enable { + darwin.always = [ + cfg.darwin.homeManagerModule + usersConfig + ]; + }; +} diff --git a/modules/homeManager/standalone.nix b/modules/homeManager/standalone.nix new file mode 100644 index 0000000..2b1100c --- /dev/null +++ b/modules/homeManager/standalone.nix @@ -0,0 +1,47 @@ +{ + delib, + lib, + config, + ... +}: +let + cfg = config.homeManager; +in +{ + options.homeManager.standalone = with delib; { + system = allowNull (strOption null); + user = allowNull (strOption null); + + defaultForEachUser = boolOption true; + }; + + config = { + homeManager.standalone = { + system = lib.mkIf ( + config.host.standaloneHomeManager.system or null != null + ) config.host.standaloneHomeManager.system; + user = lib.mkIf ( + config.host.standaloneHomeManager.user or null != null + ) config.host.standaloneHomeManager.user; + }; + + extraHostSubmodules = { + options.standaloneHomeManager = with delib; { + system = allowNull (strOption null); + user = allowNull (strOption null); + + users = listOfOption (enum (builtins.attrNames cfg.users)) ( + lib.optionals cfg.standalone.defaultForEachUser (builtins.attrNames cfg.users) + ); + }; + }; + + modules."home-manager".home.always = + lib.optionals (config.moduleSystem.name == "home" && cfg.standalone.user != null) + ( + builtins.concatMap ( + moduleSystem: config.rawModules.${moduleSystem} + ) config.homeManager.users.${cfg.standalone.user} + ); + }; +} From d6d2a75a6139147dcb3afeb45b0a92d81da6bbff Mon Sep 17 00:00:00 2001 From: yunfachi Date: Wed, 3 Dec 2025 12:33:44 +0300 Subject: [PATCH 09/23] missingFlake: init --- flake.lock | 73 +++++++++++------------------ flake.nix | 10 +--- missingFlake/default.nix | 9 ++++ missingFlake/flake.nix | 3 ++ missingFlake/home-manager/flake.nix | 3 ++ missingFlake/nix-darwin/flake.nix | 3 ++ 6 files changed, 47 insertions(+), 54 deletions(-) create mode 100644 missingFlake/default.nix create mode 100644 missingFlake/flake.nix create mode 100644 missingFlake/home-manager/flake.nix create mode 100644 missingFlake/nix-darwin/flake.nix diff --git a/flake.lock b/flake.lock index 297ed32..9edb52a 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1747046372, - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "lastModified": 1761588595, + "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", "owner": "edolstra", "repo": "flake-compat", - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", "type": "github" }, "original": { @@ -23,11 +23,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1759523803, - "narHash": "sha256-PTod9NG+i3XbbnBKMl/e5uHDBYpwIWivQ3gOWSEuIEM=", + "lastModified": 1763988335, + "narHash": "sha256-QlcnByMc8KBjpU37rbq5iP7Cp97HvjRP0ucfdh+M4Qc=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "cfc9f7bb163ad8542029d303e599c0f7eee09835", + "rev": "50b9238891e388c9fdc6a5c49e49c42533a1b5ce", "type": "github" }, "original": { @@ -58,53 +58,34 @@ } }, "home-manager": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, "locked": { - "lastModified": 1759573136, - "narHash": "sha256-ILSPD0Dm8p0w0fCVzOx98ZH8yFDrR75GmwmH3fS2VnE=", - "owner": "nix-community", - "repo": "home-manager", - "rev": "5f06ceafc6c9b773a776b9195c3f47bbe1defa43", - "type": "github" + "path": "missingFlake/home-manager", + "type": "path" }, "original": { - "owner": "nix-community", - "ref": "master", - "repo": "home-manager", - "type": "github" - } + "path": "missingFlake/home-manager", + "type": "path" + }, + "parent": [] }, "nix-darwin": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, "locked": { - "lastModified": 1758805352, - "narHash": "sha256-BHdc43Lkayd+72W/NXRKHzX5AZ+28F3xaUs3a88/Uew=", - "owner": "nix-darwin", - "repo": "nix-darwin", - "rev": "c48e963a5558eb1c3827d59d21c5193622a1477c", - "type": "github" + "path": "missingFlake/nix-darwin", + "type": "path" }, "original": { - "owner": "nix-darwin", - "repo": "nix-darwin", - "type": "github" - } + "path": "missingFlake/nix-darwin", + "type": "path" + }, + "parent": [] }, "nixpkgs": { "locked": { - "lastModified": 1759070547, - "narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=", + "lastModified": 1759417375, + "narHash": "sha256-O7eHcgkQXJNygY6AypkF9tFhsoDQjpNEojw3eFs73Ow=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "647e5c14cbd5067f44ac86b74f014962df460840", + "rev": "dc704e6102e76aad573f63b74c742cd96f8f1e6c", "type": "github" }, "original": { @@ -116,11 +97,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1762650614, - "narHash": "sha256-tPIUJjNeNs3LMWH8w2nHLx0trZJdOJ54mN2UQhcjZ9g=", + "lastModified": 1764465291, + "narHash": "sha256-jJ/E4B9Hp7U2ZmT3E0tD1LtAfATw/xjVf8sueNyeYmc=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "91ea24e62ff55f95939f32432fa5def2d6d24d2a", + "rev": "e9537535ae8f4a2f78dbef0aaa0cbb6af4abd047", "type": "github" }, "original": { @@ -131,11 +112,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1761114652, - "narHash": "sha256-f/QCJM/YhrV/lavyCVz8iU3rlZun6d+dAiC3H+CDle4=", + "lastModified": 1764517877, + "narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=", "owner": "nixos", "repo": "nixpkgs", - "rev": "01f116e4df6a15f4ccdffb1bcd41096869fb385c", + "rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 66f4095..ca89c24 100644 --- a/flake.nix +++ b/flake.nix @@ -14,14 +14,8 @@ not be overridden by the user without a special reason. */ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - home-manager = { - url = "github:nix-community/home-manager/master"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - nix-darwin = { - url = "github:nix-darwin/nix-darwin"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + home-manager.url = ./missingFlake/home-manager; + nix-darwin.url = ./missingFlake/nix-darwin; }; outputs = diff --git a/missingFlake/default.nix b/missingFlake/default.nix new file mode 100644 index 0000000..9961bf4 --- /dev/null +++ b/missingFlake/default.nix @@ -0,0 +1,9 @@ +inputName: url: +builtins.throw '' + This flake input is disabled by default to avoid pulling extra dependencies. + Since it is only required by an optional Denix module, you must explicitly + add this input to your flake.nix and override it in Denix: + + inputs.${inputName}.url = "${url}"; + inputs.denix.inputs.${inputName}.follows = "${inputName}"; +'' diff --git a/missingFlake/flake.nix b/missingFlake/flake.nix new file mode 100644 index 0000000..d7f7e12 --- /dev/null +++ b/missingFlake/flake.nix @@ -0,0 +1,3 @@ +{ + outputs = inputs: import ./default.nix "" "..."; +} diff --git a/missingFlake/home-manager/flake.nix b/missingFlake/home-manager/flake.nix new file mode 100644 index 0000000..c4d9795 --- /dev/null +++ b/missingFlake/home-manager/flake.nix @@ -0,0 +1,3 @@ +{ + outputs = inputs: import ../default.nix "home-manager" "github:nix-community/home-manager"; +} diff --git a/missingFlake/nix-darwin/flake.nix b/missingFlake/nix-darwin/flake.nix new file mode 100644 index 0000000..46fcc71 --- /dev/null +++ b/missingFlake/nix-darwin/flake.nix @@ -0,0 +1,3 @@ +{ + outputs = inputs: import ../default.nix "nix-darwin" "github:nix-darwin/nix-darwin"; +} From a1af58ca67e0dc61693cf553f145b950370525fd Mon Sep 17 00:00:00 2001 From: yunfachi Date: Wed, 3 Dec 2025 12:34:20 +0300 Subject: [PATCH 10/23] modules/robotnix: init --- flake.lock | 12 ++++++++++++ flake.nix | 2 ++ missingFlake/robotnix/flake.nix | 3 +++ modules/robotnix/default.nix | 10 ++++++++++ 4 files changed, 27 insertions(+) create mode 100644 missingFlake/robotnix/flake.nix create mode 100644 modules/robotnix/default.nix diff --git a/flake.lock b/flake.lock index 9edb52a..a785e5c 100644 --- a/flake.lock +++ b/flake.lock @@ -126,6 +126,17 @@ "type": "github" } }, + "robotnix": { + "locked": { + "path": "missingFlake/robotnix", + "type": "path" + }, + "original": { + "path": "missingFlake/robotnix", + "type": "path" + }, + "parent": [] + }, "root": { "inputs": { "git-hooks": "git-hooks", @@ -133,6 +144,7 @@ "nix-darwin": "nix-darwin", "nixpkgs": "nixpkgs_2", "nixpkgs-lib": "nixpkgs-lib", + "robotnix": "robotnix", "systems": "systems" } }, diff --git a/flake.nix b/flake.nix index ca89c24..96dc284 100644 --- a/flake.nix +++ b/flake.nix @@ -16,6 +16,7 @@ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; home-manager.url = ./missingFlake/home-manager; nix-darwin.url = ./missingFlake/nix-darwin; + robotnix.url = ./missingFlake/robotnix; }; outputs = @@ -38,6 +39,7 @@ betterHosts = ./modules/betterHosts; nixDarwin = ./modules/nixDarwin; homeManager = ./modules/homeManager; + robotnix = ./modules/robotnix; }; lib = import ./lib { diff --git a/missingFlake/robotnix/flake.nix b/missingFlake/robotnix/flake.nix new file mode 100644 index 0000000..79eda80 --- /dev/null +++ b/missingFlake/robotnix/flake.nix @@ -0,0 +1,3 @@ +{ + outputs = inputs: import ../default.nix "robotnix" "github:nix-community/robotnix"; +} diff --git a/modules/robotnix/default.nix b/modules/robotnix/default.nix new file mode 100644 index 0000000..0b360c9 --- /dev/null +++ b/modules/robotnix/default.nix @@ -0,0 +1,10 @@ +{ inputs, ... }: +{ + moduleSystems.robotnix = { + makeSystem = + { modules, ... }: + inputs.robotnix.lib.robotnixSystem { + imports = modules; + }; + }; +} From 5f14a07259509b9937a94843c31c9647b59dcbe3 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Sat, 6 Dec 2025 20:23:06 +0300 Subject: [PATCH 11/23] lib/types: fix description error in steppedInt types --- lib/types.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/types.nix b/lib/types.nix index 82d46bc..0018cc7 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -63,7 +63,7 @@ lib.types.addCheck delib.types.int (x: x == x / step * step) // { name = "steppedInt"; - description = "integer that is a multiple of ${step}"; + description = "integer that is a multiple of ${toString step}"; }; steppedIntBetween = @@ -73,7 +73,7 @@ lib.types.addCheck delib.types.int (x: x >= lowest && x <= highest && x == x / step * step) // { name = "steppedIntBetween"; - description = "integer between ${toString lowest} and ${toString highest} (inclusive) that is a multiple of ${step}"; + description = "integer between ${toString lowest} and ${toString highest} (inclusive) that is a multiple of ${toString step}"; }; # keep-sorted end } From 7509be05c5fe23386bc12d6ddc244f7ddfa28c31 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Fri, 19 Dec 2025 22:06:35 +0300 Subject: [PATCH 12/23] lib/modules/options: remove "via option" in `coercedListOfModules` I think this is a good feature, but it causes problems in many places, because the familiar file path stops being what it used to be, and automatic handlers simply break. --- lib/modules/options.nix | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/modules/options.nix b/lib/modules/options.nix index 4970808..e40e720 100644 --- a/lib/modules/options.nix +++ b/lib/modules/options.nix @@ -7,9 +7,9 @@ loc: defs: lib.concatMap ( def: - map (delib.setDefaultModuleLocation "${def.file}, via option ${lib.showOption loc}") ( - lib.toList def.value - ) + map (delib.setDefaultModuleLocation def.file + #"${def.file}, via option ${lib.showOption loc}" + ) (lib.toList def.value) ) defs; emptyValue.value = [ ]; }; @@ -28,7 +28,8 @@ else delib.setDefaultModuleLocation ) - "${def.file}, via option ${lib.showOption loc}" + def.file + #"${def.file}, via option ${lib.showOption loc}" ) (lib.toList def.value) ) defs; emptyValue.value = [ ]; From 63b10a0292578a62fd90396f03b948f402f0f387 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Sat, 20 Dec 2025 15:37:45 +0300 Subject: [PATCH 13/23] lib/types: add numberBetween, steppedNumber, steppedNumberBetween --- lib/default.nix | 9 +++++++++ lib/types.nix | 28 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/lib/default.nix b/lib/default.nix index 0376db1..d0e2e1c 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -119,6 +119,7 @@ mkLib "delib" (delib: { allowListOf allowNull allowNumber + allowNumberBetween allowOneOf allowPackage allowPath @@ -126,6 +127,8 @@ mkLib "delib" (delib: { allowSingleLineStr allowSteppedInt allowSteppedIntBetween + allowSteppedNumber + allowSteppedNumberBetween allowStr allowSubmodule allowSubmoduleWith @@ -153,6 +156,7 @@ mkLib "delib" (delib: { listOfOption listOption nullOption + numberBetweenOption numberOption oneOfOption packageOption @@ -163,6 +167,8 @@ mkLib "delib" (delib: { singleLineStrOption steppedIntBetweenOption steppedIntOption + steppedNumberBetweenOption + steppedNumberOption strOption submoduleOption submoduleWithOption @@ -189,6 +195,7 @@ mkLib "delib" (delib: { listOf null number + numberBetween oneOf package path @@ -196,6 +203,8 @@ mkLib "delib" (delib: { singleLineStr steppedInt steppedIntBetween + steppedNumber + steppedNumberBetween str submodule submoduleWith diff --git a/lib/types.nix b/lib/types.nix index 0018cc7..83165e2 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -57,6 +57,15 @@ }; }; + numberBetween = + lowest: highest: + assert lib.assertMsg (lowest <= highest) "numberBetween: lowest must be smaller than highest"; + lib.types.addCheck delib.types.number (x: x >= lowest && x <= highest) + // { + name = "numberBetween"; + description = "integer or floating point number between ${toString lowest} and ${toString highest} (both inclusive)"; + }; + steppedInt = step: assert lib.assertMsg (builtins.isInt step) "steppedInt: step must be an integer"; @@ -75,5 +84,24 @@ name = "steppedIntBetween"; description = "integer between ${toString lowest} and ${toString highest} (inclusive) that is a multiple of ${toString step}"; }; + + steppedNumber = + step: + lib.types.addCheck delib.types.number (x: x == x / step * step) + // { + name = "steppedNumber"; + description = "integer or floating point number that is a multiple of ${toString step}"; + }; + + steppedNumberBetween = + lowest: highest: step: + assert lib.assertMsg ( + lowest <= highest + ) "steppedNumberBetween: lowest must be smaller than highest"; + lib.types.addCheck delib.types.number (x: x >= lowest && x <= highest && x == x / step * step) + // { + name = "steppedNumberBetween"; + description = "integer or floating point number between ${toString lowest} and ${toString highest} (inclusive) that is a multiple of ${toString step}"; + }; # keep-sorted end } From d0996ff930e511a2e7d3eabe8125205e4b1fe33d Mon Sep 17 00:00:00 2001 From: yunfachi Date: Sun, 21 Dec 2025 15:09:25 +0300 Subject: [PATCH 14/23] lib/options: add extraAttrs argument to options --- lib/options.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/options.nix b/lib/options.nix index 2607eee..0d8d733 100644 --- a/lib/options.nix +++ b/lib/options.nix @@ -8,7 +8,12 @@ let if lib.isFunction type then typeArg: option // { type = type typeArg; } // functorForType (type typeArg) else - default: option // { inherit default; }; + default: + option + // { + inherit default; + __functor = self: extraAttrs: self // extraAttrs; + }; }; typeOptions = genOptions ( From 8a97630ad141c0ec6fe6a589b104e63161ab7c3c Mon Sep 17 00:00:00 2001 From: yunfachi Date: Sun, 21 Dec 2025 21:38:36 +0300 Subject: [PATCH 15/23] lib/options: replace `extraArgs` with `arg`, which can be either `description` or `extraArgs` --- lib/options.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/options.nix b/lib/options.nix index 0d8d733..3683316 100644 --- a/lib/options.nix +++ b/lib/options.nix @@ -12,7 +12,8 @@ let option // { inherit default; - __functor = self: extraAttrs: self // extraAttrs; + __functor = + self: arg: if builtins.isString arg then self // { description = arg; } else self // arg; }; }; From 5d1ffdbe99bcd87c82481a47aef8bfbded8736a0 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Mon, 22 Dec 2025 13:43:31 +0300 Subject: [PATCH 16/23] modules/homeManager: add null check to config.moduleSystem --- modules/homeManager/nonStandalone.nix | 2 +- modules/homeManager/standalone.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/homeManager/nonStandalone.nix b/modules/homeManager/nonStandalone.nix index 94ab1db..155d2b5 100644 --- a/modules/homeManager/nonStandalone.nix +++ b/modules/homeManager/nonStandalone.nix @@ -14,7 +14,7 @@ let lib.optionals ( # FIXME: does not duplicate rawModules.home for the current user, but duplicates all modules of the current user for all other users. # Causes issues in a rather unusual use case: using `rawModules.nixos` or `rawModules.home` when `moduleSystem = "home"` (standalone Home Manager). - config.moduleSystem.name != "home" && cfg.standalone.user != user + config.moduleSystem.name or null != "home" && cfg.standalone.user != user ) moduleSystems ++ [ "home" ] ); diff --git a/modules/homeManager/standalone.nix b/modules/homeManager/standalone.nix index 2b1100c..e86aaa7 100644 --- a/modules/homeManager/standalone.nix +++ b/modules/homeManager/standalone.nix @@ -37,7 +37,7 @@ in }; modules."home-manager".home.always = - lib.optionals (config.moduleSystem.name == "home" && cfg.standalone.user != null) + lib.optionals (config.moduleSystem.name or null == "home" && cfg.standalone.user != null) ( builtins.concatMap ( moduleSystem: config.rawModules.${moduleSystem} From 3ff9df734a5d396bbecb9034d90406775a745006 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Mon, 22 Dec 2025 22:08:05 +0300 Subject: [PATCH 17/23] lib/modules/default: fix typo in _callLib wrappers.nix --- lib/modules/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/default.nix b/lib/modules/default.nix index e9aa61b..1939b89 100644 --- a/lib/modules/default.nix +++ b/lib/modules/default.nix @@ -8,7 +8,7 @@ delib._callLib ./denixArgs.nix // delib._callLib ./helpers.nix // delib._callLib ./options.nix -// delib._callLib ./helpers.nix +// delib._callLib ./wrappers.nix // { denixConfiguration = { From 86d647b56bd388789741feae0b90078d188a5fc5 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Tue, 23 Dec 2025 15:37:50 +0300 Subject: [PATCH 18/23] lib/modules/helpers: add path support to processModule --- lib/modules/helpers.nix | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/modules/helpers.nix b/lib/modules/helpers.nix index f49507c..fa39bf9 100644 --- a/lib/modules/helpers.nix +++ b/lib/modules/helpers.nix @@ -1,7 +1,10 @@ { delib, lib, ... }: { processModule = - f: m: + f: m': + let + m = if builtins.isPath m' then import m' else m'; + in if !lib.isFunction m then f m else if !lib.isFunction (delib.callWithMocks f) then @@ -12,7 +15,10 @@ ); processModuleAndGenerateDenixArgs = - f: m: denixArgs: + f: m': denixArgs: + let + m = if builtins.isPath m' then import m' else m'; + in if !delib.isDenixArgs m then delib.processModule f m else @@ -28,7 +34,10 @@ ); processModuleWithDenixArgs = - f: m: + f: m': + let + m = if builtins.isPath m' then import m' else m'; + in delib.mirrorFunctionArgs m (delib.toDenixArgs (denixArgs: delib.processModule f (m denixArgs))); setDefaultModuleLocation = From 73345500e0de9ea08ab53c0f8b42db454d016eac Mon Sep 17 00:00:00 2001 From: yunfachi Date: Mon, 26 Jan 2026 10:24:06 +0300 Subject: [PATCH 19/23] flake: move missingFlake into separate repository --- flake.lock | 91 ++++++++++++++++++----------- flake.nix | 10 ++-- missingFlake/default.nix | 9 --- missingFlake/flake.nix | 3 - missingFlake/home-manager/flake.nix | 3 - missingFlake/nix-darwin/flake.nix | 3 - missingFlake/robotnix/flake.nix | 3 - 7 files changed, 62 insertions(+), 60 deletions(-) delete mode 100644 missingFlake/default.nix delete mode 100644 missingFlake/flake.nix delete mode 100644 missingFlake/home-manager/flake.nix delete mode 100644 missingFlake/nix-darwin/flake.nix delete mode 100644 missingFlake/robotnix/flake.nix diff --git a/flake.lock b/flake.lock index a785e5c..89e444f 100644 --- a/flake.lock +++ b/flake.lock @@ -3,15 +3,15 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1761588595, - "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", - "owner": "edolstra", + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", "repo": "flake-compat", - "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "type": "github" }, "original": { - "owner": "edolstra", + "owner": "NixOS", "repo": "flake-compat", "type": "github" } @@ -23,11 +23,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1763988335, - "narHash": "sha256-QlcnByMc8KBjpU37rbq5iP7Cp97HvjRP0ucfdh+M4Qc=", + "lastModified": 1769069492, + "narHash": "sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W+xc49RL/U=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "50b9238891e388c9fdc6a5c49e49c42533a1b5ce", + "rev": "a1ef738813b15cf8ec759bdff5761b027e3e1d23", "type": "github" }, "original": { @@ -59,33 +59,45 @@ }, "home-manager": { "locked": { - "path": "missingFlake/home-manager", - "type": "path" + "dir": "home-manager", + "lastModified": 1769406695, + "narHash": "sha256-djao88Yt82Oedn9vVKU6eJSK2xEy0AgWChiZcX/x+NY=", + "owner": "yunfachi", + "repo": "missingFlake", + "rev": "850beb1c6244c41d1fc922adc81e46e870841e8a", + "type": "github" }, "original": { - "path": "missingFlake/home-manager", - "type": "path" - }, - "parent": [] + "dir": "home-manager", + "owner": "yunfachi", + "repo": "missingFlake", + "type": "github" + } }, "nix-darwin": { "locked": { - "path": "missingFlake/nix-darwin", - "type": "path" + "dir": "nix-darwin", + "lastModified": 1769406695, + "narHash": "sha256-djao88Yt82Oedn9vVKU6eJSK2xEy0AgWChiZcX/x+NY=", + "owner": "yunfachi", + "repo": "missingFlake", + "rev": "850beb1c6244c41d1fc922adc81e46e870841e8a", + "type": "github" }, "original": { - "path": "missingFlake/nix-darwin", - "type": "path" - }, - "parent": [] + "dir": "nix-darwin", + "owner": "yunfachi", + "repo": "missingFlake", + "type": "github" + } }, "nixpkgs": { "locked": { - "lastModified": 1759417375, - "narHash": "sha256-O7eHcgkQXJNygY6AypkF9tFhsoDQjpNEojw3eFs73Ow=", + "lastModified": 1764947035, + "narHash": "sha256-EYHSjVM4Ox4lvCXUMiKKs2vETUSL5mx+J2FfutM7T9w=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "dc704e6102e76aad573f63b74c742cd96f8f1e6c", + "rev": "a672be65651c80d3f592a89b3945466584a22069", "type": "github" }, "original": { @@ -97,11 +109,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1764465291, - "narHash": "sha256-jJ/E4B9Hp7U2ZmT3E0tD1LtAfATw/xjVf8sueNyeYmc=", + "lastModified": 1769304017, + "narHash": "sha256-TE1EHvIAz81IGUKTmKQehbc9hjuxF7pe/QWdQuy/Ijc=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "e9537535ae8f4a2f78dbef0aaa0cbb6af4abd047", + "rev": "d4c8053ce1d9ba28bfb69a9f9f23ac24d313d4e8", "type": "github" }, "original": { @@ -112,11 +124,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1764517877, - "narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=", + "lastModified": 1769170682, + "narHash": "sha256-oMmN1lVQU0F0W2k6OI3bgdzp2YOHWYUAw79qzDSjenU=", "owner": "nixos", "repo": "nixpkgs", - "rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c", + "rev": "c5296fdd05cfa2c187990dd909864da9658df755", "type": "github" }, "original": { @@ -126,16 +138,25 @@ "type": "github" } }, - "robotnix": { "locked": { - "path": "missingFlake/robotnix", - "type": "path" }, "original": { - "path": "missingFlake/robotnix", - "type": "path" + "robotnix": { + "locked": { + "dir": "robotnix", + "lastModified": 1769406695, + "narHash": "sha256-djao88Yt82Oedn9vVKU6eJSK2xEy0AgWChiZcX/x+NY=", + "owner": "yunfachi", + "repo": "missingFlake", + "rev": "850beb1c6244c41d1fc922adc81e46e870841e8a", + "type": "github" }, - "parent": [] + "original": { + "dir": "robotnix", + "owner": "yunfachi", + "repo": "missingFlake", + "type": "github" + } }, "root": { "inputs": { diff --git a/flake.nix b/flake.nix index 96dc284..31affb6 100644 --- a/flake.nix +++ b/flake.nix @@ -14,9 +14,11 @@ not be overridden by the user without a special reason. */ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - home-manager.url = ./missingFlake/home-manager; - nix-darwin.url = ./missingFlake/nix-darwin; - robotnix.url = ./missingFlake/robotnix; + + # Optional inputs + home-manager.url = "github:yunfachi/missingFlake?dir=home-manager"; + nix-darwin.url = "github:yunfachi/missingFlake?dir=nix-darwin"; + robotnix.url = "github:yunfachi/missingFlake?dir=robotnix"; }; outputs = @@ -37,8 +39,8 @@ denix = ./modules/denix; betterHosts = ./modules/betterHosts; - nixDarwin = ./modules/nixDarwin; homeManager = ./modules/homeManager; + nixDarwin = ./modules/nixDarwin; robotnix = ./modules/robotnix; }; diff --git a/missingFlake/default.nix b/missingFlake/default.nix deleted file mode 100644 index 9961bf4..0000000 --- a/missingFlake/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -inputName: url: -builtins.throw '' - This flake input is disabled by default to avoid pulling extra dependencies. - Since it is only required by an optional Denix module, you must explicitly - add this input to your flake.nix and override it in Denix: - - inputs.${inputName}.url = "${url}"; - inputs.denix.inputs.${inputName}.follows = "${inputName}"; -'' diff --git a/missingFlake/flake.nix b/missingFlake/flake.nix deleted file mode 100644 index d7f7e12..0000000 --- a/missingFlake/flake.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - outputs = inputs: import ./default.nix "" "..."; -} diff --git a/missingFlake/home-manager/flake.nix b/missingFlake/home-manager/flake.nix deleted file mode 100644 index c4d9795..0000000 --- a/missingFlake/home-manager/flake.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - outputs = inputs: import ../default.nix "home-manager" "github:nix-community/home-manager"; -} diff --git a/missingFlake/nix-darwin/flake.nix b/missingFlake/nix-darwin/flake.nix deleted file mode 100644 index 46fcc71..0000000 --- a/missingFlake/nix-darwin/flake.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - outputs = inputs: import ../default.nix "nix-darwin" "github:nix-darwin/nix-darwin"; -} diff --git a/missingFlake/robotnix/flake.nix b/missingFlake/robotnix/flake.nix deleted file mode 100644 index 79eda80..0000000 --- a/missingFlake/robotnix/flake.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - outputs = inputs: import ../default.nix "robotnix" "github:nix-community/robotnix"; -} From fc067bc8b2661a577ee7c3855f00dbb1ae7ebdb2 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Mon, 26 Jan 2026 10:26:26 +0300 Subject: [PATCH 20/23] lib/modules/options: fix lib.path.check usage --- flake.lock | 3 --- lib/modules/options.nix | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 89e444f..88bd9f4 100644 --- a/flake.lock +++ b/flake.lock @@ -138,9 +138,6 @@ "type": "github" } }, - "locked": { - }, - "original": { "robotnix": { "locked": { "dir": "robotnix", diff --git a/lib/modules/options.nix b/lib/modules/options.nix index e40e720..8e5560f 100644 --- a/lib/modules/options.nix +++ b/lib/modules/options.nix @@ -2,7 +2,7 @@ { coercedListOfModules = lib.mkOptionType { name = "listOfModules"; - check = x: lib.isList x || lib.isAttrs x || lib.isFunction x || lib.path.check x; + check = x: lib.isList x || lib.isAttrs x || lib.isFunction x || delib.types.path.check x; merge = loc: defs: lib.concatMap ( @@ -16,7 +16,7 @@ coercedListOfModulesWithDenixArgs = lib.mkOptionType { name = "listOfModulesWithDenixArgs"; - check = x: lib.isList x || lib.isAttrs x || lib.isFunction x || lib.path.check x; + check = x: lib.isList x || lib.isAttrs x || lib.isFunction x || delib.types.path.check x; merge = loc: defs: lib.concatMap ( @@ -165,7 +165,7 @@ name = "denixConfiguration"; check = { - __functor = _self: x: lib.isAttrs x || lib.isFunction x || lib.path.check x; + __functor = _self: x: lib.isAttrs x || lib.isFunction x || delib.types.path.check x; isV2MergeCoherent = true; }; in From 066a93fc78ad1ba74b2642dec1b422506f0b2759 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Tue, 27 Jan 2026 15:28:11 +0300 Subject: [PATCH 21/23] modules/denix/moduleSystems: remove TODO comment --- modules/denix/moduleSystems/default.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/denix/moduleSystems/default.nix b/modules/denix/moduleSystems/default.nix index 494e731..b42f7ad 100644 --- a/modules/denix/moduleSystems/default.nix +++ b/modules/denix/moduleSystems/default.nix @@ -9,7 +9,6 @@ let selectedModuleSystem = config.moduleSystem; in { - # TODO: system-manager, nvf, nix on droid imports = [ ./myconfig.nix ./nixos.nix From 3504e7dee2b1bd6c27a0f3fe9152596ac0ac0ee0 Mon Sep 17 00:00:00 2001 From: yunfachi Date: Tue, 27 Jan 2026 15:29:03 +0300 Subject: [PATCH 22/23] modules/betterHosts: format --- modules/betterHosts/features.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/betterHosts/features.nix b/modules/betterHosts/features.nix index 27d283a..f87d39c 100644 --- a/modules/betterHosts/features.nix +++ b/modules/betterHosts/features.nix @@ -25,7 +25,8 @@ with delib; { features = listOfOption (enum cfg.features) [ ]; defaultFeatures = listOfOption (enum cfg.features) ( - cfg.default ++ (if config.type or null != null then cfg.defaultByHostType.${config.type} or [ ] else [ ]) + cfg.default + ++ (if config.type or null != null then cfg.defaultByHostType.${config.type} or [ ] else [ ]) ); } ( From fefc463c8ff244d831461b9bd1c92f5516b57eab Mon Sep 17 00:00:00 2001 From: yunfachi Date: Tue, 27 Jan 2026 15:30:04 +0300 Subject: [PATCH 23/23] lib/modules/genModule: move applyConfigForModuleSystem to abstractions --- lib/modules/default.nix | 9 +-- modules/denix/abstractions/hosts.nix | 24 ++++--- modules/denix/abstractions/modules.nix | 98 +++++++++++++------------- 3 files changed, 64 insertions(+), 67 deletions(-) diff --git a/lib/modules/default.nix b/lib/modules/default.nix index 1939b89..caf4984 100644 --- a/lib/modules/default.nix +++ b/lib/modules/default.nix @@ -63,14 +63,7 @@ delib._callLib ./denixArgs.nix { key = "denix.genModule"; - imports = lib.concatLists ( - lib.mapAttrsToList ( - moduleSystemName: rawModules: - lib.concatMap - configurationWithModules.config.moduleSystems.${moduleSystemName}.applyConfigForModuleSystem - rawModules - ) configurationWithModules.config.rawModules - ); + imports = lib.concatAttrValues configurationWithModules.config.rawModules; }; genSystem = diff --git a/modules/denix/abstractions/hosts.nix b/modules/denix/abstractions/hosts.nix index 1469eca..ea20ed5 100644 --- a/modules/denix/abstractions/hosts.nix +++ b/modules/denix/abstractions/hosts.nix @@ -34,17 +34,19 @@ in config.rawModules = lib.mapAttrs ( moduleSystemName: moduleSystem: - lib.concatLists ( - lib.mapAttrsToList ( - hostName: host: - lib.imap1 (mapItem hostName moduleSystemName ".always") host.${moduleSystemName}.always - ++ lib.optionals (config.host.name or null == hostName) ( - lib.imap1 (mapItem hostName moduleSystemName ".ifEnabled") host.${moduleSystemName}.ifEnabled - ) - ++ lib.optionals (config.host.name or null != hostName) ( - lib.imap1 (mapItem hostName moduleSystemName ".ifDisabled") host.${moduleSystemName}.ifDisabled - ) - ) config.hosts + builtins.concatMap moduleSystem.applyConfigForModuleSystem ( + lib.concatLists ( + lib.mapAttrsToList ( + hostName: host: + lib.imap1 (mapItem hostName moduleSystemName ".always") host.${moduleSystemName}.always + ++ lib.optionals (config.host.name or null == hostName) ( + lib.imap1 (mapItem hostName moduleSystemName ".ifEnabled") host.${moduleSystemName}.ifEnabled + ) + ++ lib.optionals (config.host.name or null != hostName) ( + lib.imap1 (mapItem hostName moduleSystemName ".ifDisabled") host.${moduleSystemName}.ifDisabled + ) + ) config.hosts + ) ) ) config.moduleSystems; } diff --git a/modules/denix/abstractions/modules.nix b/modules/denix/abstractions/modules.nix index 696e518..3309b5b 100644 --- a/modules/denix/abstractions/modules.nix +++ b/modules/denix/abstractions/modules.nix @@ -46,54 +46,56 @@ in config.rawModules = lib.mapAttrs ( moduleSystemName: moduleSystem: - let - myconfigPrefixWithDot = lib.optionalString ( - moduleSystem.myconfigPrefix != null - ) "${moduleSystem.myconfigPrefix}."; - in - lib.concatLists ( - lib.mapAttrsToList ( - moduleName: module: - let - specialAttrs = x: delib.keepAttrs x [ "_file" ]; - nonSpecialAttrs = x: delib.removeAttrs x [ "_file" ]; - in - let - contentOptions = - entry: - (specialAttrs entry) - // { - options = delib.setAttrByStrPath (nonSpecialAttrs entry) moduleName; - }; - contentAlways = entry: entry; - contentIfEnabled = - entry: config: - (specialAttrs entry) - // lib.mkIf (delib.getAttrByStrPath config "${myconfigPrefixWithDot}${moduleName}.enable" false) ( - nonSpecialAttrs entry - ); - contentIfDisabled = - entry: config: - (specialAttrs entry) - // lib.mkIf (!delib.getAttrByStrPath config "${myconfigPrefixWithDot}${moduleName}.enable" true) ( - nonSpecialAttrs entry - ); - in - if moduleSystemName == "myconfig" then - lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix "" - contentOptions - ) module.options - else - lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix ".always" - contentAlways - ) module.${moduleSystemName}.always - ++ lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix ".ifEnabled" - contentIfEnabled - ) module.${moduleSystemName}.ifEnabled - ++ lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix ".ifDisabled" - contentIfDisabled - ) module.${moduleSystemName}.ifDisabled - ) config.modules + builtins.concatMap moduleSystem.applyConfigForModuleSystem ( + let + myconfigPrefixWithDot = lib.optionalString ( + moduleSystem.myconfigPrefix != null + ) "${moduleSystem.myconfigPrefix}."; + in + lib.concatLists ( + lib.mapAttrsToList ( + moduleName: module: + let + specialAttrs = x: delib.keepAttrs x [ "_file" ]; + nonSpecialAttrs = x: delib.removeAttrs x [ "_file" ]; + in + let + contentOptions = + entry: + (specialAttrs entry) + // { + options = delib.setAttrByStrPath (nonSpecialAttrs entry) moduleName; + }; + contentAlways = entry: entry; + contentIfEnabled = + entry: config: + (specialAttrs entry) + // lib.mkIf (delib.getAttrByStrPath config "${myconfigPrefixWithDot}${moduleName}.enable" false) ( + nonSpecialAttrs entry + ); + contentIfDisabled = + entry: config: + (specialAttrs entry) + // lib.mkIf (!delib.getAttrByStrPath config "${myconfigPrefixWithDot}${moduleName}.enable" true) ( + nonSpecialAttrs entry + ); + in + if moduleSystemName == "myconfig" then + lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix "" + contentOptions + ) module.options + else + lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix ".always" + contentAlways + ) module.${moduleSystemName}.always + ++ lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix ".ifEnabled" + contentIfEnabled + ) module.${moduleSystemName}.ifEnabled + ++ lib.imap1 (mapItem moduleName moduleSystemName moduleSystem.myconfigPrefix ".ifDisabled" + contentIfDisabled + ) module.${moduleSystemName}.ifDisabled + ) config.modules + ) ) ) config.moduleSystems; }