diff --git a/clanServices/hello-world/default.nix b/clanServices/hello-world/default.nix index f322030cd..0f945e331 100644 --- a/clanServices/hello-world/default.nix +++ b/clanServices/hello-world/default.nix @@ -3,13 +3,20 @@ # The test for this module in ./tests/vm/default.nix shows an example of how # the service is used. - -{ packages }: -{ ... }: +{ ... }@service: { _class = "clan.service"; - manifest.name = "clan-core/hello-word"; + manifest.name = "clan-core/hello-world"; manifest.description = "This is a test"; + manifest.dependencies = { + # + home-manager = { + recomendedUrl = "github:nix-community/home-manager/release-25.05"; + }; + }; + + # Declare dependencies that the user must provide via flake inputs + # Or via 'clan.serviceOverrides..dependencies.resolved # This service provides two roles: "morning" and "evening". Roles can be # defined in this file directly (e.g. the "morning" role) or split up into a diff --git a/clanServices/hello-world/flake-module.nix b/clanServices/hello-world/flake-module.nix index c6330b1a0..e41c5c196 100644 --- a/clanServices/hello-world/flake-module.nix +++ b/clanServices/hello-world/flake-module.nix @@ -5,9 +5,7 @@ ... }: let - module = lib.modules.importApply ./default.nix { - inherit (self) packages; - }; + module = ./default.nix; in { clan.modules = { diff --git a/flake.nix b/flake.nix index 6f30e5810..081fa074d 100644 --- a/flake.nix +++ b/flake.nix @@ -66,7 +66,39 @@ }; clan = { meta.name = "clan-core"; + + modules = { + myModule = { ... }: { + # + _module.args = { inherit inputs; }; + }; + + # # depends on home-manager 25.05 + # myEnzime = { ... }: { + # imports = [ inputs.enzime.yours ]; + # dependencies.home-manager = lib.mkForce "my-home-manager"; + # }; + # # depends on home-manager 24.05 + # myLassulus = { ... }: { + # imports = [ inputs.lassulus.his ]; + # dependencies.home-manager = lib.mkForce "my-home-manager"; + # }; + }; + + + serviceOverrides = { + "clan-core/hello-world" = { + dependencies = { + flake-parts = "flake-parts"; + }; + }; + }; + inventory = { + instances.hello-world = { + roles.morning.tags = [ "all" ]; + }; + machines = { "test-darwin-machine" = { machineClass = "darwin"; diff --git a/lib/modules/clan/interface.nix b/lib/modules/clan/interface.nix index a097f7344..f82da831d 100644 --- a/lib/modules/clan/interface.nix +++ b/lib/modules/clan/interface.nix @@ -228,6 +228,38 @@ in ''; }; + serviceOverrides = lib.mkOption { + type = types.attrsOf (types.submoduleWith { + modules = [ + { + options.dependencies = lib.mkOption { + type = types.attrsOf types.raw; + description = "Override a dependencies of this service"; + }; + } + ]; + }); + default = { }; + description = '' + Override/inject dependencies to a service. + + Example: + + ```nix + { + servicesOverrides = { + # Override need to be done by manifest name to avoid ambiguity + "clan-core/hello-world" = { + dependencies = { + home-manager = inputs.home-manager-v2; + }; + }; + }; + } + ``` + ''; + }; + inventory = lib.mkOption { type = types.submoduleWith { modules = [ diff --git a/lib/modules/clan/module.nix b/lib/modules/clan/module.nix index ef28ce9cf..0aea6f51c 100644 --- a/lib/modules/clan/module.nix +++ b/lib/modules/clan/module.nix @@ -248,7 +248,7 @@ in staticModules = clan-core.clan.modules; distributedServices = clanLib.inventory.mapInstances { - inherit (clanConfig) inventory exportsModule; + inherit (clanConfig) inventory exportsModule serviceOverrides; inherit flakeInputs directory; clanCoreModules = clan-core.clan.modules; prefix = [ "distributedServices" ]; diff --git a/lib/modules/inventory/distributed-service/all-services-wrapper.nix b/lib/modules/inventory/distributed-service/all-services-wrapper.nix index eda9940ab..eec68ade3 100644 --- a/lib/modules/inventory/distributed-service/all-services-wrapper.nix +++ b/lib/modules/inventory/distributed-service/all-services-wrapper.nix @@ -2,6 +2,7 @@ { # TODO: consume directly from clan.config directory, + serviceOverrides, }: { lib, @@ -31,10 +32,12 @@ in ( { name, ... }: { - _module.args._ctx = [ name ]; - _module.args.exports = config.exports; - _module.args.directory = directory; - + _module.args = { + _ctx = [ name ]; + exports = config.exports; + directory = directory; + inherit (specialArgs) clanLib _unsafe; + }; } ) ./service-module.nix @@ -43,6 +46,9 @@ in inherit (specialArgs) clanLib; prefix = _ctx; }) + (service: { + dependencies = lib.mapAttrs (n: v: { resolved = v; }) serviceOverrides.${service.config.manifest.name}.dependencies or { }; + }) ]; }; }; diff --git a/lib/modules/inventory/distributed-service/inventory-adapter.nix b/lib/modules/inventory/distributed-service/inventory-adapter.nix index e35cc4339..385efc106 100644 --- a/lib/modules/inventory/distributed-service/inventory-adapter.nix +++ b/lib/modules/inventory/distributed-service/inventory-adapter.nix @@ -26,8 +26,9 @@ in inventory, directory, clanCoreModules, - prefix ? [ ], exportsModule, + prefix ? [ ], + serviceOverrides ? { }, }: let # machineHasTag = machineName: tagName: lib.elem tagName inventory.machines.${machineName}.tags; @@ -127,9 +128,10 @@ in specialArgs = { inherit clanLib; _ctx = prefix; + _unsafe.flakeInputs = flakeInputs; }; modules = [ - (import ./all-services-wrapper.nix { inherit directory; }) + (import ./all-services-wrapper.nix { inherit directory serviceOverrides; }) ] ++ modules; }; diff --git a/lib/modules/inventory/distributed-service/service-module.nix b/lib/modules/inventory/distributed-service/service-module.nix index 35256a753..713521ab8 100644 --- a/lib/modules/inventory/distributed-service/service-module.nix +++ b/lib/modules/inventory/distributed-service/service-module.nix @@ -2,6 +2,7 @@ lib, config, _ctx, + _unsafe, directory, exports, ... @@ -106,6 +107,10 @@ let in { options = { + + debug = mkOption { + default = _unsafe.flakeInputs; + }; # Option to disable some behavior during docs rendering _docs_rendering = mkOption { default = false; @@ -113,6 +118,54 @@ in type = types.bool; }; + dependencies = mkOption { + type = types.attrsWith { + placeholder = "dependencyName"; + elemType = types.submoduleWith { + modules = [ + ({name,...}@dep: { + options.name = mkOption { + default = name; + type = types.str; + description = "The name of the dependency, usually the input name."; + }; + options.resolved = mkOption { + type = types.raw; + default = _unsafe.flakeInputs.${dep.config.name} or (throw '' + The dependency '${dep.config.name}' could not be found in the flake inputs. + + This module requires '${dep.config.name}' to be present + Fixes: + - Add '${dep.config.name}' to the flake inputs + - Inject a custom dependency via 'clan.serviceOverrides..dependencies.${dep.config.name} = ...' + ''); + description = '' + The resolved value of the dependency. + ''; + }; + }) + ]; + }; + }; + description = '' + Dependencies of this service. + + Can be declared via `clan.lib.mkDependency`. + + ```nix + { + home-manager = clan.lib.mkDependency { + name = "home-manager"; + }; + } + ``` + + This will map `inputs.home-manager` to `dependencies.home-manager`. + The dependency can then be safely accessed via `config.dependencies.home-manager` from the toplevel arguments of this module. + ''; + default = { }; + }; + instances = mkOption { visible = false; defaultText = "Throws: 'The service must define its instances' when not defined";