diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index abaeba06..47cc8209 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,8 +20,6 @@ jobs: # Execute /tests/* - run: nix develop --command check-derivation-outputs - - run: nix develop --command check-derivation-outputs-old - - run: nix develop --command check-channel-patching - run: nix develop --command check-overlays-flow - run: nix develop --command check-hosts-config diff --git a/README.md b/README.md index 47d7d793..94d6ab16 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ Main flake-utils-plus features (Attributes visible from `flake.nix`): - Option [`nix.generateNixPathFromInputs`](./lib/options.nix) - Generate `nix.nixPath` from available inputs. - Option [`nix.linkInputs`](./lib/options.nix) - Symlink inputs to /etc/nix/inputs. - Simple and clean support for multiple `nixpkgs` references. - - `nixpkgs` references patching. - `channelsConfig` - Config applied to all `nixpkgs` references. - `hostDefaults` - Default configuration shared between host definitions. - `outputsBuilder` - Clean way to export packages/apps/etc. @@ -92,9 +91,6 @@ in flake-utils-plus.lib.mkFlake { # Channel specific config options. channels..config = { allowUnfree = true; }; - # Patches to apply on selected channel. - channels..patches = [ ./someAwesomePatch.patch ]; - # Overlays to apply on a selected channel. channels..overlaysBuilder = channels: [ (final: prev: { inherit (channels.unstable) neovim; }) diff --git a/devShell.nix b/devShell.nix index 3b0b9909..1cce5461 100644 --- a/devShell.nix +++ b/devShell.nix @@ -68,12 +68,10 @@ devshell.mkShell { command = "fd --extension nix --exec nix-instantiate --parse --quiet {} >/dev/null"; } - (test "channel-patching") (test "derivation-outputs") - (test "derivation-outputs-old") (test "hosts-config") (test "overlays-flow") - (test "all" // { command = "check-channel-patching && check-derivation-outputs && check-derivation-outputs-old && check-hosts-config && check-overlays-flow"; }) + (test "all" // { command = "check-derivation-outputs && check-hosts-config && check-overlays-flow"; }) (dry-nixos-build "minimal-multichannel" "Hostname1") (dry-nixos-build "minimal-multichannel" "Hostname2") diff --git a/examples/exporters/flake.nix b/examples/exporters/flake.nix index 09a25cbb..5f1db630 100644 --- a/examples/exporters/flake.nix +++ b/examples/exporters/flake.nix @@ -16,6 +16,7 @@ inherit self inputs; # Channel specific overlays. Overlays `coreutils` from `unstable` channel. + channels.unstable = { }; channels.nixpkgs.overlaysBuilder = channels: [ (final: prev: { inherit (channels.unstable) ranger; }) ]; diff --git a/examples/home-manager+nur+neovim/flake.nix b/examples/home-manager+nur+neovim/flake.nix index e4cf40d9..4482c56c 100644 --- a/examples/home-manager+nur+neovim/flake.nix +++ b/examples/home-manager+nur+neovim/flake.nix @@ -26,6 +26,7 @@ channelsConfig.allowUnfree = true; + channels.nixpkgs = { }; sharedOverlays = [ nur.overlay diff --git a/examples/minimal-multichannel/flake.nix b/examples/minimal-multichannel/flake.nix index 56e5109d..8283579d 100644 --- a/examples/minimal-multichannel/flake.nix +++ b/examples/minimal-multichannel/flake.nix @@ -16,6 +16,8 @@ # Channels are automatically generated from nixpkgs inputs # e.g the inputs which contain `legacyPackages` attribute are used. channelsConfig.allowUnfree = true; + channels.nixpkgs = { }; + channels.unstable = { }; # Modules shared between all hosts diff --git a/flake.nix b/flake.nix index f4c5a515..034e8aaf 100644 --- a/flake.nix +++ b/flake.nix @@ -24,26 +24,16 @@ rec { inherit overlay; - # Deprecated in favor of 'nix.generateRegistryFromInputs = true;' - nixosModules.saneFlakeDefaults = { nix.generateRegistryFromInputs = true; }; nixosModules.autoGenFromInputs = import ./lib/options.nix; devShell.x86_64-linux = import ./devShell.nix { system = "x86_64-linux"; }; lib = flake-utils.lib // { - inherit mkFlake exportModules exportOverlays exportPackages; - - # Deprecated - should use top-level functions instead - exporters = { - inherit modulesFromList fromOverlays internalOverlays; - }; - inherit systemFlake modulesFromList; + inherit mkFlake exportModules exportOverlays exportPackages systemFlake modulesFromList; # DO NOT USE - subject to change without notice internal = internal-functions; - repl = ./lib/repl.nix; - # merge nested attribute sets and lists mergeAny = lhs: rhs: lhs // mapAttrs @@ -53,15 +43,6 @@ else value ) rhs; - - patchChannel = system: channel: patches: - if patches == [ ] then channel else - (import channel { inherit system; }).pkgs.applyPatches { - name = if channel ? shortRev then "nixpkgs-patched-${channel.shortRev}" else "nixpkgs-patched"; - src = channel; - patches = patches; - }; - }; }; } diff --git a/lib/mkFlake.nix b/lib/mkFlake.nix index d2745341..228eaa84 100644 --- a/lib/mkFlake.nix +++ b/lib/mkFlake.nix @@ -1,32 +1,18 @@ { flake-utils-plus }: { self -, defaultSystem ? "x86_64-linux" # will be deprecated soon use hostDefaults.system instead , supportedSystems ? flake-utils-plus.lib.defaultSystems , inputs - , channels ? { } , channelsConfig ? { } , sharedOverlays ? [ ] - -, nixosProfiles ? { } # will be deprecated soon, use hosts, instead. -, hosts ? nixosProfiles -, sharedExtraArgs ? { } # deprecate soon, prefer hostDefaults -, sharedModules ? [ ] # deprecate soon, prefer hostDefaults +, hosts ? { } , hostDefaults ? { - system = defaultSystem; - modules = sharedModules; - extraArgs = sharedExtraArgs; + system = "x86_64-linux"; + modules = [ ]; + extraArgs = { }; } - -, outputsBuilder ? null - -, packagesBuilder ? null -, defaultPackageBuilder ? null -, appsBuilder ? null -, defaultAppBuilder ? null -, devShellBuilder ? null -, checksBuilder ? null +, outputsBuilder ? _: { } , ... }@args: @@ -34,7 +20,6 @@ let inherit (flake-utils-plus.lib) eachSystem mergeAny - patchChannel ; inherit (flake-utils-plus.lib.internal) filterAttrs @@ -42,7 +27,6 @@ let reverseList ; inherit (builtins) - pathExists attrNames attrValues concatMap @@ -52,10 +36,13 @@ let foldl' genList head + isAttrs + isFunction isString length listToAttrs mapAttrs + pathExists removeAttrs split tail @@ -82,8 +69,6 @@ let optionalAttrs = check: value: if check then value else { }; otherArguments = removeAttrs args [ - "defaultSystem" # TODO: deprecated, remove - "sharedExtraArgs" # deprecated "inputs" "hosts" "hostDefaults" @@ -91,21 +76,38 @@ let "channels" "channelsConfig" "self" - "sharedModules" # deprecated "sharedOverlays" "supportedSystems" - - "outputsBuilder" - "packagesBuilder" - "defaultPackageBuilder" - "appsBuilder" - "defaultAppBuilder" - "devShellBuilder" - "checksBuilder" ]; getChannels = system: self.pkgs.${system}; + ensureChannelsWitsInputs = mapAttrs + (n: v: + if (!v ? input) then + v // { + input = inputs.${n} or ( + throw '' + No input is inferable by name from flake inputs for channel "${n}" + ''); + } + else v + ) + channels; getNixpkgs = host: (getChannels host.system).${host.channelName}; + mergeNixpkgsConfigs = input: lconf: rconf: ( + input.lib.evalModules { + modules = [ + "${input}/nixos/modules/misc/assertions.nix" + "${input}/nixos/modules/misc/nixpkgs.nix" + { + nixpkgs.config = lconf; + } + { + nixpkgs.config = rconf; + } + ]; + } + ).config.nixpkgs.config; configurationBuilder = reverseDomainName: host': ( let @@ -118,99 +120,111 @@ let if domainLabels == [ ] then (lib.mkDefault null) # null is the networking.domain option's default else concatStringsSep "." domainLabels; - selectedNixpkgs = getNixpkgs host; host = evalHostArgs (mergeAny hostDefaults host'); - patchedChannel = selectedNixpkgs.path; - channels = getChannels host.system; + selectedNixpkgs = getNixpkgs host; + channels' = getChannels host.system; + lib = selectedNixpkgs.lib; specialArgs = host.specialArgs // { channel = selectedNixpkgs; }; /* nixos specific arguments */ - # Use lib from patched nixpkgs - lib = selectedNixpkgs.lib; - # Use nixos modules from patched nixpkgs - baseModules = import (patchedChannel + "/nixos/modules/module-list.nix"); nixosSpecialArgs = let f = channelName: - { "${channelName}ModulesPath" = toString (channels.${channelName}.input + "/nixos/modules"); }; + { "${channelName}ModulesPath" = toString (channels'.${channelName}.input + "/nixos/modules"); }; in # Add `ModulesPath`s - (foldl' (lhs: rhs: lhs // rhs) { } (map f (attrNames channels))) - # Override `modulesPath` because otherwise imports from there will not use patched nixpkgs - // { modulesPath = toString (patchedChannel + "/nixos/modules"); }; - - - # The only way to find out if a host has `nixpkgs.config` set to - # the non-default value is by evalling most of the config. - hostConfig = (lib.evalModules { - prefix = [ ]; - check = false; - modules = baseModules ++ host.modules; - args = { inherit inputs; } // host.extraArgs; - specialArgs = nixosSpecialArgs // specialArgs; - }).config; - in - { - ${host.output}.${reverseDomainName} = host.builder ({ - inherit (host) system; - modules = [ - ({ pkgs, lib, options, config, ... }: { - # 'mkMerge` to separate out each part into its own module - _type = "merge"; - contents = [ - (optionalAttrs (options ? networking.hostName) { - networking.hostName = hostname; - }) - - (optionalAttrs (options ? networking.domain) { - networking.domain = domain; - }) - - (if options ? nixpkgs.pkgs then - { - nixpkgs.config = selectedNixpkgs.config; - nixpkgs.pkgs = - # Make sure we don't import nixpkgs again if not - # necessary. We can't use `config.nixpkgs.config` - # because that triggers infinite recursion. - if (hostConfig.nixpkgs.config == { }) then - selectedNixpkgs - else - import patchedChannel - { - inherit (host) system; - overlays = selectedNixpkgs.overlays; - config = selectedNixpkgs.config // config.nixpkgs.config; - } // { inherit (selectedNixpkgs) name input; }; - } - else { _module.args.pkgs = selectedNixpkgs; }) + (foldl' (lhs: rhs: lhs // rhs) { } (map f (attrNames channels'))) + ; + + # genericModule MUST work gracefully with distinct module sets and + # cannot make any assumption other than the nixpkgs module system + # is used. + # See: https://github.com/NixOS/nixpkgs/blob/master/lib/modules.nix + # Exemplary module sets are: nixos, darwin, home-manager, etc + genericModule = preflight: { pkgs, lib, options, ... }: { + # 'mkMerge` to separate out each part into its own module + _type = "merge"; + contents = ( + if ((preflight == null) || (!options ? nixpkgs.pkgs)) then + # equivalent to nixpkgs.pkgs = selectedNixpkgs + [{ _module.args.pkgs = selectedNixpkgs; }] + else + # if preflight.nixpkgs.config == {}, + # then the memorized evaluation of selectedNixpkgs will be used + # and we won't incur in an additional (expensive) evaluation. + # This works because nixos invokes at some point the same function + # with the same arguments as we already have in importChannel. + # DYOR: + # -> https://github.com/NixOS/nixpkgs/blob/b63a54f81ce96391e6da6aab5965926e7cdbce47/nixos/modules/misc/nixpkgs.nix#L58-L60 + # -> https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/default.nix + # -> lib.systems.elaborate is idempotent + [ + { + nixpkgs = { inherit (selectedNixpkgs) system overlays; }; + } + { + # however, let the module system merge -> advanced config merge (sic!) + nixpkgs = { inherit (selectedNixpkgs) config; }; + } + { + nixpkgs = { inherit (preflight.config.nixpkgs) config; }; + } + ] + ) + ++ + [ + { + _module.args = { inherit inputs; } // host.extraArgs; + } + + (optionalAttrs (options ? networking.hostName) { + networking.hostName = hostname; + }) - (optionalAttrs (options ? system.configurationRevision) { - system.configurationRevision = lib.mkIf (self ? rev) self.rev; - }) + (optionalAttrs (options ? networking.domain) { + networking.domain = domain; + }) - (optionalAttrs (options ? nix.package) { - nix.package = lib.mkDefault pkgs.nixUnstable; - }) + (optionalAttrs (options ? system.configurationRevision) { + system.configurationRevision = lib.mkIf (self ? rev) self.rev; + }) - (optionalAttrs (options ? nix.extraOptions) { - nix.extraOptions = "extra-experimental-features = nix-command ca-references flakes"; - }) + (optionalAttrs (options ? nix.package) { + nix.package = lib.mkDefault pkgs.nixUnstable; + }) - { - # at this point we assume, that an evaluator at least - # uses nixpkgs.lib to evaluate modules. - _module.args = { inherit inputs; } // host.extraArgs; - } - ]; + (optionalAttrs (options ? nix.extraOptions) { + nix.extraOptions = "extra-experimental-features = nix-command ca-references flakes"; }) - ] ++ host.modules; + ]; + }; + + evalArgs = { + inherit (host) system; + modules = [ (genericModule null) ] ++ host.modules; inherit specialArgs; - } // (optionalAttrs (host.output == "nixosConfigurations") { - inherit lib baseModules; + } + // + (optionalAttrs (host.output == "nixosConfigurations") { specialArgs = nixosSpecialArgs // specialArgs; - })); + } + ); + + # The only way to find out if a host has `nixpkgs.config` set to + # the non-default value is by evalling the config. + # If it's not set, repeating the evaluation is cheap since + # all module evaluations except misc/nixpkgs.nix are memorized + # since `pkgs` would not change. + preFlightEvaled = host.builder (evalArgs // { + modules = [ (genericModule null) ] ++ host.modules; + }); + + in + { + ${host.output}.${reverseDomainName} = host.builder (evalArgs // { + modules = [ (genericModule preFlightEvaled) ] ++ host.modules; + }); } ); @@ -220,37 +234,25 @@ mergeAny otherArguments ( eachSystem supportedSystems (system: let - filterAttrs = pred: set: - listToAttrs (concatMap (name: let value = set.${name}; in if pred name value then [ ({ inherit name value; }) ] else [ ]) (attrNames set)); - - # Little hack, we make sure that `legacyPackages` contains `nix` to make sure that we are dealing with nixpkgs. - # For some odd reason `devshell` contains `legacyPackages` out put as well - channelFlakes = filterAttrs (_: value: value ? legacyPackages && value.legacyPackages.x86_64-linux ? nix) inputs; - channelsFromFlakes = mapAttrs (name: input: { inherit input; }) channelFlakes; - - importChannel = name: value: (import (patchChannel system value.input (value.patches or [ ])) { + importChannel = name: value: (import value.input { inherit system; overlays = [ (final: prev: { __dontExport = true; # in case user uses overlaysFromChannelsExporter, doesn't hurt for others - inherit srcs; + inherit srcs name; + inherit (value) input; }) - ] ++ sharedOverlays ++ (if (value ? overlaysBuilder) then (value.overlaysBuilder pkgs) else [ ]) ++ [ flake-utils-plus.overlay ]; - config = channelsConfig // (value.config or { }); - }) // { inherit name; inherit (value) input; }; - - pkgs = mapAttrs importChannel (mergeAny channelsFromFlakes channels); - + ] + ++ + sharedOverlays ++ (if (value ? overlaysBuilder) then (value.overlaysBuilder pkgs) else [ ]) + ++ + [ flake-utils-plus.overlay ]; + config = mergeNixpkgsConfigs value.input channelsConfig (value.config or { }); + }); - deprecatedBuilders = channels: { } - // optionalAttrs (packagesBuilder != null) { packages = packagesBuilder channels; } - // optionalAttrs (defaultPackageBuilder != null) { defaultPackage = defaultPackageBuilder channels; } - // optionalAttrs (appsBuilder != null) { apps = appsBuilder channels; } - // optionalAttrs (defaultAppBuilder != null) { defaultApp = defaultAppBuilder channels; } - // optionalAttrs (devShellBuilder != null) { devShell = devShellBuilder channels; } - // optionalAttrs (checksBuilder != null) { checks = checksBuilder channels; }; + pkgs = mapAttrs importChannel ensureChannelsWitsInputs; - systemOutputs = (if outputsBuilder == null then deprecatedBuilders else outputsBuilder) pkgs; + systemOutputs = outputsBuilder pkgs; mkOutputs = attrs: output: attrs // diff --git a/tests/channel-patching/flake.nix b/tests/channel-patching/flake.nix deleted file mode 100644 index 3de608fc..00000000 --- a/tests/channel-patching/flake.nix +++ /dev/null @@ -1,80 +0,0 @@ -{ - inputs.utils.url = path:../../; - - outputs = inputs@{ self, nixpkgs, utils }: - utils.lib.systemFlake { - inherit self inputs; - supportedSystems = [ "x86_64-linux" ]; - - - - - ################# - ### Test Data ### - ################# - - channelsConfig.allowBroken = true; - - channels.nixpkgs = { - input = nixpkgs; - patches = [ ./myNixpkgsPatch.patch ]; - config.allowUnfree = true; - }; - - hosts.PatchedHost.modules = [ - ({ lib, ... }: { - patchedModule.test = lib.patchedFunction "using patched module via patched function"; - nixpkgs.config.packageOverrides = pkgs: { }; - - # To keep Nix from complaining - boot.loader.grub.devices = [ "nodev" ]; - fileSystems."/" = { device = "test"; fsType = "ext4"; }; - }) - ]; - - - outputsBuilder = channels: { - - packages = { - # Using patched channel - inherit (channels.nixpkgs) flake-utils-plus-test; - }; - - - - ###################### - ### Test execution ### - ###################### - - checks = - let - inherit (utils.lib.check-utils channels.nixpkgs) hasKey isEqual; - hostConfig = self.nixosConfigurations.PatchedHost.config; - in - { - - # Patched package gets passed to `packageBuilder` - patchedPackageGetsPassedToBuilders = isEqual self.packages.x86_64-linux.flake-utils-plus-test.pname "hello"; - - # Modules (and lib) from patched nixpkgs are used - patchedModuleAndFunctionWorks = isEqual hostConfig.patchedModule.test "using patched module via patched function"; - - # `channelsConfig.*` is used - globalChannelConfigWorks = hasKey hostConfig.nixpkgs.pkgs.config "allowBroken"; - - # `channels.nixpkgs.config.*` is also used - channelSpecificConfigWorks = hasKey hostConfig.nixpkgs.pkgs.config "allowUnfree"; - - # `options.nixpkgs.config.*` is also used - modulesNixpkgsConfigWorks = hasKey hostConfig.nixpkgs.pkgs.config "packageOverrides"; - - }; - }; - - - }; -} - - - - diff --git a/tests/channel-patching/myNixpkgsPatch.patch b/tests/channel-patching/myNixpkgsPatch.patch deleted file mode 100644 index 793ba283..00000000 --- a/tests/channel-patching/myNixpkgsPatch.patch +++ /dev/null @@ -1,38 +0,0 @@ -diff --git a/lib/default.nix b/lib/default.nix -index 50320669e28..ff835870e4a 100644 ---- a/lib/default.nix -+++ b/lib/default.nix -@@ -145,5 +145,7 @@ let - nixType imap; - inherit (self.versions) - splitVersion; -+ -+ patchedFunction = x: x; - }); - in lib -diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix -index f72be732216..fbbf9776a6b 100644 ---- a/nixos/modules/module-list.nix -+++ b/nixos/modules/module-list.nix -@@ -1101,4 +1101,10 @@ - ./virtualisation/vmware-guest.nix - ./virtualisation/xen-dom0.nix - ./virtualisation/xe-guest-utilities.nix -+ ({ lib, config, ... }: { -+ options.patchedModule.test = lib.mkOption { -+ default = null; -+ example = "test"; -+ }; -+ }) - ] -diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix -index cdf28b9782c..48ed72bf3c1 100644 ---- a/pkgs/top-level/all-packages.nix -+++ b/pkgs/top-level/all-packages.nix -@@ -31025,4 +31025,6 @@ in - lc3tools = callPackage ../development/tools/lc3tools {}; - - zktree = callPackage ../applications/misc/zktree {}; -+ -+ flake-utils-plus-test = callPackage ../applications/misc/hello { }; - } diff --git a/tests/derivation-outputs-old/flake.nix b/tests/derivation-outputs-old/flake.nix deleted file mode 100644 index 57f16d32..00000000 --- a/tests/derivation-outputs-old/flake.nix +++ /dev/null @@ -1,91 +0,0 @@ -{ - inputs.utils.url = path:../../; - - outputs = inputs@{ self, nixpkgs, utils }: - let - mkApp = utils.lib.mkApp; - - packagePname = drvKey: self.packages.x86_64-linux.${drvKey}.pname; - - appHasSuffix = drvKey: suffix: nixpkgs.lib.hasSuffix suffix self.apps.x86_64-linux.${drvKey}.program; - - defaultAppHasSuffix = suffix: nixpkgs.lib.hasSuffix suffix self.defaultApp.x86_64-linux.program; - - pnameFromOutput = output: self.${output}.x86_64-linux.pname; - in - utils.lib.systemFlake { - inherit self inputs; - supportedSystems = [ "x86_64-linux" ]; - channels.nixpkgs.input = nixpkgs; - - - - ################# - ### Test Data ### - ################# - - defaultPackageBuilder = channels: channels.nixpkgs.coreutils; - - packagesBuilder = channels: { - inherit (channels.nixpkgs) coreutils; - }; - - # Should Get merged with `packagesBuilder` - packages.x86_64-linux.coreutils2 = self.pkgs.x86_64-linux.nixpkgs.coreutils; - - - - appsBuilder = channels: { - coreutils = mkApp { - drv = channels.nixpkgs.coreutils; - exePath = "/bin/nice"; - }; - }; - - - defaultAppBuilder = channels: mkApp { - drv = channels.nixpkgs.coreutils; - exePath = "/bin/nice"; - }; - - devShellBuilder = channels: channels.nixpkgs.mkShell { - pname = "super-shell"; - }; - - - - - ###################### - ### Test execution ### - ###################### - - checksBuilder = channels: - let - isTrue = cond: - if cond - then channels.nixpkgs.runCommandNoCC "success" { } "echo success > $out" - else channels.nixpkgs.runCommandNoCC "failure" { } "exit 1"; - in - { - - # Packages - defaultPackageValid = isTrue (pnameFromOutput "defaultPackage" == "coreutils"); - - packagesValid = isTrue (packagePname "coreutils" == "coreutils"); - - packagesMerged = isTrue (packagePname "coreutils2" == "coreutils"); - - - # Apps - appsValid = isTrue (appHasSuffix "coreutils" "nice"); - - defaultAppValid = isTrue (defaultAppHasSuffix "nice"); - - # Devshell - devshellValid = isTrue (pnameFromOutput "devShell" == "super-shell"); - - }; - - }; -} - diff --git a/tests/hosts-config/flake.nix b/tests/hosts-config/flake.nix index 044c427f..9f50eb5f 100644 --- a/tests/hosts-config/flake.nix +++ b/tests/hosts-config/flake.nix @@ -74,7 +74,7 @@ inherit (utils.lib.check-utils channels.nixpkgs) hasKey isEqual; plainHost = self.someConfigurations.Plain; - plainHostPkgs = plainHost.config.nixpkgs.pkgs; + plainHostPkgs = plainHost._module.args.pkgs; plainHostName = plainHost.config.networking.hostName; plainHostDomain = plainHost.config.networking.domain; @@ -83,7 +83,7 @@ reverseDnsHostDomain = reverseDnsHost.config.networking.domain; customizedHost = self.darwinConfigurations.Customized; - customizedHostPkgs = customizedHost.config.nixpkgs.pkgs; + customizedHostPkgs = customizedHost._module.args.pkgs; in {