diff --git a/README.md b/README.md index b6fca04..89c5156 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ fit together instead of colliding. ## Quick start -Use `caisson-core.mkLib` to compose your library, then `mkFlake` to -produce the flake outputs. By convention, your primary configuration +Use `caisson-core.mkLib` to compose your library, then +`lib.caisson.flake-parts.mkConfiguration` to produce the flake outputs. By convention, your primary configuration lives in `configs/flake-parts/`. ```nix @@ -43,7 +43,7 @@ lives in `configs/flake-parts/`. # Compose a library: the machinery lands under lib.caisson-core, # and caisson's flake-parts integration overlay contributes - # lib.caisson (mkFlake and friends). + # lib.caisson (one namespace per integration target). lib = caisson.lib.caisson-core.mkLib { inherit inputs; @@ -52,8 +52,8 @@ lives in `configs/flake-parts/`. flake = { # The flake-parts modules this flake defines: closed over your # inputs, importable here, exportable to downstream consumers. - default = lib.caisson.mkFlakeModule ./modules/flake-parts/default; - # other = lib.caisson.mkFlakeModule inputs.other-flake.flakeModules.default; + default = lib.caisson.flake-parts.mkModule ./modules/flake-parts/default; + # other = lib.caisson.flake-parts.mkModule inputs.other-flake.flakeModules.default; }; }; @@ -71,10 +71,10 @@ lives in `configs/flake-parts/`. }; - in lib.caisson.mkFlake { + in lib.caisson.flake-parts.mkConfiguration { # Convention: your primary config lives in configs/flake-parts/ - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/my-flake; + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/my-flake; # Select which modules (yours or your dependencies') this flake composes. moduleImports = modules: { inherit (modules) default; }; diff --git a/docs/development/testing-architecture.md b/docs/development/testing-architecture.md index ad2272c..99a6d72 100644 --- a/docs/development/testing-architecture.md +++ b/docs/development/testing-architecture.md @@ -150,8 +150,8 @@ lib = inputs.caisson-core.lib.caisson-core.mkLib { }; }; -lib.caisson.mkFlake { - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/unit-tests; +lib.caisson.flake-parts.mkConfiguration { + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/unit-tests; }; ``` @@ -163,7 +163,7 @@ overlay files register from that path because a flake cannot reference files outside its own source tree. The tests are not only testing library functions in isolation; the composition -and `mkFlake` path is the same one a downstream consumer exercises, so they +and `lib.caisson.flake-parts.mkConfiguration` path is the same one a downstream consumer exercises, so they verify that the framework's composition machinery works end-to-end. ### nix-unit integration @@ -213,7 +213,7 @@ appear to a consumer. ### Purpose Integration test flakes verify that caisson works correctly when consumed as a -dependency: that `mkLib`, `mkFlake`, class-keyed module registration, and +dependency: that `mkLib`, `lib.caisson.flake-parts.mkConfiguration`, class-keyed module registration, and module composition behave as expected from a consumer's perspective. ### Structure @@ -222,7 +222,7 @@ Each integration test is a standalone flake under `tests/integration//` th 1. Takes `parent` (caisson's evaluated outputs, from the pool) as an input 2. Calls `parent.lib.caisson-core.mkLib { inherit inputs; ... }` to bootstrap -3. Uses `lib.caisson.mkFlake` to compose a flake +3. Uses `lib.caisson.flake-parts.mkConfiguration` to compose a flake 4. Defines a `checks..` derivation that succeeds if composition worked diff --git a/examples/literate-flake/flake.nix b/examples/literate-flake/flake.nix index ecfa2d4..3a8bb53 100644 --- a/examples/literate-flake/flake.nix +++ b/examples/literate-flake/flake.nix @@ -56,7 +56,7 @@ project's exported overlays and modules become available under `/`, and the usual selections pick from them per item. Registering caisson this way brings in its integrations - (`lib.caisson`, mkFlake included) and its exported modules. + (`lib.caisson`, the flake-parts one included) and its exported modules. - `modules` is a function from the composed `lib`, used to register this flake's own class-keyed modules. - `libOverlays` is a function from an input-closed `mkLibOverlay` @@ -73,7 +73,7 @@ modules = lib: { # Demonstrate class-keyed module registration. - # This class is not imported by mkFlake in this example. + # This class is not imported by the flake-parts mkConfiguration in this example. generic = { noop = lib.caisson-core.mkModule "generic" ({ ... }: { }); }; @@ -82,7 +82,7 @@ # takes the closure attrset ({ closure-inputs, closure-lib, # mkModule, ... }) as its first arg list; files that don't need it # take `{ ... }:`. - default = lib.caisson.mkFlakeModule ./modules/flake-parts/default; + default = lib.caisson.flake-parts.mkModule ./modules/flake-parts/default; }; }; @@ -96,7 +96,7 @@ /* Step 2: Create the flake outputs. - `mkFlake` wraps flake-parts' mkFlake, injecting the framework's core + `lib.caisson.flake-parts.mkConfiguration` wraps flake-parts' mkFlake, injecting the framework's core module and threading `lib` as a special arg so modules receive the fully composed library. @@ -108,8 +108,8 @@ ("caisson/default" is caisson's default module, providing configInfo and the export options). */ - lib.caisson.mkFlake { - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/literate-flake; + lib.caisson.flake-parts.mkConfiguration { + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/literate-flake; moduleImports = modules: [ modules."caisson/default" diff --git a/examples/literate-flake/modules/flake-parts/default/default.nix b/examples/literate-flake/modules/flake-parts/default/default.nix index 5777cdd..3f7ee1b 100644 --- a/examples/literate-flake/modules/flake-parts/default/default.nix +++ b/examples/literate-flake/modules/flake-parts/default/default.nix @@ -2,7 +2,7 @@ /* A flake module defines options, config, and per-system outputs. - Because mkFlake threads the composed `lib` as a special arg, modules + Because lib.caisson.flake-parts.mkConfiguration threads the composed `lib` as a special arg, modules receive the full library -- including overlays registered by this flake. Here we use `lib.literate-flake.greet` which was added by our library overlay. diff --git a/flake.nix b/flake.nix index bef7f73..9000d79 100644 --- a/flake.nix +++ b/flake.nix @@ -70,7 +70,7 @@ modules = composedLib: { flake = { - default = composedLib.caisson.mkFlakeModule ./modules/flake-parts/default; + default = composedLib.caisson.flake-parts.mkModule ./modules/flake-parts/default; # flake-parts' partitions module, registered so consumers # can select it from the registry instead of declaring a # flake-parts input of their own. @@ -79,8 +79,8 @@ # registry (nixpkgs-interface) and the package-set # machinery that reifies `caisson.nixpkgs.pkgSets` per # system (nixpkgs, which imports the interface). - nixpkgs = composedLib.caisson.mkFlakeModule ./modules/flake-parts/nixpkgs; - nixpkgs-interface = composedLib.caisson.mkFlakeModule ./modules/flake-parts/nixpkgs-interface; + nixpkgs = composedLib.caisson.flake-parts.mkModule ./modules/flake-parts/nixpkgs; + nixpkgs-interface = composedLib.caisson.flake-parts.mkModule ./modules/flake-parts/nixpkgs-interface; }; }; @@ -100,11 +100,11 @@ in let - flakeOutputs = lib.caisson.mkFlake { + flakeOutputs = lib.caisson.flake-parts.mkConfiguration { name = "caisson"; - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/caisson; + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/caisson; moduleImports = modules: [ modules.default diff --git a/lib-overlays/check-args.nix b/lib-overlays/check-args.nix new file mode 100644 index 0000000..756cabb --- /dev/null +++ b/lib-overlays/check-args.nix @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: MIT +# +# Every integration's entry point takes exactly the caisson-shaped +# arguments (configModule, moduleImports, specialArgs, pkgSets, +# ecosystemSrc, and a target's own few) and composes the evaluator's +# call from them. Nothing else is forwarded: an evaluator argument +# handed in directly would be silently overwritten, silently dropped, +# or surface as a conflict deep inside the evaluator. The +# `...WithEcosystemArgs` twin of each entry point is the way to the +# evaluator's full surface: it takes the same arguments plus +# `ecosystemArgs`, merged over the composed call verbatim, last. +# +# context: the entry point, for the message. +# accepted: the argument names it takes. +# hints: per-name pointers for the common mistakes (an evaluator +# name where a caisson name exists). +# open: the twin's name, or null when checking the twin itself. +{ + context, + accepted, + hints ? { }, + open ? null, +}: +args: +let + unknown = builtins.filter (name: !(builtins.elem name accepted)) (builtins.attrNames args); + name = builtins.head unknown; + message = + if hints ? ${name} then + "${context} does not accept `${name}`: ${hints.${name}}" + else if open != null then + "${context} does not accept `${name}`; it takes ${builtins.concatStringsSep ", " accepted}. The evaluator's own arguments are available through ${open}, in `ecosystemArgs`." + else + "${context} does not accept `${name}`; it takes ${builtins.concatStringsSep ", " accepted}. Evaluator arguments go in `ecosystemArgs`."; +in +if unknown == [ ] then args else throw message diff --git a/lib-overlays/colmena/default.nix b/lib-overlays/colmena/default.nix index 2baefd2..38a4b45 100644 --- a/lib-overlays/colmena/default.nix +++ b/lib-overlays/colmena/default.nix @@ -1,5 +1,23 @@ # SPDX-License-Identifier: MIT -{ closure-inputs, ... }: +# +# The colmena integration. A hive is a set of NixOS configurations plus +# deployment metadata, and colmena's binary reads it through a small +# versioned attrset (the hive schema). +# +# mkConfiguration evaluates the hive: a module of class `colmena` with +# `meta` (the metadata colmena's binary reads) and `nodes.`, +# projected onto the schema. The hive module receives +# `mkNixosConfiguration` as a module argument, closed over the hive's +# colmena source: lib.caisson.nixos's composition plus colmena's public +# node modules (deploymentOptions, keyChownModule, keyServiceModule, +# assertionModule), with nixos.mkConfiguration's signature, so its +# `ecosystemSrc` is nixpkgs as for any NixOS configuration. A node is +# an ordinary NixOS configuration that also declares `deployment`; a +# consumer that exports it as `nixosConfigurations.` reads it +# back from the hive, one evaluation for nixos-rebuild and colmena +# apply. Projects can contribute hive modules through the registry +# like any other class. +{ ... }: { imports = [ ]; @@ -7,81 +25,243 @@ overlay = final: prev: let - mkColmenaModule = final.caisson-core.mkModule "colmena"; + mkHiveModule = final.caisson-core.mkModule "colmena"; resolveEcosystemSrc = import ../resolve-ecosystem-src.nix { name = "colmena"; context = "caisson.colmena"; resolve = final.caisson-core.resolve; }; + resolveSrc = + explicit: + resolveEcosystemSrc { + inherit explicit; + manifest = final.caisson-core.manifest or { }; + }; assertColmenaEcosystemSrc = ecosystemSrc: - if ecosystemSrc ? lib && ecosystemSrc.lib ? makeHive then + if ecosystemSrc ? lib && ecosystemSrc.lib ? makeHive && ecosystemSrc ? nixosModules then ecosystemSrc else - throw "lib.caisson.colmena.mkColmenaHive requires `ecosystemSrc.lib.makeHive`."; + throw "lib.caisson.colmena requires `ecosystemSrc.lib.makeHive` and `ecosystemSrc.nixosModules` (a colmena flake)."; - mkCommonArgs = - args@{ - modules ? [ ], - moduleImports ? builtins.attrValues, - specialArgs ? { }, - ... - }: + # The hive schema this integration emits. Colmena's binary asserts + # the version; mkConfiguration asserts it against the ecosystem + # source's own makeHive, so a colmena revision that moves the + # schema fails loudly at evaluation rather than at deploy time. + schema = "v0.5"; + metaConfigKeys = [ + "name" + "description" + "machinesFile" + "allowApplyAll" + ]; + + # colmena's public node modules, as one NixOS module. + mkDeploymentModule = src: { + _file = "caisson-colmena:deployment"; + imports = [ + src.nixosModules.deploymentOptions + src.nixosModules.assertionModule + src.nixosModules.keyChownModule + src.nixosModules.keyServiceModule + ]; + }; + + nodeAccepted = [ + "ecosystemSrc" + "pkgSets" + "configModule" + "moduleImports" + "specialArgs" + "system" + ]; + nodeHints = { + modules = "pass the host's module as `configModule`; registered nixos-class modules are selected with `moduleImports`."; + pkgs = "pass the package set as `pkgSets.pkgs`."; + deployment = "set `deployment.*` in the host's configModule; the node declares those options."; + }; + checkNodeArgs = import ../check-args.nix { + context = "mkNixosConfiguration (the hive module argument)"; + accepted = nodeAccepted; + hints = nodeHints; + open = "mkNixosConfigurationWithEcosystemArgs"; + }; + checkOpenNodeArgs = import ../check-args.nix { + context = "mkNixosConfigurationWithEcosystemArgs (the hive module argument)"; + accepted = nodeAccepted ++ [ "ecosystemArgs" ]; + hints = nodeHints; + }; + + # The node constructors a hive module receives, closed over the + # hive's colmena source: nixos.mkConfiguration over the host's + # module plus the deployment module. Their `ecosystemSrc` is + # nixpkgs, as for any NixOS configuration. + mkNodeConstructors = + src: let - selectedModules = moduleImports (final.caisson-core.modules.colmena or { }); + nodeArgsOf = + args: + args + // { + configModule = { + _file = "caisson-colmena:node"; + imports = [ + args.configModule + (mkDeploymentModule src) + ]; + }; + }; in { - modules = selectedModules ++ modules; - # Framework defaults first; caller's specialArgs wins on conflict. - # This is intentional and normal in the Nix ecosystem. - specialArgs = { - inputs = closure-inputs; - } - // specialArgs; + mkNixosConfiguration = + rawArgs: final.caisson.nixos.mkConfiguration (nodeArgsOf (checkNodeArgs rawArgs)); + mkNixosConfigurationWithEcosystemArgs = + rawArgs: + final.caisson.nixos.mkConfigurationWithEcosystemArgs (nodeArgsOf (checkOpenNodeArgs rawArgs)); + }; + + # The hive's options. `meta` mirrors the keys colmena's binary + # reads (its metaOptions also declare per-node package sets and + # special arguments, which have no meaning here: every node is an + # evaluated configuration already). + hiveOptions = + { lib, ... }: + { + options = { + meta = { + name = lib.mkOption { + type = lib.types.str; + default = "hive"; + description = "The name of the hive."; + }; + description = lib.mkOption { + type = lib.types.str; + default = "A Colmena Hive"; + description = "A short description of the hive."; + }; + machinesFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + apply = value: if value == null then null else toString value; + description = "The machines file passed to nix-store as `builders` when realizing this hive."; + }; + allowApplyAll = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether `colmena apply` without a node filter is allowed."; + }; + }; + nodes = lib.mkOption { + type = lib.types.attrsOf lib.types.raw; + default = { }; + description = "The hive's nodes: NixOS configurations from the hive module's `mkNixosConfiguration` argument."; + }; + }; }; - mkColmenaHive = - args@{ + accepted = [ + "ecosystemSrc" + "configModule" + "moduleImports" + "specialArgs" + "pkgSets" + ]; + hints = { + modules = "pass the hive module as `configModule`; registered colmena-class modules are selected with `moduleImports`."; + meta = "hive metadata is the hive module's `meta`."; + nodes = "the nodes are the hive module's `nodes`."; + defaults = "there is no hive-wide module: every node is an evaluated NixOS configuration (the hive module's mkNixosConfiguration); select shared modules there."; + network = "hive metadata is the hive module's `meta`."; + }; + checkArgs = import ../check-args.nix { + context = "lib.caisson.colmena.mkConfiguration"; + inherit accepted hints; + open = "lib.caisson.colmena.mkConfigurationWithEcosystemArgs"; + }; + checkOpenArgs = import ../check-args.nix { + context = "lib.caisson.colmena.mkConfigurationWithEcosystemArgs"; + accepted = accepted ++ [ "ecosystemArgs" ]; + inherit hints; + }; + + checkNode = + name: node: + if builtins.isAttrs node && node ? config && node.config ? deployment then + node + else + throw '' + lib.caisson.colmena.mkConfiguration: nodes.${name} is not a colmena node. Evaluate the host with the hive module's `mkNixosConfiguration` argument. + ''; + + compose = + { ecosystemSrc ? null, + configModule, + moduleImports ? builtins.attrValues, + specialArgs ? { }, + pkgSets ? null, ... }: let - checkedEcosystemSrc = assertColmenaEcosystemSrc (resolveEcosystemSrc { - explicit = ecosystemSrc; - manifest = final.caisson-core.manifest or { }; - }); - common = mkCommonArgs args; - passthroughArgs = builtins.removeAttrs args [ - "ecosystemSrc" - "modules" - "moduleImports" - "specialArgs" - ]; - baseMeta = passthroughArgs.meta or { }; - baseDefaults = passthroughArgs.defaults or { }; + src = assertColmenaEcosystemSrc (resolveSrc ecosystemSrc); + selectedModules = moduleImports (final.caisson-core.modules.colmena or { }); + hive = + (final.evalModules { + class = "colmena"; + modules = [ hiveOptions ] ++ selectedModules ++ [ configModule ]; + specialArgs = + mkNodeConstructors src // (if pkgSets != null then { inherit pkgSets; } else { }) // specialArgs; + }).config; + nodes = builtins.mapAttrs checkNode hive.nodes; + upstreamSchema = (src.lib.makeHive { }).__schema; in - checkedEcosystemSrc.lib.makeHive ( - passthroughArgs - // { - meta = baseMeta // { - specialArgs = (baseMeta.specialArgs or { }) // common.specialArgs; - }; - defaults = - { ... }: - { - imports = common.modules ++ [ baseDefaults ]; + if upstreamSchema != schema then + throw "lib.caisson.colmena.mkConfiguration emits hive schema ${schema}, but this colmena expects ${upstreamSchema}." + else + rec { + __schema = schema; + inherit nodes; + toplevel = builtins.mapAttrs (_: node: node.config.system.build.toplevel) nodes; + deploymentConfig = builtins.mapAttrs (_: node: node.config.deployment) nodes; + deploymentConfigSelected = + names: final.filterAttrs (name: _: builtins.elem name names) deploymentConfig; + evalSelected = names: final.filterAttrs (name: _: builtins.elem name names) toplevel; + evalSelectedDrvPaths = names: builtins.mapAttrs (_: drv: drv.drvPath) (evalSelected names); + metaConfig = final.getAttrs metaConfigKeys hive.meta; + introspect = + f: + f { + lib = final; + pkgs = + if pkgSets != null && pkgSets ? pkgs then + pkgSets.pkgs + else + throw "lib.caisson.colmena: `colmena eval` needs a package set; pass `pkgSets.pkgs` to mkConfiguration."; + inherit nodes; }; - } - ); + }; + + mkConfiguration = rawArgs: compose (checkArgs rawArgs); + + # The same hive, then `ecosystemArgs` merged over it verbatim: the + # hive attrset is what colmena's binary reads, so any of its + # attributes can be set or replaced there. + mkConfigurationWithEcosystemArgs = + rawArgs: + let + args = checkOpenArgs rawArgs; + in + compose args // (args.ecosystemArgs or { }); in { caisson = (prev.caisson or { }) // { colmena = ((prev.caisson or { }).colmena or { }) // { + mkModule = mkHiveModule; inherit - mkColmenaHive - mkColmenaModule + mkConfiguration + mkConfigurationWithEcosystemArgs ; }; }; diff --git a/lib-overlays/flake-parts/default.nix b/lib-overlays/flake-parts/default.nix index 6111b55..7d4dac2 100644 --- a/lib-overlays/flake-parts/default.nix +++ b/lib-overlays/flake-parts/default.nix @@ -1,8 +1,8 @@ # SPDX-License-Identifier: MIT # # The flake-parts integration: projecting a composition into flake -# outputs. A peer of the other integrations, it carries mkFlake, the -# `flake` module class (mkFlakeModule), the option types (option +# outputs. A peer of the other integrations, it carries mkConfiguration, the +# `flake` module class (mkModule), the option types (option # types are this integration's medium), the export machinery (the # core flake-parts module reads the composition's manifest at # `caisson-core.manifest` and projects the `libOverlays` and @@ -30,7 +30,9 @@ && builtins.isList (v.imports or [ ]) && builtins.all isLibOverlay (v.imports or [ ]); - types = ((prev.caisson or { }).types or { }) // { + prevNs = (prev.caisson or { }).flake-parts or { }; + + types = (prevNs.types or { }) // { libOverlay = final.mkOptionType { name = "libOverlay"; @@ -60,7 +62,37 @@ }; - mkFlake = + # flake-parts' own mkFlake arguments this entry point composes are + # refused with a pointer to the caisson argument; the rest forward. + accepted = [ + "configModule" + "moduleImports" + "name" + "specialArgs" + "pkgSets" + ]; + hints = { + inputs = "the flake's inputs come from the composition's manifest; pass them to caisson-core.mkLib."; + self = "the flake's own outputs come from the composition's manifest; pass inputs (self included) to caisson-core.mkLib."; + modules = "pass the configuration's module as `configModule`; registered flake-class modules are selected with `moduleImports`."; + moduleLocation = "pass the flake's canonical name as `name`."; + }; + checkArgs = import ../check-args.nix { + context = "lib.caisson.flake-parts.mkConfiguration"; + inherit accepted hints; + open = "lib.caisson.flake-parts.mkConfigurationWithEcosystemArgs"; + }; + checkOpenArgs = import ../check-args.nix { + context = "lib.caisson.flake-parts.mkConfigurationWithEcosystemArgs"; + accepted = accepted ++ [ "ecosystemArgs" ]; + inherit hints; + }; + mkConfiguration = rawArgs: mkConfigurationChecked (checkArgs rawArgs); + # The same composition, then `ecosystemArgs` merged over the + # flake-parts mkFlake call verbatim: everything it takes (inputs, + # specialArgs, self, moduleLocation) can be set or replaced there. + mkConfigurationWithEcosystemArgs = rawArgs: mkConfigurationChecked (checkOpenArgs rawArgs); + mkConfigurationChecked = args@{ configModule, @@ -81,70 +113,72 @@ # exists. name ? null, - ... + # Package sets for the flake evaluation itself, handed to the + # flake-class modules as the `pkgSets` special argument. Per + # system package sets are the nixpkgs integration's business + # (caisson.nixpkgs.pkgSets); this is the flake-level slot the + # other integrations also carry. + pkgSets ? null, + + specialArgs ? { }, + + ecosystemArgs ? { }, }: ( - if builtins.hasAttr "inputs" args then - builtins.abort "inputs were passed to mkFlake. This is an easy mistake to make, but they should be passed to mkLib." - else - let - - manifest = - final.caisson-core.manifest or (throw '' - caisson.mkFlake projects a composition's manifest into flake - outputs, but this composed library carries no manifest at - `caisson-core.manifest`. Compose the library with - caisson-core.mkLib, which captures one. - ''); - - filteredArgs = builtins.removeAttrs args [ - "configModule" - "modules" - "moduleImports" - "name" - ]; - - finalArgs = - (if name != null then { moduleLocation = name; } else { }) - // filteredArgs - // { - inputs = manifest.inputs; - specialArgs = { - lib = final; - } - // filteredArgs.specialArgs or { }; - }; - - # Selection over the flake class of the registry, the - # same source every adapter selects from, so modules - # arriving by any channel (local registration, overlay - # contribution, consumed project) are selectable here. - importedModules = moduleImports (final.caisson-core.modules.flake or { }); - - finalModule = ( - { lib, ... }: - { - imports = [ - closure-inputs.flake-parts.flakeModules.flakeModules - closure-inputs.flake-parts.flakeModules.modules - ../../modules/flake-parts/core - ] - ++ importedModules - ++ [ configModule ] - ++ (if name != null then [ { caisson.configInfo.configName = lib.mkDefault name; } ] else [ ]); + let + + manifest = + final.caisson-core.manifest or (throw '' + caisson.flake-parts.mkConfiguration projects a composition's manifest into flake + outputs, but this composed library carries no manifest at + `caisson-core.manifest`. Compose the library with + caisson-core.mkLib, which captures one. + ''); + + finalArgs = + (if name != null then { moduleLocation = name; } else { }) + // { + inputs = manifest.inputs; + specialArgs = { + lib = final; } - ); - - in - closure-inputs.flake-parts.lib.mkFlake finalArgs finalModule + // (if pkgSets != null then { inherit pkgSets; } else { }) + // specialArgs; + } + // ecosystemArgs; + + # Selection over the flake class of the registry, the + # same source every adapter selects from, so modules + # arriving by any channel (local registration, overlay + # contribution, consumed project) are selectable here. + importedModules = moduleImports (final.caisson-core.modules.flake or { }); + + finalModule = ( + { lib, ... }: + { + imports = [ + closure-inputs.flake-parts.flakeModules.flakeModules + closure-inputs.flake-parts.flakeModules.modules + ../../modules/flake-parts/core + ] + ++ importedModules + ++ [ configModule ] + ++ (if name != null then [ { caisson.configInfo.configName = lib.mkDefault name; } ] else [ ]); + } + ); + + in + closure-inputs.flake-parts.lib.mkFlake finalArgs finalModule ); in { caisson = (prev.caisson or { }) // { - inherit mkFlake types; - mkFlakeModule = final.caisson-core.mkModule "flake"; + flake-parts = prevNs // { + inherit mkConfiguration mkConfigurationWithEcosystemArgs types; + mkModule = final.caisson-core.mkModule "flake"; + }; }; flake-parts = (prev.flake-parts or { }) // closure-inputs.flake-parts.lib; diff --git a/lib-overlays/home-manager/default.nix b/lib-overlays/home-manager/default.nix index 16035dd..90f44a8 100644 --- a/lib-overlays/home-manager/default.nix +++ b/lib-overlays/home-manager/default.nix @@ -7,7 +7,7 @@ overlay = final: prev: let - mkHomeManagerModule = final.caisson-core.mkModule "homeManager"; + mkModule = final.caisson-core.mkModule "homeManager"; mkNixosModule = final.caisson-core.mkModule "nixos"; resolveEcosystemSrc = import ../resolve-ecosystem-src.nix { @@ -27,7 +27,7 @@ if pkgSets ? pkgs then pkgSets else - throw "lib.caisson.home-manager.mkHomeConfiguration requires `pkgSets.pkgs` to be defined."; + throw "lib.caisson.home-manager.mkConfiguration requires `pkgSets.pkgs` to be defined."; resolveOutPath = value: @@ -80,7 +80,7 @@ mkSourceMetaModule = sourceMeta: - mkHomeManagerModule ( + mkModule ( { ... }: { config, @@ -152,7 +152,7 @@ pkgSets, configModule, moduleImports ? builtins.attrValues, - extraSpecialArgs ? { }, + specialArgs ? { }, osConfig ? null, check ? true, minimal ? false, @@ -187,34 +187,82 @@ ]; }; pkgs = checkedPkgSets.pkgs; - # Framework defaults first; caller's extraSpecialArgs wins on conflict. - # This is intentional and normal in the Nix ecosystem. + # Framework defaults first; caller's specialArgs wins on conflict. + # This is intentional and normal in the Nix ecosystem. home-manager + # calls these extraSpecialArgs; the caisson surface uses one name. extraSpecialArgs = { pkgSets = checkedPkgSets; inherit osConfig; sourceMeta = resolvedSourceMeta; } - // extraSpecialArgs; + // specialArgs; evaluatorPath = "${hmSource}/modules"; }; - mkHomeConfiguration = - args: + # home-manager's evaluator takes exactly the arguments mkCommonArgs + # composes, so there is nothing to forward: the evaluator's own + # names are refused with a pointer to the caisson argument, and any + # other name is refused as unknown. + hints = { + configuration = "pass the configuration's module as `configModule`; registered class modules are selected with `moduleImports`."; + modules = "pass the configuration's module as `configModule`; registered class modules are selected with `moduleImports`."; + pkgs = "pass the package set as `pkgSets.pkgs`."; + extraSpecialArgs = "pass extra module arguments as `specialArgs`."; + }; + configurationArgs = [ + "ecosystemSrc" + "pkgSets" + "configModule" + "moduleImports" + "specialArgs" + "osConfig" + "check" + "minimal" + "sourceMeta" + ]; + checkArgs = import ../check-args.nix { + context = "lib.caisson.home-manager.mkConfiguration"; + accepted = configurationArgs; + inherit hints; + open = "lib.caisson.home-manager.mkConfigurationWithEcosystemArgs"; + }; + checkOpenArgs = import ../check-args.nix { + context = "lib.caisson.home-manager.mkConfigurationWithEcosystemArgs"; + accepted = configurationArgs ++ [ "ecosystemArgs" ]; + inherit hints; + }; + ecosystemArgsOf = common: { + inherit (common) + check + configuration + extraSpecialArgs + minimal + pkgs + ; + }; + + mkConfiguration = + rawArgs: + let + common = mkCommonArgs (checkArgs rawArgs); + evaluator = import common.evaluatorPath; + in + evaluator (ecosystemArgsOf common); + + # The same composition, then `ecosystemArgs` merged over the + # evaluator call verbatim: everything home-manager's evaluator + # takes (configuration, pkgs, lib, minimal, check, + # extraSpecialArgs) can be set or replaced there. + mkConfigurationWithEcosystemArgs = + rawArgs: let + args = checkOpenArgs rawArgs; common = mkCommonArgs args; evaluator = import common.evaluatorPath; in - evaluator { - inherit (common) - check - configuration - extraSpecialArgs - minimal - pkgs - ; - }; + evaluator (ecosystemArgsOf common // (args.ecosystemArgs or { })); - mkHomeConfigurationMinimal = args: mkHomeConfiguration (args // { minimal = true; }); + mkConfigurationMinimal = args: mkConfiguration (args // { minimal = true; }); mkStandaloneAdapter = args@{ @@ -226,10 +274,19 @@ in { homeModules = selectedModules; - buildHome = configModule: mkHomeConfiguration (args // { inherit configModule moduleImports; }); + buildHome = configModule: mkConfiguration (args // { inherit configModule moduleImports; }); }; + # The adapter keeps `...` (its extras are NixOS-module options, + # not evaluator arguments); only the old special-arguments name is + # refused. mkNixosAdapter = + rawArgs: + if rawArgs ? extraSpecialArgs then + throw "lib.caisson.home-manager.mkNixosAdapter does not accept `extraSpecialArgs`: pass extra module arguments as `specialArgs`." + else + mkNixosAdapterChecked rawArgs; + mkNixosAdapterChecked = args@{ users, ecosystemSrc ? null, @@ -259,7 +316,7 @@ # only mounted at login anyway. Currently limited to exactly one # hosted user (one shared unit cannot carry per-user ExecStarts). activationMode ? "upstream", - extraSpecialArgs ? { }, + specialArgs ? { }, ... }: assert final.assertMsg (builtins.elem activationMode [ @@ -317,7 +374,7 @@ # a `users.users.` entry (its injected defs dereference the # user record), and creating one would conflict with # systemd-homed's ownership of the account. Instead each user is - # evaluated with the same standalone evaluator (mkHomeConfiguration) + # evaluated with the same standalone evaluator (mkConfiguration) # that `home-manager switch` uses (the embedded generation is the # standalone one by construction), and a complete /etc user unit # runs its activation when the user's service manager starts. @@ -326,8 +383,8 @@ username = builtins.head (builtins.attrNames users); userActivations = builtins.mapAttrs ( _username: userArgs: - (mkHomeConfiguration { - inherit ecosystemSrc extraSpecialArgs; + (mkConfiguration { + inherit ecosystemSrc specialArgs; pkgSets = checkedPkgSets; configModule = if userArgs ? configModule then @@ -392,13 +449,13 @@ useGlobalPkgs useUserPackages ; - # Framework defaults first; caller's extraSpecialArgs wins on conflict. + # Framework defaults first; caller's specialArgs wins on conflict. # This is intentional and normal in the Nix ecosystem. extraSpecialArgs = { pkgSets = checkedPkgSets; sourceMeta = resolvedSourceMeta; } - // extraSpecialArgs; + // specialArgs; # The extra entries mirror mkCommonArgs/standalone defaults so a # hosted user generation evaluates to the same derivation as the # standalone profile built from the same source. @@ -466,9 +523,10 @@ home-manager = ((prev.caisson or { }).home-manager or { }) // { inherit assertSourceCoherence - mkHomeConfiguration - mkHomeConfigurationMinimal - mkHomeManagerModule + mkConfiguration + mkConfigurationMinimal + mkConfigurationWithEcosystemArgs + mkModule mkNixosAdapter mkSourceMeta mkStandaloneAdapter diff --git a/lib-overlays/nixos/compose.nix b/lib-overlays/nixos/compose.nix new file mode 100644 index 0000000..ea326fc --- /dev/null +++ b/lib-overlays/nixos/compose.nix @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: MIT +# +# The composition the nixos integration's entry points share: one +# definition of the module list and special arguments, so the variants +# cannot express different machines from the same arguments. +{ final }: +let + assertPkgSets = + context: pkgSets: + if pkgSets ? pkgs then pkgSets else throw "${context} requires `pkgSets.pkgs` to be defined."; + + # eval-config evaluations carry the nixpkgs module, so the package + # set lands on `nixpkgs.pkgs`; the minimal evaluator has no such + # module, so there it is the `pkgs` module argument instead. + mkFrameworkModule = pkgSets: { + _file = "caisson-nixos:framework"; + config = { + nixpkgs.pkgs = pkgSets.pkgs; + }; + }; + mkMinimalFrameworkModule = pkgSets: { + _file = "caisson-nixos:framework-minimal"; + config = { + _module.args.pkgs = final.mkDefault pkgSets.pkgs; + }; + }; +in +{ + context, + minimal ? false, +}: +{ + pkgSets, + configModule, + moduleImports ? builtins.attrValues, + specialArgs ? { }, + ... +}: +let + checkedPkgSets = assertPkgSets context pkgSets; + selectedModules = moduleImports (final.caisson-core.modules.nixos or { }); + frameworkModule = + if minimal then mkMinimalFrameworkModule checkedPkgSets else mkFrameworkModule checkedPkgSets; +in +{ + inherit checkedPkgSets; + modules = selectedModules ++ [ + configModule + frameworkModule + ]; + # Framework defaults first; the caller's win on conflict, as is + # normal in the Nix ecosystem. + specialArgs = { + pkgSets = checkedPkgSets; + } + // specialArgs; +} diff --git a/lib-overlays/nixos/default.nix b/lib-overlays/nixos/default.nix index 90a759c..1437f30 100644 --- a/lib-overlays/nixos/default.nix +++ b/lib-overlays/nixos/default.nix @@ -7,7 +7,7 @@ overlay = final: prev: let - mkNixosModule = final.caisson-core.mkModule "nixos"; + mkModule = final.caisson-core.mkModule "nixos"; resolveEcosystemSrc = import ../resolve-ecosystem-src.nix { name = "nixpkgs"; @@ -21,130 +21,131 @@ manifest = final.caisson-core.manifest or { }; }; - assertPkgSets = - pkgSets: - if pkgSets ? pkgs then - pkgSets - else - throw "lib.caisson.nixos.mkSystem requires `pkgSets.pkgs` to be defined."; + composeNixos = import ./compose.nix { inherit final; }; - mkFrameworkModule = pkgSets: { - _file = "caisson-nixos:framework"; - config = { - nixpkgs.pkgs = pkgSets.pkgs; - }; + commonAccepted = [ + "ecosystemSrc" + "pkgSets" + "configModule" + "moduleImports" + "specialArgs" + ]; + hints = { + modules = "pass the configuration's module as `configModule`; registered class modules are selected with `moduleImports`."; + pkgs = "pass the package set as `pkgSets.pkgs`."; + baseModules = "the base module list belongs to the variant: mkConfiguration and mkConfigurationFull evaluate with NixOS' module list, mkConfigurationMinimal without it."; }; + mkCheck = + name: extra: open: + import ../check-args.nix { + context = "lib.caisson.nixos.${name}"; + accepted = commonAccepted ++ extra; + inherit hints open; + }; - mkCommonArgs = - args@{ - pkgSets, - configModule, - moduleImports ? builtins.attrValues, - specialArgs ? { }, - ... + compose = + { + minimal ? false, }: + args: + composeNixos { + context = "lib.caisson.nixos.mkConfiguration"; + inherit minimal; + } args + // { + src = resolveSrc (args.ecosystemSrc or null); + }; + + # eval-config evaluations. `system` defaults to the package set's + # host platform. + evalConfigArgs = args: common: { + modules = common.modules; + specialArgs = common.specialArgs; + system = + args.system or (common.checkedPkgSets.pkgs.stdenv.hostPlatform.system + or (common.checkedPkgSets.pkgs.system or null) + ); + }; + evalConfig = common: import "${common.src}/nixos/lib/eval-config.nix"; + + mkConfiguration = + rawArgs: let - checkedPkgSets = assertPkgSets pkgSets; - resolvedSystem = - args.system - or (checkedPkgSets.pkgs.stdenv.hostPlatform.system or (checkedPkgSets.pkgs.system or null)); - selectedModules = moduleImports (final.caisson-core.modules.nixos or { }); - extraModules = selectedModules; - frameworkModule = mkFrameworkModule checkedPkgSets; + args = mkCheck "mkConfiguration" [ + "system" + ] "lib.caisson.nixos.mkConfigurationWithEcosystemArgs" rawArgs; + common = compose { } args; in - { - inherit checkedPkgSets; - system = resolvedSystem; - modules = extraModules ++ [ - configModule - frameworkModule - ]; - # Framework defaults first; caller's specialArgs wins on conflict. - # This is intentional and normal in the Nix ecosystem. - specialArgs = { - pkgSets = checkedPkgSets; - } - // specialArgs; - }; + evalConfig common (evalConfigArgs args common); - mkSystem = - args@{ - ecosystemSrc ? null, - ... - }: + # eval-config with nixpkgs' module list passed explicitly as + # `baseModules`. + mkConfigurationFull = + rawArgs: let - src = resolveSrc ecosystemSrc; - common = mkCommonArgs args; - passthroughArgs = builtins.removeAttrs args [ - "ecosystemSrc" - "pkgSets" - "configModule" - "moduleImports" - "specialArgs" - ]; - evalConfig = import "${src}/nixos/lib/eval-config.nix"; + args = mkCheck "mkConfigurationFull" [ + "system" + ] "lib.caisson.nixos.mkConfigurationWithEcosystemArgs" rawArgs; + common = compose { } args; in - evalConfig ( - passthroughArgs + evalConfig common ( + evalConfigArgs args common // { - modules = common.modules; - specialArgs = common.specialArgs; - system = common.system; + baseModules = import "${common.src}/nixos/modules/module-list.nix"; } ); - mkSystemFull = - args@{ - ecosystemSrc ? null, - ... - }: + # The same composition as mkConfiguration, then `ecosystemArgs` + # merged over the eval-config call verbatim: everything + # eval-config takes (system, pkgs, baseModules, specialArgs, + # modules, modulesLocation, prefix, lib, extraModules) can be set + # or replaced there. + mkConfigurationWithEcosystemArgs = + rawArgs: let - src = resolveSrc ecosystemSrc; - common = mkCommonArgs args; - passthroughArgs = builtins.removeAttrs args [ - "ecosystemSrc" - "pkgSets" - "configModule" - "moduleImports" - "specialArgs" - ]; - evalConfig = import "${src}/nixos/lib/eval-config.nix"; + args = mkCheck "mkConfigurationWithEcosystemArgs" [ "system" "ecosystemArgs" ] null rawArgs; + common = compose { } args; in - evalConfig ( - passthroughArgs - // { - baseModules = import "${src}/nixos/modules/module-list.nix"; - modules = common.modules; - specialArgs = common.specialArgs; - system = common.system; - } - ); + evalConfig common (evalConfigArgs args common // (args.ecosystemArgs or { })); - mkSystemMinimal = - args@{ - ecosystemSrc ? null, - prefix ? [ ], - ... - }: + # nixos/lib's evalModules: no NixOS base modules, so the config + # module declares any options it uses; the package set arrives as + # the `pkgs` module argument. + evalMinimalArgs = args: common: { + prefix = args.prefix or [ ]; + modules = common.modules; + specialArgs = common.specialArgs; + }; + evalMinimal = common: (import "${common.src}/nixos/lib" { }).evalModules; + + mkConfigurationMinimal = + rawArgs: let - src = resolveSrc ecosystemSrc; - common = mkCommonArgs args; - nixosLib = import "${src}/nixos/lib" { }; + args = mkCheck "mkConfigurationMinimal" [ + "prefix" + ] "lib.caisson.nixos.mkConfigurationMinimalWithEcosystemArgs" rawArgs; + common = compose { minimal = true; } args; in - nixosLib.evalModules { - inherit prefix; - modules = common.modules; - specialArgs = common.specialArgs; - }; + evalMinimal common (evalMinimalArgs args common); + + mkConfigurationMinimalWithEcosystemArgs = + rawArgs: + let + args = mkCheck "mkConfigurationMinimalWithEcosystemArgs" [ "prefix" "ecosystemArgs" ] null rawArgs; + common = compose { minimal = true; } args; + in + evalMinimal common (evalMinimalArgs args common // (args.ecosystemArgs or { })); in { caisson = (prev.caisson or { }) // { nixos = ((prev.caisson or { }).nixos or { }) // { inherit - mkNixosModule - mkSystem - mkSystemMinimal - mkSystemFull + mkModule + mkConfiguration + mkConfigurationFull + mkConfigurationMinimal + mkConfigurationWithEcosystemArgs + mkConfigurationMinimalWithEcosystemArgs ; }; }; diff --git a/lib-overlays/system-manager/default.nix b/lib-overlays/system-manager/default.nix index 888c87b..3b68afc 100644 --- a/lib-overlays/system-manager/default.nix +++ b/lib-overlays/system-manager/default.nix @@ -7,7 +7,7 @@ overlay = final: prev: let - mkSystemManagerModule = final.caisson-core.mkModule "systemManager"; + mkModule = final.caisson-core.mkModule "systemManager"; resolveEcosystemSrc = import ../resolve-ecosystem-src.nix { name = "system-manager"; @@ -20,45 +20,75 @@ if ecosystemSrc ? lib && ecosystemSrc.lib ? makeSystemConfig then ecosystemSrc else - throw "lib.caisson.system-manager.mkSystemConfig requires `ecosystemSrc.lib.makeSystemConfig`."; + throw "lib.caisson.system-manager.mkConfiguration requires `ecosystemSrc.lib.makeSystemConfig`."; mkCommonArgs = args@{ - modules ? [ ], + configModule, moduleImports ? builtins.attrValues, specialArgs ? { }, + pkgSets ? null, ... }: let selectedModules = moduleImports (final.caisson-core.modules.systemManager or { }); + # system-manager instantiates its own nixpkgs from + # `nixpkgs.hostPlatform`; a supplied package set seeds that + # platform (an explicit hostPlatform wins) and rides along as + # the `pkgSets` module argument. + pkgSetsModule = + if pkgSets != null && pkgSets ? pkgs then + [ + { + _file = "caisson-system-manager:pkgSets"; + nixpkgs.hostPlatform = final.mkDefault pkgSets.pkgs.stdenv.hostPlatform.system; + } + ] + else + [ ]; in { - modules = selectedModules ++ modules; + modules = selectedModules ++ pkgSetsModule ++ [ configModule ]; # Framework defaults first; caller's specialArgs wins on conflict. # This is intentional and normal in the Nix ecosystem. specialArgs = { inputs = closure-inputs; } + // (if pkgSets != null then { inherit pkgSets; } else { }) // specialArgs; }; - mkSystemConfig = - args@{ - ecosystemSrc ? null, - ... - }: + accepted = [ + "ecosystemSrc" + "configModule" + "moduleImports" + "specialArgs" + "pkgSets" + ]; + hints = { + modules = "pass the configuration's module as `configModule`; registered class modules are selected with `moduleImports`."; + extraSpecialArgs = "pass extra module arguments as `specialArgs`."; + }; + checkArgs = import ../check-args.nix { + context = "lib.caisson.system-manager.mkConfiguration"; + inherit accepted hints; + open = "lib.caisson.system-manager.mkConfigurationWithEcosystemArgs"; + }; + checkOpenArgs = import ../check-args.nix { + context = "lib.caisson.system-manager.mkConfigurationWithEcosystemArgs"; + accepted = accepted ++ [ "ecosystemArgs" ]; + inherit hints; + }; + # makeSystemConfig's arguments, composed from the caisson arguments, + # plus the compatibility bridge below. + compose = + args: let checkedEcosystemSrc = assertSystemManagerEcosystemSrc (resolveEcosystemSrc { - explicit = ecosystemSrc; + explicit = args.ecosystemSrc or null; manifest = final.caisson-core.manifest or { }; }); common = mkCommonArgs args; - passthroughArgs = builtins.removeAttrs args [ - "ecosystemSrc" - "modules" - "moduleImports" - "specialArgs" - ]; # system-manager imports selected NixOS modules from its own # nixpkgs input; current nixos-unstable restructured @@ -135,20 +165,42 @@ final.optional (!smDeclaresDisplayManager) displayManagerSinkModule ++ final.optional nixosNixOwnsDaemonOptions nixStubReplacementModule; in - checkedEcosystemSrc.lib.makeSystemConfig ( - passthroughArgs - // { + { + inherit checkedEcosystemSrc; + ecosystemArgs = { inherit (common) specialArgs; modules = common.modules ++ compatModules; - } + }; + }; + + mkConfiguration = + rawArgs: + let + composed = compose (checkArgs rawArgs); + in + composed.checkedEcosystemSrc.lib.makeSystemConfig composed.ecosystemArgs; + + # The same composition, then `ecosystemArgs` merged over the + # evaluator call verbatim: everything makeSystemConfig takes + # (modules, overlays, specialArgs, allowUnsupportedNixpkgs) can be + # set or replaced there. + mkConfigurationWithEcosystemArgs = + rawArgs: + let + args = checkOpenArgs rawArgs; + composed = compose args; + in + composed.checkedEcosystemSrc.lib.makeSystemConfig ( + composed.ecosystemArgs // (args.ecosystemArgs or { }) ); in { caisson = (prev.caisson or { }) // { system-manager = ((prev.caisson or { }).system-manager or { }) // { inherit - mkSystemConfig - mkSystemManagerModule + mkConfiguration + mkConfigurationWithEcosystemArgs + mkModule ; }; }; diff --git a/lib-overlays/terranix/default.nix b/lib-overlays/terranix/default.nix index 38011cf..15d260a 100644 --- a/lib-overlays/terranix/default.nix +++ b/lib-overlays/terranix/default.nix @@ -7,7 +7,7 @@ overlay = final: prev: let - mkTerranixModule = final.caisson-core.mkModule "terranix"; + mkModule = final.caisson-core.mkModule "terranix"; resolveEcosystemSrc = import ../resolve-ecosystem-src.nix { name = "terranix"; @@ -20,62 +20,100 @@ if ecosystemSrc ? lib && ecosystemSrc.lib ? terranixConfiguration then ecosystemSrc else - throw "lib.caisson.terranix.mkTerranixConfiguration requires `ecosystemSrc.lib.terranixConfiguration`."; + throw "lib.caisson.terranix.mkConfiguration requires `ecosystemSrc.lib.terranixConfiguration`."; - mkCommonArgs = - args@{ - modules ? [ ], + accepted = [ + "ecosystemSrc" + "configModule" + "moduleImports" + "specialArgs" + "pkgSets" + ]; + hints = { + modules = "pass the configuration's module as `configModule`; registered class modules are selected with `moduleImports`."; + extraArgs = "pass extra module arguments as `specialArgs`."; + pkgs = "pass the package set as `pkgSets.pkgs`."; + system = "pass the package set as `pkgSets.pkgs`; terranix evaluates against it."; + }; + checkArgs = import ../check-args.nix { + context = "lib.caisson.terranix.mkConfiguration"; + inherit accepted hints; + open = "lib.caisson.terranix.mkConfigurationWithEcosystemArgs"; + }; + checkOpenArgs = import ../check-args.nix { + context = "lib.caisson.terranix.mkConfigurationWithEcosystemArgs"; + accepted = accepted ++ [ "ecosystemArgs" ]; + inherit hints; + }; + + # terranixConfiguration's arguments, composed from the caisson + # arguments: pkgSets.pkgs is `pkgs` (terranix evaluates against + # a package set, so mkConfiguration requires one), the selected + # class modules and the config module are `modules`, and + # specialArgs becomes terranix's `extraArgs` (framework defaults + # first; the caller's win on conflict, as is normal in the Nix + # ecosystem). + compose = + { + ecosystemSrc ? null, + configModule, moduleImports ? builtins.attrValues, - extraArgs ? { }, + specialArgs ? { }, + pkgSets ? null, ... }: let + checkedEcosystemSrc = assertTerranixEcosystemSrc (resolveEcosystemSrc { + explicit = ecosystemSrc; + manifest = final.caisson-core.manifest or { }; + }); selectedModules = moduleImports (final.caisson-core.modules.terranix or { }); in { - modules = selectedModules ++ modules; - # Framework defaults first; caller's extraArgs wins on conflict. - # This is intentional and normal in the Nix ecosystem. - extraArgs = { - inputs = closure-inputs; - } - // extraArgs; + inherit checkedEcosystemSrc; + ecosystemArgs = (if pkgSets != null && pkgSets ? pkgs then { pkgs = pkgSets.pkgs; } else { }) // { + modules = selectedModules ++ [ configModule ]; + extraArgs = { + inputs = closure-inputs; + } + // (if pkgSets != null then { inherit pkgSets; } else { }) + // specialArgs; + }; }; - mkTerranixConfiguration = - args@{ - ecosystemSrc ? null, - ... - }: + mkConfiguration = + rawArgs: let - checkedEcosystemSrc = assertTerranixEcosystemSrc (resolveEcosystemSrc { - explicit = ecosystemSrc; - manifest = final.caisson-core.manifest or { }; - }); - common = mkCommonArgs args; - passthroughArgs = builtins.removeAttrs args [ - "ecosystemSrc" - "extraArgs" - "moduleImports" - "modules" - ]; + args = checkArgs rawArgs; + composed = compose args; + in + if !(args ? pkgSets && args.pkgSets ? pkgs) then + throw "lib.caisson.terranix.mkConfiguration requires `pkgSets.pkgs` to be defined." + else + composed.checkedEcosystemSrc.lib.terranixConfiguration composed.ecosystemArgs; + + # The same composition, then `ecosystemArgs` merged over the + # evaluator call verbatim: everything terranixConfiguration takes + # (system, pkgs, modules, extraArgs, strip_nulls) can be set or + # replaced there; pkgSets is optional here since `system` or + # `pkgs` may come that way. + mkConfigurationWithEcosystemArgs = + rawArgs: + let + args = checkOpenArgs rawArgs; + composed = compose args; in - checkedEcosystemSrc.lib.terranixConfiguration ( - passthroughArgs - // { - inherit (common) - extraArgs - modules - ; - } + composed.checkedEcosystemSrc.lib.terranixConfiguration ( + composed.ecosystemArgs // (args.ecosystemArgs or { }) ); in { caisson = (prev.caisson or { }) // { terranix = ((prev.caisson or { }).terranix or { }) // { inherit - mkTerranixConfiguration - mkTerranixModule + mkConfiguration + mkConfigurationWithEcosystemArgs + mkModule ; }; }; diff --git a/modules/flake-parts/core/caisson/libOverlays.nix b/modules/flake-parts/core/caisson/libOverlays.nix index 136df4e..4d2a295 100644 --- a/modules/flake-parts/core/caisson/libOverlays.nix +++ b/modules/flake-parts/core/caisson/libOverlays.nix @@ -5,7 +5,7 @@ export.enabled = lib.mkEnableOption "lib overlay export"; exported = lib.mkOption { - type = lib.types.functionTo (lib.types.attrsOf lib.caisson.types.libOverlay); + type = lib.types.functionTo (lib.types.attrsOf lib.caisson.flake-parts.types.libOverlay); description = '' Function that selects which registered library overlays to export as flake outputs. Receives the set of overlays registered via `mkLib` diff --git a/modules/flake-parts/core/caisson/manifest.nix b/modules/flake-parts/core/caisson/manifest.nix index fcfa1c5..9847917 100644 --- a/modules/flake-parts/core/caisson/manifest.nix +++ b/modules/flake-parts/core/caisson/manifest.nix @@ -2,7 +2,7 @@ { lib, ... }: { options.caisson.manifest = lib.mkOption { - type = lib.caisson.types.manifest; + type = lib.caisson.flake-parts.types.manifest; readOnly = true; default = lib.caisson-core.manifest; defaultText = "the composed library's caisson-core.manifest"; diff --git a/modules/flake-parts/core/default.nix b/modules/flake-parts/core/default.nix index 0b67332..5d16af3 100644 --- a/modules/flake-parts/core/default.nix +++ b/modules/flake-parts/core/default.nix @@ -1,6 +1,6 @@ # SPDX-License-Identifier: MIT # -# The core flake-parts module, wired into every mkFlake evaluation by +# The core flake-parts module, wired into every lib.caisson.flake-parts.mkConfiguration evaluation by # the flake-parts integration. Plain modules on purpose: everything # they need comes through `lib` (the composed library in specialArgs), # so they are imported directly rather than through mkModule. diff --git a/tests/integration/basic-composition/flake.nix b/tests/integration/basic-composition/flake.nix index b379a34..a46af80 100644 --- a/tests/integration/basic-composition/flake.nix +++ b/tests/integration/basic-composition/flake.nix @@ -35,8 +35,8 @@ }; in - lib.caisson.mkFlake { + lib.caisson.flake-parts.mkConfiguration { # Convention: config lives in configs/flake-parts/ - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/basic-composition; + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/basic-composition; }; } diff --git a/tests/integration/lib-consumer-chain/final-consumer/flake.nix b/tests/integration/lib-consumer-chain/final-consumer/flake.nix index 099ce38..ebde106 100644 --- a/tests/integration/lib-consumer-chain/final-consumer/flake.nix +++ b/tests/integration/lib-consumer-chain/final-consumer/flake.nix @@ -27,7 +27,7 @@ }; }; in - lib.caisson.mkFlake { - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/final-consumer; + lib.caisson.flake-parts.mkConfiguration { + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/final-consumer; }; } diff --git a/tests/integration/lib-consumer-chain/middle-flake/flake.nix b/tests/integration/lib-consumer-chain/middle-flake/flake.nix index 5dbd24b..80788c2 100644 --- a/tests/integration/lib-consumer-chain/middle-flake/flake.nix +++ b/tests/integration/lib-consumer-chain/middle-flake/flake.nix @@ -19,7 +19,7 @@ modules = lib: { flake = { - default = lib.caisson.mkFlakeModule ./modules/flake-parts/default; + default = lib.caisson.flake-parts.mkModule ./modules/flake-parts/default; }; }; @@ -29,7 +29,7 @@ }; }; in - lib.caisson.mkFlake { - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/middle-flake; + lib.caisson.flake-parts.mkConfiguration { + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/middle-flake; }; } diff --git a/tests/integration/minimal-consumer/flake.nix b/tests/integration/minimal-consumer/flake.nix index 007a187..026385f 100644 --- a/tests/integration/minimal-consumer/flake.nix +++ b/tests/integration/minimal-consumer/flake.nix @@ -33,8 +33,8 @@ }; }; in - lib.caisson.mkFlake { - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/minimal-consumer; + lib.caisson.flake-parts.mkConfiguration { + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/minimal-consumer; moduleImports = modules: [ modules.caisson-default ]; }; diff --git a/tests/integration/module-class-export/flake.nix b/tests/integration/module-class-export/flake.nix index 5c8e033..5498c21 100644 --- a/tests/integration/module-class-export/flake.nix +++ b/tests/integration/module-class-export/flake.nix @@ -41,7 +41,7 @@ }; }; in - lib.caisson.mkFlake { - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/module-class-export; + lib.caisson.flake-parts.mkConfiguration { + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/module-class-export; }; } diff --git a/tests/integration/nixpkgs-consumer/flake.nix b/tests/integration/nixpkgs-consumer/flake.nix index 040fc70..686822a 100644 --- a/tests/integration/nixpkgs-consumer/flake.nix +++ b/tests/integration/nixpkgs-consumer/flake.nix @@ -24,9 +24,9 @@ }; }; in - lib.caisson.mkFlake { + lib.caisson.flake-parts.mkConfiguration { # The default moduleImports selects every registered flake module, # so the nixpkgs machinery arrives through the projects channel. - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/nixpkgs-consumer; + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/nixpkgs-consumer; }; } diff --git a/tests/integration/nixpkgs-interface-consumer/flake.nix b/tests/integration/nixpkgs-interface-consumer/flake.nix index ceb16d6..e493ae2 100644 --- a/tests/integration/nixpkgs-interface-consumer/flake.nix +++ b/tests/integration/nixpkgs-interface-consumer/flake.nix @@ -24,8 +24,8 @@ }; }; in - lib.caisson.mkFlake { - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/nixpkgs-interface-consumer; + lib.caisson.flake-parts.mkConfiguration { + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/nixpkgs-interface-consumer; # Interface only: the registry option without the package-set # machinery. diff --git a/tests/integration/nixpkgs-no-pkg-sets/flake.nix b/tests/integration/nixpkgs-no-pkg-sets/flake.nix index d52ebbe..37be37c 100644 --- a/tests/integration/nixpkgs-no-pkg-sets/flake.nix +++ b/tests/integration/nixpkgs-no-pkg-sets/flake.nix @@ -24,9 +24,9 @@ }; }; in - lib.caisson.mkFlake { + lib.caisson.flake-parts.mkConfiguration { # The default moduleImports applies the nixpkgs machinery through # the projects channel; this flake configures none of it. - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/nixpkgs-no-pkg-sets; + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/nixpkgs-no-pkg-sets; }; } diff --git a/tests/integration/nixpkgs-overlay-export/flake.nix b/tests/integration/nixpkgs-overlay-export/flake.nix index a05ffee..a5bcbf0 100644 --- a/tests/integration/nixpkgs-overlay-export/flake.nix +++ b/tests/integration/nixpkgs-overlay-export/flake.nix @@ -24,7 +24,7 @@ }; }; in - lib.caisson.mkFlake { - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/nixpkgs-overlay-export; + lib.caisson.flake-parts.mkConfiguration { + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/nixpkgs-overlay-export; }; } diff --git a/tests/unit/flake.nix b/tests/unit/flake.nix index 3dfe1ee..3a3ae7a 100644 --- a/tests/unit/flake.nix +++ b/tests/unit/flake.nix @@ -55,7 +55,7 @@ }; }; in - lib.caisson.mkFlake { - configModule = lib.caisson.mkFlakeModule ./configs/flake-parts/unit-tests; + lib.caisson.flake-parts.mkConfiguration { + configModule = lib.caisson.flake-parts.mkModule ./configs/flake-parts/unit-tests; }; } diff --git a/tests/unit/lib-overlays.nix b/tests/unit/lib-overlays.nix index 0c42854..f90af3a 100644 --- a/tests/unit/lib-overlays.nix +++ b/tests/unit/lib-overlays.nix @@ -7,7 +7,7 @@ let # The passed lib is the composed library from the unit test flake, # carrying both framework namespaces: `caisson-core` (machinery, # registry, manifest) and `caisson` (the integrations). - mkFlakeModule = lib.caisson.mkFlakeModule; + mkFlakePartsModule = lib.caisson.flake-parts.mkModule; mkLibOverlay = lib.caisson-core.mkLibOverlay; mkModule = lib.caisson-core.mkModule; @@ -18,7 +18,7 @@ let # Test-facing mkLib: registers the flake-parts integration into # every test composition (so composed test libraries carry - # caisson.mkFlake), and otherwise defers to caisson-core.mkLib. + # caisson.flake-parts.mkConfiguration), and otherwise defers to caisson-core.mkLib. # Malformed arguments pass through untouched so the machinery's own # shape errors stay observable. testMkLib = @@ -242,7 +242,7 @@ in hasSelfModules = builtins.isAttrs closure-self-modules; hasMkMod = builtins.isFunction mkModule; }; - result = mkFlakeModule module; + result = mkFlakePartsModule module; evaluated = result { config = { }; }; in evaluated.hasInputs && evaluated.hasLib && evaluated.hasSelfModules && evaluated.hasMkMod; @@ -258,14 +258,14 @@ in { ok = true; }; - evaluated = (mkFlakeModule module) { config = { }; }; + evaluated = (mkFlakePartsModule module) { config = { }; }; in evaluated.ok; expected = true; }; "test: throws on a plain attrset module" = { - expr = builtins.tryEval (mkFlakeModule { + expr = builtins.tryEval (mkFlakePartsModule { options = { }; }); expected = { @@ -280,7 +280,7 @@ in modulePath = builtins.toFile "mk-module-path-module.nix" '' { ... }: { config, ... }: { ok = true; } ''; - result = mkFlakeModule modulePath; + result = mkFlakePartsModule modulePath; in { file = builtins.toString result._file; @@ -306,8 +306,8 @@ in modulePath = builtins.toFile "mk-module-dedup-module.nix" '' { ... }: { config, ... }: { ok = true; } ''; - a = mkFlakeModule modulePath; - b = mkFlakeModule modulePath; + a = mkFlakePartsModule modulePath; + b = mkFlakePartsModule modulePath; in a.key == b.key; expected = true; @@ -326,7 +326,7 @@ in _file = "test-wrapper"; imports = [ innerModule ]; }; - result = mkFlakeModule wrapped; + result = mkFlakePartsModule wrapped; in builtins.isAttrs result && builtins.hasAttr "_file" result && builtins.hasAttr "imports" result; expected = true; @@ -414,7 +414,7 @@ in }; }; - "test: mkFlakeModule is equivalent to mkModule \"flake\"" = { + "test: flake-parts.mkModule is equivalent to mkModule \"flake\"" = { expr = let module = @@ -439,7 +439,7 @@ in { config = { }; }; }; - viaAlias = (caisson.mkFlakeModule module) { config = { }; }; + viaAlias = (caisson.flake-parts.mkModule module) { config = { }; }; viaFactory = ((caisson.mkModule "flake") module) { config = { }; }; in { @@ -918,13 +918,13 @@ in inputs = mockInputs; modules = callbackLib: { flake = { - inspect = callbackLib.caisson.mkFlakeModule ( + inspect = callbackLib.caisson.flake-parts.mkModule ( { ... }: { flake.inspect = { hasNamespace = callbackLib ? caisson; hasMkModule = builtins.isFunction callbackLib.caisson-core.mkModule; - hasMkFlakeModule = builtins.isFunction callbackLib.caisson.mkFlakeModule; + hasMkFlakeModule = builtins.isFunction callbackLib.caisson.flake-parts.mkModule; hasMkLibOverlay = builtins.isFunction callbackLib.caisson-core.mkLibOverlay; hasImportApply = builtins.isFunction callbackLib.caisson-core.importApply; }; @@ -933,8 +933,8 @@ in }; }; }; - outputs = myLib.caisson.mkFlake { - configModule = myLib.caisson.mkFlakeModule ( + outputs = myLib.caisson.flake-parts.mkConfiguration { + configModule = myLib.caisson.flake-parts.mkModule ( { ... }: { systems = [ "x86_64-linux" ]; @@ -970,7 +970,7 @@ in }; modules = callbackLib: { flake = { - inspect = callbackLib.caisson.mkFlakeModule ( + inspect = callbackLib.caisson.flake-parts.mkModule ( { ... }: { flake.callbackSawOverlay = callbackLib.fromCallbackOverlay or "missing"; @@ -979,8 +979,8 @@ in }; }; }; - outputs = myLib.caisson.mkFlake { - configModule = myLib.caisson.mkFlakeModule ( + outputs = myLib.caisson.flake-parts.mkConfiguration { + configModule = myLib.caisson.flake-parts.mkModule ( { ... }: { systems = [ "x86_64-linux" ]; @@ -1010,7 +1010,7 @@ in }; modules = callbackLib: { flake = { - inspect = callbackLib.caisson.mkFlakeModule ( + inspect = callbackLib.caisson.flake-parts.mkModule ( { ... }: { lib, ... }: { @@ -1023,8 +1023,8 @@ in }; }; }; - outputs = myLib.caisson.mkFlake { - configModule = myLib.caisson.mkFlakeModule ( + outputs = myLib.caisson.flake-parts.mkConfiguration { + configModule = myLib.caisson.flake-parts.mkModule ( { ... }: { systems = [ "x86_64-linux" ]; @@ -1040,7 +1040,7 @@ in }; }; - "test: modules.flake attrset receives working modules in mkFlake" = { + "test: modules.flake attrset receives working modules in flake-parts.mkConfiguration" = { expr = let myLib = mkTestLib { @@ -1065,14 +1065,14 @@ in expected = true; }; - "test: modules registered via lib aliases work in mkFlake" = { + "test: modules registered via lib aliases work in flake-parts.mkConfiguration" = { expr = let myLib = caisson.mkLib { inputs = mockInputs; modules = callbackLib: { flake = { - fromAlias = callbackLib.caisson.mkFlakeModule ( + fromAlias = callbackLib.caisson.flake-parts.mkModule ( { ... }: { flake.fromAlias = true; @@ -1081,8 +1081,8 @@ in }; }; }; - outputs = myLib.caisson.mkFlake { - configModule = myLib.caisson.mkFlakeModule ( + outputs = myLib.caisson.flake-parts.mkConfiguration { + configModule = myLib.caisson.flake-parts.mkModule ( { ... }: { systems = [ "x86_64-linux" ]; @@ -1154,7 +1154,7 @@ in }; modules = runtimeLib: { flake = { - inspect = runtimeLib.caisson.mkFlakeModule ( + inspect = runtimeLib.caisson.flake-parts.mkModule ( { ... }: { lib, ... }: { @@ -1167,8 +1167,8 @@ in }; }; }; - outputs = myLib.caisson.mkFlake { - configModule = myLib.caisson.mkFlakeModule ( + outputs = myLib.caisson.flake-parts.mkConfiguration { + configModule = myLib.caisson.flake-parts.mkModule ( { ... }: { systems = [ "x86_64-linux" ]; @@ -1261,26 +1261,43 @@ in }; }; - mkFlake = { - "test: rejects inputs arg via hasAttr guard" = { - # mkFlake uses builtins.abort (not throw) when inputs is present, - # so tryEval cannot catch it. Instead we test the guard condition - # directly: mkFlake checks builtins.hasAttr "inputs" args. - expr = builtins.hasAttr "inputs" { - inputs = { }; - configModule = { }; - }; - expected = true; + flake-parts-mkConfiguration = { + "test: refuses inputs (they belong to mkLib)" = { + expr = + (builtins.tryEval ( + lib.caisson.flake-parts.mkConfiguration { + inputs = { }; + configModule = { }; + } + )).success; + expected = false; + }; + + "test: refuses modules (configModule and moduleImports carry them)" = { + expr = + (builtins.tryEval ( + lib.caisson.flake-parts.mkConfiguration { + modules = [ ]; + configModule = { }; + } + )).success; + expected = false; }; - "test: does not reject args without inputs" = { - expr = builtins.hasAttr "inputs" { configModule = { }; }; + "test: refuses evaluator arguments outside the ecosystem-args twin" = { + expr = + (builtins.tryEval ( + lib.caisson.flake-parts.mkConfiguration { + configModule = { }; + ecosystemArgs = { }; + } + )).success; expected = false; }; "test: filteredArgs strips reserved keys" = { - # mkFlake removes configModule, modules, and moduleImports before - # forwarding to flake-parts. Verify the stripping logic in isolation. + # The composed mkFlake call is built from named arguments; this + # checks the removeAttrs idiom in isolation. expr = let args = { @@ -1313,7 +1330,7 @@ in }; "test: specialArgs merges with user-provided specialArgs" = { - # mkFlake merges { lib = final; } with any specialArgs the caller provides. + # flake-parts.mkConfiguration merges { lib = final; } with any specialArgs the caller provides. expr = let filteredArgs = { @@ -1410,8 +1427,8 @@ in }; }; - outputs = myLib.caisson.mkFlake { - configModule = myLib.caisson.mkFlakeModule ( + outputs = myLib.caisson.flake-parts.mkConfiguration { + configModule = myLib.caisson.flake-parts.mkModule ( { ... }: { systems = [ "x86_64-linux" ]; @@ -1443,8 +1460,8 @@ in }; }; - outputs = myLib.caisson.mkFlake { - configModule = myLib.caisson.mkFlakeModule ( + outputs = myLib.caisson.flake-parts.mkConfiguration { + configModule = myLib.caisson.flake-parts.mkModule ( { ... }: { systems = [ "x86_64-linux" ]; @@ -1457,7 +1474,7 @@ in expected = true; }; - "test: mkFlake works when modules.flake is absent" = { + "test: flake-parts.mkConfiguration works when modules.flake is absent" = { expr = let myLib = caisson.mkLib { @@ -1468,8 +1485,8 @@ in }; }; }; - outputs = myLib.caisson.mkFlake { - configModule = myLib.caisson.mkFlakeModule ( + outputs = myLib.caisson.flake-parts.mkConfiguration { + configModule = myLib.caisson.flake-parts.mkModule ( { ... }: { systems = [ "x86_64-linux" ]; @@ -1494,7 +1511,7 @@ in "test: mkModule with null throws" = { # null is not a function taking the closure attrset; plain values must # be imported/registered directly, so mkModule rejects them loudly. - expr = builtins.tryEval (mkFlakeModule null); + expr = builtins.tryEval (mkFlakePartsModule null); expected = { success = false; value = false; @@ -1504,12 +1521,14 @@ in types = { "test: libOverlay type accepts a built overlay" = { - expr = caisson.types.libOverlay.check (mkLibOverlay ({ ... }: { overlay = final: prev: { }; })); + expr = caisson.flake-parts.types.libOverlay.check ( + mkLibOverlay ({ ... }: { overlay = final: prev: { }; }) + ); expected = true; }; "test: libOverlay type accepts nested imports" = { - expr = caisson.types.libOverlay.check { + expr = caisson.flake-parts.types.libOverlay.check { imports = [ { imports = [ ]; @@ -1522,12 +1541,12 @@ in }; "test: libOverlay type rejects a bare overlay function" = { - expr = caisson.types.libOverlay.check (final: prev: { }); + expr = caisson.flake-parts.types.libOverlay.check (final: prev: { }); expected = false; }; "test: libOverlay type rejects a malformed import" = { - expr = caisson.types.libOverlay.check { + expr = caisson.flake-parts.types.libOverlay.check { imports = [ (final: prev: { }) ]; overlay = final: prev: { }; }; @@ -1558,15 +1577,16 @@ in ecosystemResolution = let - # A minimal colmena "ecosystem": the adapter only needs - # lib.makeHive, so a stub shows which channel resolution chose. - colmenaStub = probe: { - lib.makeHive = hiveArgs: { + # A minimal terranix "ecosystem": the adapter only needs + # lib.terranixConfiguration, so a stub shows which channel + # resolution chose. + terranixStub = probe: { + lib.terranixConfiguration = evaluatorArgs: { stubbed = probe; - inherit hiveArgs; + inherit evaluatorArgs; }; }; - # Test compositions register caisson's real colmena integration + # Test compositions register caisson's real terranix integration # (built from its source file, like the flake-parts # registration) alongside the harness's flake-parts # registration. @@ -1576,7 +1596,7 @@ in { inputs = mockInputs; libOverlays = _mkLibOverlay: { - colmena = mkLibOverlay (inputs.parent.outPath + "/lib-overlays/colmena"); + terranix = mkLibOverlay (inputs.parent.outPath + "/lib-overlays/terranix"); }; } // extra @@ -1586,18 +1606,25 @@ in "test: a declared ecosystem resolves for an adapter" = { expr = let - myLib = mkResolutionLib { ecosystems.colmena = colmenaStub "declared"; }; + myLib = mkResolutionLib { ecosystems.terranix = terranixStub "declared"; }; in - (myLib.caisson.colmena.mkColmenaHive { }).stubbed; + (myLib.caisson.terranix.mkConfiguration { + configModule = { }; + pkgSets.pkgs = { }; + }).stubbed; expected = "declared"; }; "test: an explicit ecosystemSrc beats the declaration" = { expr = let - myLib = mkResolutionLib { ecosystems.colmena = colmenaStub "declared"; }; + myLib = mkResolutionLib { ecosystems.terranix = terranixStub "declared"; }; in - (myLib.caisson.colmena.mkColmenaHive { ecosystemSrc = colmenaStub "explicit"; }).stubbed; + (myLib.caisson.terranix.mkConfiguration { + ecosystemSrc = terranixStub "explicit"; + configModule = { }; + pkgSets.pkgs = { }; + }).stubbed; expected = "explicit"; }; @@ -1606,11 +1633,14 @@ in let myLib = mkResolutionLib { inputs = mockInputs // { - colmena = colmenaStub "input"; + terranix = terranixStub "input"; }; }; in - (myLib.caisson.colmena.mkColmenaHive { }).stubbed; + (myLib.caisson.terranix.mkConfiguration { + configModule = { }; + pkgSets.pkgs = { }; + }).stubbed; expected = "input"; }; @@ -1619,17 +1649,25 @@ in let myLib = mkResolutionLib { inputs = mockInputs // { - colmena = colmenaStub "input"; + terranix = terranixStub "input"; }; - ecosystems.colmena = colmenaStub "declared"; + ecosystems.terranix = terranixStub "declared"; }; in - (myLib.caisson.colmena.mkColmenaHive { }).stubbed; + (myLib.caisson.terranix.mkConfiguration { + configModule = { }; + pkgSets.pkgs = { }; + }).stubbed; expected = "declared"; }; "test: a full miss throws at the adapter" = { - expr = builtins.tryEval ((mkResolutionLib { }).caisson.colmena.mkColmenaHive { }).stubbed; + expr = + builtins.tryEval + ((mkResolutionLib { }).caisson.terranix.mkConfiguration { + configModule = { }; + pkgSets.pkgs = { }; + }).stubbed; expected = { success = false; value = false; @@ -1639,9 +1677,9 @@ in "test: declarations join the manifest" = { expr = let - myLib = mkResolutionLib { ecosystems.colmena = colmenaStub "declared"; }; + myLib = mkResolutionLib { ecosystems.terranix = terranixStub "declared"; }; in - (myLib.caisson-core.manifest.ecosystems.colmena.lib.makeHive { }).stubbed; + (myLib.caisson-core.manifest.ecosystems.terranix.lib.terranixConfiguration { }).stubbed; expected = "declared"; }; };