From 65b36eb2cb28cdf405eff94a7ff71fcc0fdaffdd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 5 Jan 2026 09:29:29 +0100 Subject: [PATCH 1/7] Remove mkSubmoduleOptions usages, document as deprecated The workaround is no longer needed since Nixpkgs 22.05 (https://github.com/NixOS/nixpkgs/pull/156533). Declaring options directly in a submodule now works, e.g. `options.flake.foo`. The function is kept for backwards compatibility but documented as deprecated. The minimum supported Nixpkgs lib version is already 22.05, so this change does not drop support for any previously supported version. --- lib.nix | 42 ++++++++++++++++++--------- modules/formatter.nix | 15 ++++------ modules/nixosConfigurations.nix | 51 +++++++++++++++------------------ modules/nixosModules.nix | 31 +++++++++----------- modules/overlays.nix | 45 +++++++++++++---------------- 5 files changed, 91 insertions(+), 93 deletions(-) diff --git a/lib.nix b/lib.nix index 76a96e3..09ee1e8 100644 --- a/lib.nix +++ b/lib.nix @@ -141,8 +141,26 @@ let in eval.config.flake; - # For extending options in an already declared submodule. - # Workaround for https://github.com/NixOS/nixpkgs/issues/146882 + /** + Deprecated. Declare options directly, e.g. `options.foo.bar = mkOption { ... }`, + provided that `foo` is already declared as a submodule option. + + In flake-parts, `flake` is declared as a submodule option by the core modules, + so `options.flake.` declarations work directly. + + This function wraps option declarations in a submodule, allowing them to + be merged into an existing submodule option. For example, if `foo` is + already declared as a submodule option, using + `options.foo = mkSubmoduleOptions { bar = mkOption {...}; }` would add + `bar` to the `foo` submodule. + + # History + + This was a workaround for https://github.com/NixOS/nixpkgs/issues/146882, + fixed in Nixpkgs 22.05 by https://github.com/NixOS/nixpkgs/pull/156533. + With the fix, declaring `options.foo.bar` directly works when `foo` is + already a submodule option. Documented as deprecated in flake-parts in January 2026. + */ mkSubmoduleOptions = options: mkOption { @@ -177,18 +195,16 @@ let _file = file; options = { - flake = flake-parts-lib.mkSubmoduleOptions { - ${name} = mkOption { - type = attrsWith { - elemType = option.type; - lazy = true; - placeholder = "system"; - }; - default = { }; - description = '' - See {option}`perSystem.${name}` for description and examples. - ''; + flake.${name} = mkOption { + type = attrsWith { + elemType = option.type; + lazy = true; + placeholder = "system"; }; + default = { }; + description = '' + See {option}`perSystem.${name}` for description and examples. + ''; }; perSystem = flake-parts-lib.mkPerSystemOption { diff --git a/modules/formatter.nix b/modules/formatter.nix index 838b7cf..e2959ed 100644 --- a/modules/formatter.nix +++ b/modules/formatter.nix @@ -8,20 +8,17 @@ let types ; inherit (flake-parts-lib) - mkSubmoduleOptions mkPerSystemOption ; in { options = { - flake = mkSubmoduleOptions { - formatter = mkOption { - type = types.lazyAttrsOf types.package; - default = { }; - description = '' - An attribute set of per system a package used by [`nix fmt`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-fmt.html). - ''; - }; + flake.formatter = mkOption { + type = types.lazyAttrsOf types.package; + default = { }; + description = '' + An attribute set of per system a package used by [`nix fmt`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-fmt.html). + ''; }; perSystem = mkPerSystemOption { diff --git a/modules/nixosConfigurations.nix b/modules/nixosConfigurations.nix index 7dc973b..597132d 100644 --- a/modules/nixosConfigurations.nix +++ b/modules/nixosConfigurations.nix @@ -1,41 +1,36 @@ -{ lib, flake-parts-lib, ... }: +{ lib, ... }: let inherit (lib) mkOption types literalExpression ; - inherit (flake-parts-lib) - mkSubmoduleOptions - ; in { options = { - flake = mkSubmoduleOptions { - nixosConfigurations = mkOption { - type = types.lazyAttrsOf types.raw; - default = { }; - description = '' - Instantiated NixOS configurations. Used by `nixos-rebuild`. + flake.nixosConfigurations = mkOption { + type = types.lazyAttrsOf types.raw; + default = { }; + description = '' + Instantiated NixOS configurations. Used by `nixos-rebuild`. - `nixosConfigurations` is for specific machines. If you want to expose - reusable configurations, add them to [`nixosModules`](#opt-flake.nixosModules) - in the form of modules (no `lib.nixosSystem`), so that you can reference - them in this or another flake's `nixosConfigurations`. - ''; - example = literalExpression '' - { - my-machine = inputs.nixpkgs.lib.nixosSystem { - # system is not needed with freshly generated hardware-configuration.nix - # system = "x86_64-linux"; # or set nixpkgs.hostPlatform in a module. - modules = [ - ./my-machine/nixos-configuration.nix - config.nixosModules.my-module - ]; - }; - } - ''; - }; + `nixosConfigurations` is for specific machines. If you want to expose + reusable configurations, add them to [`nixosModules`](#opt-flake.nixosModules) + in the form of modules (no `lib.nixosSystem`), so that you can reference + them in this or another flake's `nixosConfigurations`. + ''; + example = literalExpression '' + { + my-machine = inputs.nixpkgs.lib.nixosSystem { + # system is not needed with freshly generated hardware-configuration.nix + # system = "x86_64-linux"; # or set nixpkgs.hostPlatform in a module. + modules = [ + ./my-machine/nixos-configuration.nix + config.nixosModules.my-module + ]; + }; + } + ''; }; }; } diff --git a/modules/nixosModules.nix b/modules/nixosModules.nix index 737648d..86ee9cc 100644 --- a/modules/nixosModules.nix +++ b/modules/nixosModules.nix @@ -1,31 +1,26 @@ -{ self, lib, flake-parts-lib, moduleLocation, ... }: +{ self, lib, moduleLocation, ... }: let inherit (lib) mapAttrs mkOption types ; - inherit (flake-parts-lib) - mkSubmoduleOptions - ; in { options = { - flake = mkSubmoduleOptions { - nixosModules = mkOption { - type = types.lazyAttrsOf types.deferredModule; - default = { }; - apply = mapAttrs (k: v: { - _class = "nixos"; - _file = "${toString moduleLocation}#nixosModules.${k}"; - imports = [ v ]; - }); - description = '' - NixOS modules. + flake.nixosModules = mkOption { + type = types.lazyAttrsOf types.deferredModule; + default = { }; + apply = mapAttrs (k: v: { + _class = "nixos"; + _file = "${toString moduleLocation}#nixosModules.${k}"; + imports = [ v ]; + }); + description = '' + NixOS modules. - You may use this for reusable pieces of configuration, service modules, etc. - ''; - }; + You may use this for reusable pieces of configuration, service modules, etc. + ''; }; }; } diff --git a/modules/overlays.nix b/modules/overlays.nix index a09e8f6..84285ff 100644 --- a/modules/overlays.nix +++ b/modules/overlays.nix @@ -1,37 +1,32 @@ -{ lib, flake-parts-lib, ... }: +{ lib, ... }: let inherit (lib) mkOption types ; - inherit (flake-parts-lib) - mkSubmoduleOptions - ; in { options = { - flake = mkSubmoduleOptions { - overlays = mkOption { - # uniq -> ordered: https://github.com/NixOS/nixpkgs/issues/147052 - # also update description when done - type = types.lazyAttrsOf (types.uniq (types.functionTo (types.functionTo (types.lazyAttrsOf types.unspecified)))); - # This eta expansion exists for the sole purpose of making nix flake check happy. - apply = lib.mapAttrs (_k: f: final: prev: f final prev); - default = { }; - example = lib.literalExpression or lib.literalExample '' - { - default = final: prev: {}; - } - ''; - description = '' - An attribute set of [overlays](https://nixos.org/manual/nixpkgs/stable/#chap-overlays). + flake.overlays = mkOption { + # uniq -> ordered: https://github.com/NixOS/nixpkgs/issues/147052 + # also update description when done + type = types.lazyAttrsOf (types.uniq (types.functionTo (types.functionTo (types.lazyAttrsOf types.unspecified)))); + # This eta expansion exists for the sole purpose of making nix flake check happy. + apply = lib.mapAttrs (_k: f: final: prev: f final prev); + default = { }; + example = lib.literalExpression or lib.literalExample '' + { + default = final: prev: {}; + } + ''; + description = '' + An attribute set of [overlays](https://nixos.org/manual/nixpkgs/stable/#chap-overlays). - Note that the overlays themselves are not mergeable. While overlays - can be composed, the order of composition is significant, but the - module system does not guarantee sufficiently deterministic - definition ordering, across versions and when changing `imports`. - ''; - }; + Note that the overlays themselves are not mergeable. While overlays + can be composed, the order of composition is significant, but the + module system does not guarantee sufficiently deterministic + definition ordering, across versions and when changing `imports`. + ''; }; }; } From bd4b9dd600ce04f0e0ceddfd05cc435032d3ecdf Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 5 Jan 2026 09:43:04 +0100 Subject: [PATCH 2/7] Remove unnecessary lib fallbacks literalExpression was added in Nixpkgs 21.11, getExe in 22.05. The minimum supported lib version is 22.05, so these fallbacks are unnecessary. --- modules/apps.nix | 9 ++------- modules/overlays.nix | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/modules/apps.nix b/modules/apps.nix index 2f30a77..3030d32 100644 --- a/modules/apps.nix +++ b/modules/apps.nix @@ -8,12 +8,7 @@ let mkTransposedPerSystemModule ; - getExe = lib.getExe or ( - x: - "${lib.getBin x}/bin/${x.meta.mainProgram or (throw ''Package ${x.name or ""} does not have meta.mainProgram set, so I don't know how to find the main executable. You can set meta.mainProgram, or pass the full path to executable, e.g. program = "''${pkg}/bin/foo"'')}" - ); - - programType = lib.types.coercedTo derivationType getExe lib.types.str; + programType = lib.types.coercedTo derivationType lib.getExe lib.types.str; derivationType = lib.types.package // { check = lib.isDerivation; @@ -56,7 +51,7 @@ mkTransposedPerSystemModule { description = '' Programs runnable with nix run ``. ''; - example = lib.literalExpression or lib.literalExample '' + example = lib.literalExpression '' { default.program = "''${config.packages.hello}/bin/hello"; } diff --git a/modules/overlays.nix b/modules/overlays.nix index 84285ff..172336c 100644 --- a/modules/overlays.nix +++ b/modules/overlays.nix @@ -14,7 +14,7 @@ in # This eta expansion exists for the sole purpose of making nix flake check happy. apply = lib.mapAttrs (_k: f: final: prev: f final prev); default = { }; - example = lib.literalExpression or lib.literalExample '' + example = lib.literalExpression '' { default = final: prev: {}; } From ef399e1230d637d6cf35da6aaee64b9aa2eb5965 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 5 Jan 2026 10:18:02 +0100 Subject: [PATCH 3/7] lib: Remove lib.deferredModuleWith fallback The `lib.deferredModuleWith or (...)` pattern never matched because `lib.deferredModuleWith` doesn't exist - only `lib.types.deferredModuleWith`. This was always using flake-parts' own implementation. --- lib.nix | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib.nix b/lib.nix index 09ee1e8..8495a93 100644 --- a/lib.nix +++ b/lib.nix @@ -33,7 +33,7 @@ let else maybeFlake ? inputs && maybeFlake ? outputs && maybeFlake ? sourceInfo; # Polyfill https://github.com/NixOS/nixpkgs/pull/163617 - deferredModuleWith = lib.deferredModuleWith or ( + deferredModuleWith = attrs@{ staticModules ? [ ] }: mkOptionType { name = "deferredModule"; description = "module"; @@ -54,8 +54,7 @@ let staticModules = lhs.staticModules ++ rhs.staticModules; }; }; - } - ); + }; errorExample = '' For example: From 8abc1c2ee541acd1f9496dd5a544e91629e8f1a9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 5 Jan 2026 10:19:30 +0100 Subject: [PATCH 4/7] lib: Document deferredModuleWith as deprecated The deferredModule type was pioneered in flake-parts for perSystem. The Nixpkgs version (lib.types.deferredModuleWith) has an improved merge function that returns a single module, whereas this version returns a list. --- lib.nix | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib.nix b/lib.nix index 8495a93..ff0914f 100644 --- a/lib.nix +++ b/lib.nix @@ -32,7 +32,21 @@ let then maybeFlake._type == "flake" else maybeFlake ? inputs && maybeFlake ? outputs && maybeFlake ? sourceInfo; - # Polyfill https://github.com/NixOS/nixpkgs/pull/163617 + /** + Deprecated for any use except type-merging into `perSystem`. + Use `lib.types.deferredModuleWith` instead, and add `apply = m: [ m ];` if needed. + + The deferredModule type was pioneered in flake-parts for the `perSystem` option. + The Nixpkgs version has an improved merge function that returns a single module, + whereas this version returns a list. The flake-parts version was not updated to + match this improvement in Nixpkgs. + + # History + + This predates `lib.types.deferredModuleWith`, added in Nixpkgs 22.11 + (https://github.com/NixOS/nixpkgs/pull/163617). + Documented as deprecated in flake-parts in January 2026. + */ deferredModuleWith = attrs@{ staticModules ? [ ] }: mkOptionType { name = "deferredModule"; From e8afcc669c81bf0bacdc58566f940902a51c86af Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 5 Jan 2026 10:19:55 +0100 Subject: [PATCH 5/7] lib: Bump minVersion to 23.05 flake-parts uses `evalModules` with the `class` argument, which was added in Nixpkgs 23.05. Update minVersion to reflect the actual minimum supported version. --- lib.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.nix b/lib.nix index ff0914f..a203d55 100644 --- a/lib.nix +++ b/lib.nix @@ -281,7 +281,7 @@ let # A best effort, lenient estimate. Please use a recent nixpkgs lib if you # override it at all. - minVersion = "22.05"; + minVersion = "23.05pre-git"; in From 1da4b5a42f784d02a910d50f25088482bca08cf9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 5 Jan 2026 11:20:29 +0100 Subject: [PATCH 6/7] lib: Deprecate mkDeferredModuleType, mkDeferredModuleOption Rename to mkPerSystemType and mkPerSystemOption respectively, since the flake-parts deferredModule implementation has different semantics than Nixpkgs' types.deferredModuleWith (list merge vs single module). The old names are kept as deprecated aliases. Add mkLegacyDeferredModuleType as internal helper preserving the list-merge behavior. --- lib.nix | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/lib.nix b/lib.nix index a203d55..c8ea2ba 100644 --- a/lib.nix +++ b/lib.nix @@ -70,6 +70,13 @@ let }; }; + # Internal: preserves legacy list-merge behavior for perSystem type-merging. + mkLegacyDeferredModuleType = + module: + deferredModuleWith { + staticModules = [ module ]; + }; + errorExample = '' For example: @@ -182,18 +189,32 @@ let }; }; - mkDeferredModuleType = - module: - deferredModuleWith { - staticModules = [ module ]; - }; - mkPerSystemType = mkDeferredModuleType; + /** + Deprecated. Use mkPerSystemType/mkPerSystemOption for `perSystem` type-merging, or + use Nixpkgs `types.deferredModule` directly, noting the lack of list wrapping; + see `deferredModuleWith` docs. + */ + mkDeferredModuleType = mkLegacyDeferredModuleType; + /** + Given a module, construct an option type suitable for type-merging into `perSystem`'s type. + */ + mkPerSystemType = mkLegacyDeferredModuleType; + + /** + Deprecated. Use mkPerSystemOption for `perSystem` type-merging, or + use `mkOption` and Nixpkgs `types.deferredModule` directly, noting the + lack of list wrapping; see `deferredModuleWith` docs. + */ mkDeferredModuleOption = module: mkOption { type = flake-parts-lib.mkPerSystemType module; }; + + /** + Given a module, construct an option declaration suitable for merging into the core `perSystem` module. + */ mkPerSystemOption = mkDeferredModuleOption; # Polyfill https://github.com/NixOS/nixpkgs/pull/344216 From 341edfddc6d8d4aefbac57de6b81bc3a11ab67ed Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 5 Jan 2026 11:34:31 +0100 Subject: [PATCH 7/7] ChangeLog: Document earlier breaking change and deprecations Add entry for 2024-05-16 when class argument was introduced, which raised the minimum Nixpkgs lib version to 23.05. Add entry for today's deprecations of mkSubmoduleOptions, mkDeferredModuleType, and mkDeferredModuleOption. --- ChangeLog.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 6103b3b..2a046d4 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,24 @@ +# 2026-01-05 + + - Deprecated `mkSubmoduleOptions`. Declare options directly instead, e.g. + `options.flake.foo = mkOption { ... }`. This works since Nixpkgs 22.05. + + - Deprecated `mkDeferredModuleType` and `mkDeferredModuleOption`. Use + `mkPerSystemType` and `mkPerSystemOption` respectively for `perSystem` + type-merging. For other uses, use Nixpkgs' `types.deferredModuleWith`. + + Note: flake-parts' implementation returns a list on merge, whereas Nixpkgs' + returns a single module. Add `apply = m: [ m ];` to your option if you need + the list behavior. + +# 2024-05-16 + + - **Breaking**: Minimum supported Nixpkgs lib version is now 23.05 (was 22.05), + due to the use of the `class` argument in `evalModules`. + + - Add `class` to `evalModules` calls for imports "type checking". + # 2023-05-30 - Fix a strictness issue in `perInput`, affecting `inputs'`, `self'`.