From 3ebe1846b6bec79e6599457e7db225fc549ce351 Mon Sep 17 00:00:00 2001 From: IceDBorn Date: Sun, 8 Feb 2026 08:49:13 +0200 Subject: [PATCH 1/6] feat: load icedos modules from extra-modules --- lib/icedos.nix | 113 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 26 deletions(-) diff --git a/lib/icedos.nix b/lib/icedos.nix index 4aafd1f7..01db67b9 100644 --- a/lib/icedos.nix +++ b/lib/icedos.nix @@ -18,6 +18,7 @@ let inherit (lib) flatten hasAttrByPath; inherit (icedosLib) + ICEDOS_CONFIG_ROOT ICEDOS_STAGE INPUTS_PREFIX filterByAttrs @@ -89,29 +90,36 @@ let getExternalModuleOutputs = modules: let - inherit (builtins) attrNames; + inherit (builtins) attrNames filter; inherit (lib) flatten hasAttr listToAttrs; - modulesAsInputs = map ( - { _repoInfo, ... }: - let - inherit (_repoInfo) url; + modulesAsInputs = + map + ( + { _repoInfo, ... }: + let + inherit (_repoInfo) url; - flakeRev = - if (hasAttr "rev" _repoInfo) then - "/${_repoInfo.rev}" - else if (hasAttr "narHash" _repoInfo) then - "?narHash=${_repoInfo.narHash}" - else - ""; - in - { - name = getFullSubmoduleName { inherit url; }; - value = { - url = "${url}${flakeRev}"; - }; - } - ) modules; + flakeRev = + if (hasAttr "rev" _repoInfo) then + "/${_repoInfo.rev}" + else if (hasAttr "narHash" _repoInfo) then + "?narHash=${_repoInfo.narHash}" + else + ""; + in + { + name = getFullSubmoduleName { inherit url; }; + value = { + url = "${url}${flakeRev}"; + }; + } + ) + ( + filter ( + mod: !(hasAttr "skipModuleAsInput" mod._repoInfo && mod._repoInfo.skipModuleAsInput) + ) modules + ); moduleInputs = flatten ( map ( @@ -135,10 +143,13 @@ let i else "${ - getFullSubmoduleName { - inherit (_repoInfo) url; - subMod = meta.name; - } + if (hasAttr "skipModuleAsInput" _repoInfo && _repoInfo.skipModuleAsInput) then + "icedos-config" + else + getFullSubmoduleName { + inherit (_repoInfo) url; + subMod = meta.name; + } }-${i}"; value = removeAttrs inputs.${i} [ "override" ]; } @@ -169,7 +180,11 @@ let maskedInputs = { inherit (inputs) nixpkgs home-manager; icedos-state = if (hasAttr "icedos-state" inputs) then inputs.icedos-state else null; - self = inputs.${getFullSubmoduleName { inherit (_repoInfo) url; }}; + self = + if (hasAttr "skipModuleAsInput" _repoInfo && _repoInfo.skipModuleAsInput) then + "icedos-config" + else + inputs.${getFullSubmoduleName { inherit (_repoInfo) url; }}; } // remappedInputs; in @@ -348,7 +363,53 @@ let ) ); - outputs = getExternalModuleOutputs deduped; + outputsFromDeps = getExternalModuleOutputs deduped; + + outputsFromExtraModules = + let + configFlake = + if (hasAttr "icedos-config" inputs) then + inputs.icedos-config + else + builtins.getFlake "path:${ICEDOS_CONFIG_ROOT}"; + inherit (configFlake) narHash; + + importExtraModule = + extra: + (import extra { icedosLib = finalIcedosLib; }) + // { + _repoInfo = { + inherit narHash; + url = "path:${extra}"; + skipModuleAsInput = true; + }; + meta.name = extra; + }; + + extraModules = + if (pathExists "${configFlake}/extra-modules") then + map importExtraModule ( + flatten ( + icedosLib.scanModules { + path = "${configFlake}/extra-modules"; + filename = "icedos.nix"; + } + ) + ) + else + [ ]; + in + getExternalModuleOutputs (flatten extraModules); + + nixosModules = + params: (outputsFromDeps.nixosModules params) ++ (outputsFromExtraModules.nixosModules params); + + outputs = outputsFromDeps // { + inherit nixosModules; + inputs = outputsFromDeps.inputs ++ outputsFromExtraModules.inputs; + outputs = outputsFromDeps.outputs ++ outputsFromExtraModules.outputs; + nixosModulesText = outputsFromDeps.nixosModulesText ++ outputsFromExtraModules.nixosModulesText; + }; in outputs; }; From 1214da591b95ca165dbbf6cc2b3a52b14f2b8d07 Mon Sep 17 00:00:00 2001 From: IceDBorn Date: Sun, 8 Feb 2026 19:20:16 +0200 Subject: [PATCH 2/6] refactor: rework module resolution logic Rework module resolution logic, breaking it into smaller reusable functions and adding more comments --- lib/icedos.nix | 670 ++++++++++++++++++++++++++++++------------------- 1 file changed, 411 insertions(+), 259 deletions(-) diff --git a/lib/icedos.nix b/lib/icedos.nix index 01db67b9..331cf314 100644 --- a/lib/icedos.nix +++ b/lib/icedos.nix @@ -15,7 +15,12 @@ let replaceStrings ; - inherit (lib) flatten hasAttrByPath; + inherit (lib) + elem + filter + flatten + hasAttrByPath + ; inherit (icedosLib) ICEDOS_CONFIG_ROOT @@ -30,6 +35,8 @@ let finalIcedosLib = icedosLib // rec { inputIsOverride = { input }: (hasAttr "override" input) && input.override; + # Generate a sanitized full submodule name from URL and submodule name + # Replaces special characters with underscores for avoiding flake registry warnings and errors getFullSubmoduleName = { url, @@ -39,6 +46,42 @@ let if subMod == null then "${INPUTS_PREFIX}-${url}" else "${INPUTS_PREFIX}-${url}-${subMod}" ); + _readFlakeLock = readFile "${self}/flake.lock" |> builtins.fromJSON; + + # Determine the revision suffix from flake.lock based on repo name + # Returns either /{rev}, ?narHash={hash}, or empty string + _getRevisionFromLock = + { + repoName, + lock, + }: + let + hasRev = hasAttrByPath [ "nodes" repoName "locked" "rev" ] lock; + hasNarHash = hasAttrByPath [ "nodes" repoName "locked" "narHash" ] lock; + in + if (builtins.getEnv "ICEDOS_UPDATE" == "1") || (!hasRev && !hasNarHash) then + "" + else if hasRev then + "/${lock.nodes.${repoName}.locked.rev}" + else + "?narHash=${lock.nodes.${repoName}.locked.narHash}"; + + # Get the flake revision string (with / or ? prefix if available) + _resolveFlakeRevision = + { + url, + repoName, + }: + if (!(pathExists "${self}/flake.lock")) || ((stringStartsWith "path:" url) && (ICEDOS_STAGE == "genflake")) then + "" + else + _getRevisionFromLock { + inherit repoName; + lock = _readFlakeLock; + }; + + # Fetch a modules repository, resolving the URL and loading its icedos modules + # Handles overrides, flake resolution, and module file loading fetchModulesRepository = { url, @@ -46,38 +89,26 @@ let ... }: let - inherit (builtins) - fromJSON - getEnv - getFlake - ; - - _url = if (hasAttr url overrides) then overrides.${url} else url; - + inherit (builtins) getFlake; inherit (lib) optionalAttrs; + # Apply override URL if available + _url = if (hasAttr url overrides) then overrides.${url} else url; repoName = getFullSubmoduleName { url = _url; }; - flakeRev = - let - lock = fromJSON (readFile "${self}/flake.lock"); - in - if (getEnv "ICEDOS_UPDATE" == "1") then - "" - else if (stringStartsWith "path:" _url) && (ICEDOS_STAGE == "genflake") then - "" - else if (hasAttrByPath [ "nodes" repoName "locked" "rev" ] lock) then - "/${lock.nodes.${repoName}.locked.rev}" - else if (hasAttrByPath [ "nodes" repoName "locked" "narHash" ] lock) then - "?narHash=${lock.nodes.${repoName}.locked.narHash}" - else - ""; + # Resolve the flake revision from lock file + flakeRev = _resolveFlakeRevision { + url = _url; + inherit repoName; + }; - rev = if (pathExists "${self}/flake.lock") then flakeRev else ""; + # Build complete flake URL with revision + flakeUrl = "${_url}${flakeRev}"; - flakeUrl = "${_url}${rev}"; + # Load the flake (either fresh or from inputs) flake = if (ICEDOS_STAGE == "genflake") then (getFlake flakeUrl) else inputs.${repoName}; + # Extract icedos modules from the flake modules = flake.icedosModules { icedosLib = finalIcedosLib; }; in { @@ -87,130 +118,285 @@ let } // (optionalAttrs (hasAttr "rev" flake) { inherit (flake) rev; }); - getExternalModuleOutputs = + # Convert external modules into flake input declarations + # Filters out modules marked to skip as inputs + _modulesToInputs = + modules: + let + inherit (builtins) filter; + shouldIncludeAsInput = + mod: !(hasAttr "skipModuleAsInput" mod._repoInfo && mod._repoInfo.skipModuleAsInput); + in + map ( + { _repoInfo, ... }: + let + inherit (_repoInfo) url; + flakeRev = + if (hasAttr "rev" _repoInfo) then + "/${_repoInfo.rev}" + else if (hasAttr "narHash" _repoInfo) then + "?narHash=${_repoInfo.narHash}" + else + ""; + in + { + name = getFullSubmoduleName { inherit url; }; + value = { + url = "${url}${flakeRev}"; + }; + } + ) (filter shouldIncludeAsInput modules); + + # Extract input dependencies from modules and create properly namespaced input declarations + # Handles override inputs separately to preserve their names + _getModuleInputs = modules: let inherit (builtins) attrNames filter; - inherit (lib) flatten hasAttr listToAttrs; - - modulesAsInputs = - map - ( - { _repoInfo, ... }: - let - inherit (_repoInfo) url; - - flakeRev = - if (hasAttr "rev" _repoInfo) then - "/${_repoInfo.rev}" - else if (hasAttr "narHash" _repoInfo) then - "?narHash=${_repoInfo.narHash}" - else - ""; - in - { - name = getFullSubmoduleName { inherit url; }; - value = { - url = "${url}${flakeRev}"; - }; - } - ) - ( - filter ( - mod: !(hasAttr "skipModuleAsInput" mod._repoInfo && mod._repoInfo.skipModuleAsInput) - ) modules - ); - - moduleInputs = flatten ( + modulesWithInputs = filter (hasAttr "inputs") modules; + in + flatten ( + map ( + { + _repoInfo, + inputs, + meta, + ... + }: map ( + i: + let + isOverride = inputIsOverride { input = inputs.${i}; }; + moduleIdentifier = + if (hasAttr "skipModuleAsInput" _repoInfo && _repoInfo.skipModuleAsInput) then + "icedos-config" + else + getFullSubmoduleName { + inherit (_repoInfo) url; + subMod = meta.name; + }; + in { - _repoInfo, - inputs, - meta, - ... - }: - map ( - i: - let - isOverride = inputIsOverride { - input = inputs.${i}; - }; - in - { - _originalName = i; - name = - if isOverride then - i - else - "${ - if (hasAttr "skipModuleAsInput" _repoInfo && _repoInfo.skipModuleAsInput) then - "icedos-config" - else - getFullSubmoduleName { - inherit (_repoInfo) url; - subMod = meta.name; - } - }-${i}"; - value = removeAttrs inputs.${i} [ "override" ]; - } - ) (attrNames inputs) - ) (filterByAttrs [ "inputs" ] modules) - ); + _originalName = i; + name = if isOverride then i else "${moduleIdentifier}-${i}"; + value = removeAttrs inputs.${i} [ "override" ]; + } + ) (attrNames inputs) + ) modulesWithInputs + ); - inputs = modulesAsInputs ++ moduleInputs; + # Create a masked inputs set for nixos module evaluation + # Ensures modules use consistent input names and see appropriate dependencies + _createMaskedInputs = + { + baseInputs, + moduleInputs, + repoInfo, + isSkipModuleAsInput, + }: + { + inherit (baseInputs) nixpkgs home-manager; + icedos-state = if (hasAttr "icedos-state" baseInputs) then baseInputs.icedos-state else null; - options = map ( - { options, ... }: - { - inherit options; - } - ) (filterByAttrs [ "options" ] modules); + self = + if isSkipModuleAsInput then + "icedos-config" + else + baseInputs.${getFullSubmoduleName { url = repoInfo.url; }}; + } + // ( + let + inherit (builtins) listToAttrs; + in + listToAttrs ( + map (i: { + name = i._originalName; + value = baseInputs.${i.name}; + }) moduleInputs + ) + ); + + # Extract all options declarations from modules that define them + _getModuleOptions = + modules: + map ( + { options, ... }: + { + inherit options; + } + ) (filterByAttrs [ "options" ] modules); + + # Process output modules into nixos modules with proper input masking + # Each module's outputs are evaluated with its appropriate input set + _extractNixosModules = + { + inputs, + modules, + }: + let + inherit (lib) flatten; - nixosModulesPerIcedosModule = + moduleInputs = _getModuleInputs modules; + + processModuleOutputs = { inputs, ... }: { _repoInfo, outputs, ... }: let - remappedInputs = listToAttrs ( - map (i: { - name = i._originalName; - value = inputs.${i.name}; - }) moduleInputs - ); - - maskedInputs = { - inherit (inputs) nixpkgs home-manager; - icedos-state = if (hasAttr "icedos-state" inputs) then inputs.icedos-state else null; - self = - if (hasAttr "skipModuleAsInput" _repoInfo && _repoInfo.skipModuleAsInput) then - "icedos-config" - else - inputs.${getFullSubmoduleName { inherit (_repoInfo) url; }}; - } - // remappedInputs; + maskedInputs = _createMaskedInputs { + baseInputs = inputs; + inherit moduleInputs; + repoInfo = _repoInfo; + isSkipModuleAsInput = hasAttr "skipModuleAsInput" _repoInfo && _repoInfo.skipModuleAsInput; + }; in outputs.nixosModules { inputs = maskedInputs; }; + in + flatten ( + map (processModuleOutputs { inherit inputs; }) (filterByAttrs [ "outputs" "nixosModules" ] modules) + ); + + # Main function to extract all outputs from external modules + # Combines inputs, nixos modules, options, and module text outputs + getExternalModuleOutputs = + modules: + let + inherit (lib) flatten; + + modulesAsInputs = _modulesToInputs modules; + moduleInputs = _getModuleInputs modules; + options = _getModuleOptions modules; nixosModules = params: - flatten ( - map (nixosModulesPerIcedosModule params) (filterByAttrs [ "outputs" "nixosModules" ] modules) - ); + _extractNixosModules { + inputs = params.inputs; + inherit modules; + }; - nixosModulesText = ( - flatten ( - map (mod: mod.outputs.nixosModulesText) (filterByAttrs [ "outputs" "nixosModulesText" ] modules) - ) + nixosModulesText = flatten ( + map (mod: mod.outputs.nixosModulesText) (filterByAttrs [ "outputs" "nixosModulesText" ] modules) ); in { + inputs = modulesAsInputs ++ moduleInputs; + inherit - inputs nixosModules nixosModulesText options ; }; + # Generate a unique key for a module (url/name combination) + _getModuleKey = url: name: "${url}/${name}"; + + # Build a set of override URL mappings from dependencies that define overrides + _buildOverridesMap = + { + newDeps, + loadOverrides, + existingOverrides, + }: + let + inherit (builtins) filter listToAttrs; + filteredDeps = filter (hasAttr "overrideUrl") newDeps; + in + if loadOverrides then + listToAttrs ( + map (dep: { + name = dep.url; + value = dep.overrideUrl; + }) filteredDeps + ) + else + existingOverrides; + + # Load module files from a repository and ensure a default module exists + # Returns list of modules with _repoInfo attached to each + _loadModulesFromRepo = + repo: + let + modules = map ( + f: + { + _repoInfo = repo; + } + // import f { + inherit config lib; + icedosLib = finalIcedosLib; + } + ) repo.files; + + hasDefault = findFirst (mod: mod.meta.name == "default") modules != null; + in + if hasDefault then + modules + else + modules + ++ [ + { + _repoInfo = repo; + meta.name = "default"; + } + ]; + + # Check if a module is already loaded (by key) + _isModuleLoaded = + existingDeps: url: name: + elem (_getModuleKey url name) existingDeps; + + # Filter new modules to only include those that are needed and not already loaded + _filterNewModules = + { + modules, + existingDeps, + requestedNames, + }: + let + inherit (builtins) filter; + + isRequested = mod: (mod.meta.name == "default") || (elem mod.meta.name requestedNames); + isNew = mod: !_isModuleLoaded existingDeps mod._repoInfo.url mod.meta.name; + in + filter (mod: isRequested mod && isNew mod) modules; + + # Extract internal dependencies from a module's metadata + # Optionally includes optional dependencies based on flag + _getModuleDependencies = + { + mod, + fetchOptionalDependencies, + }: + let + inherit (mod) meta; + baseDeps = meta.dependencies or [ ]; + optionalDeps = if fetchOptionalDependencies then (meta.optionalDependencies or [ ]) else [ ]; + in + baseDeps ++ optionalDeps; + + # Convert dependency metadata to resolved dependency entries (filtering already-loaded modules) + _resolveDependencyEntries = + { + deps, + sourceUrl, + allKnownKeys, + }: + map ( + { + url ? sourceUrl, + modules ? [ ], + }: + let + realUrl = if (url == "self") then sourceUrl else url; + in + { + url = realUrl; + modules = filter (mod: !elem (_getModuleKey realUrl mod) allKnownKeys) modules; + } + ) deps; + + # Recursively resolve external dependencies, fetching repositories and extracting modules + # Handles deduplication and override merging across the entire dependency tree resolveExternalDependencyRecursively = { newDeps, @@ -220,122 +406,114 @@ let }: let inherit (builtins) - elem filter foldl' length - listToAttrs ; - inherit (lib) optional optionals unique; - - getModuleKey = url: name: "${url}/${name}"; - - overrides = - let - filteredDeps = filter (hasAttr "overrideUrl") newDeps; - - result = listToAttrs ( - map (dep: { - name = dep.url; - value = dep.overrideUrl; - }) filteredDeps - ); - in - if loadOverrides then result else existingOverrides; + inherit (lib) optional unique; - loadModulesFromRepo = - repo: - let - modules = map ( - f: - { - _repoInfo = repo; - } - // import f { - inherit config lib; - icedosLib = finalIcedosLib; - } - ) repo.files; - - hasDefault = findFirst (mod: mod.meta.name == "default") modules != null; - - result = - if hasDefault then - modules - else - ( - modules - ++ [ - { - _repoInfo = repo; - meta.name = "default"; - } - ] - ); - in - result; + # Build override map from new dependencies or use existing + overrides = _buildOverridesMap { + inherit newDeps loadOverrides existingOverrides; + }; + # Process each dependency and accumulate results result = foldl' ( acc: newDep: let - # Get list of needed modules - missingModules = ( - filter (mod: !elem (getModuleKey newDep.url mod) existingDeps) (newDep.modules or [ ]) - ); + # Determine which modules are not yet loaded + missingModules = filter (mod: !_isModuleLoaded existingDeps newDep.url mod) (newDep.modules or [ ]); - # Optional new repo + # Fetch repository if new modules are needed or default isn't loaded newRepo = optional ( - (length missingModules) > 0 || !elem (getModuleKey newDep.url "default") existingDeps + ((length missingModules) > 0) || !_isModuleLoaded existingDeps newDep.url "default" ) (fetchModulesRepository (newDep // { inherit overrides; })); - # Convert to list of modules - newModules = filter ( - mod: - (!elem (getModuleKey mod._repoInfo.url mod.meta.name) existingDeps) - && (elem mod.meta.name (newDep.modules or [ ]) || mod.meta.name == "default") - ) (flatMap loadModulesFromRepo newRepo); + # Load and filter modules from the repository + newModules = _filterNewModules { + modules = flatMap _loadModulesFromRepo newRepo; + existingDeps = existingDeps; + requestedNames = newDep.modules or [ ]; + }; - # Convert to keys - newModulesKeys = map (mod: getModuleKey mod._repoInfo.url mod.meta.name) newModules; - allKnownKeys = (unique (existingDeps ++ newModulesKeys)); + # Build set of all known module keys (existing + new) + newModulesKeys = map (mod: _getModuleKey mod._repoInfo.url mod.meta.name) newModules; + allKnownKeys = unique (existingDeps ++ newModulesKeys); - # Get deps + # Extract and resolve nested dependencies from new modules innerDeps = flatMap ( mod: - map - ( - { - url ? newDep.url, - modules ? [ ], - }: - { - url = if (url == "self") then newDep.url else url; - modules = filter (mod: !elem (getModuleKey url mod) allKnownKeys) modules; - } - ) - ( - let - inherit (mod) meta; - in - (meta.dependencies or [ ]) - ++ optionals (newDep.fetchOptionalDependencies or false) (meta.optionalDependencies or [ ]) - ) + _resolveDependencyEntries { + deps = _getModuleDependencies { + inherit mod; + fetchOptionalDependencies = newDep.fetchOptionalDependencies or false; + }; + sourceUrl = newDep.url; + inherit allKnownKeys; + } ) newModules; - in - flatten ( - acc - ++ newModules - ++ optional ((length innerDeps) > 0) (resolveExternalDependencyRecursively { + + # Recursively resolve inner dependencies if any + resolvedInnerDeps = optional ((length innerDeps) > 0) (resolveExternalDependencyRecursively { newDeps = innerDeps; existingDeps = allKnownKeys; existingOverrides = overrides; - }) - ) + }); + in + flatten (acc ++ newModules ++ resolvedInnerDeps) ) [ ] newDeps; in result; + # Import an extra module file and attach repository info + # Extra modules are stored locally in the config directory + _importExtraModule = + { + filePath, + narHash, + }: + (import filePath { icedosLib = finalIcedosLib; }) + // { + _repoInfo = { + inherit narHash; + url = "path:${filePath}"; + skipModuleAsInput = true; + }; + meta.name = filePath; + }; + + # Load all extra modules from the config's extra-modules directory + # Returns empty list if no extra modules directory exists + _loadExtraModules = + { + configFlake, + narHash, + }: + let + extraModulesPath = "${configFlake}/extra-modules"; + in + if !(pathExists extraModulesPath) then + [ ] + else + map (filePath: _importExtraModule { inherit filePath narHash; }) ( + flatten ( + icedosLib.scanModules { + path = extraModulesPath; + filename = "icedos.nix"; + } + ) + ); + + # Get the configuration flake (either from inputs or local filesystem) + _getConfigFlake = + if (hasAttr "icedos-config" inputs) then + inputs.icedos-config + else + builtins.getFlake "path:${ICEDOS_CONFIG_ROOT}"; + + # Main function to resolve and process all modules from config + # Deduplicates modules, extracts outputs, and combines external + extra modules modulesFromConfig = let inherit (builtins) @@ -343,72 +521,46 @@ let listToAttrs ; - inherit (lib) - flatten - ; + inherit (lib) flatten; - modules = ( + # Resolve external dependencies from config repositories + externalModules = ( resolveExternalDependencyRecursively { newDeps = config.repositories; loadOverrides = true; } ); + # Deduplicate modules by (url, name) pair deduped = attrValues ( listToAttrs ( map (m: { name = "${m._repoInfo.url}-${m.meta.name}"; value = m; - }) (flatten modules) + }) (flatten externalModules) ) ); - outputsFromDeps = getExternalModuleOutputs deduped; + # Get outputs from external modules + externalOutputs = getExternalModuleOutputs deduped; - outputsFromExtraModules = - let - configFlake = - if (hasAttr "icedos-config" inputs) then - inputs.icedos-config - else - builtins.getFlake "path:${ICEDOS_CONFIG_ROOT}"; - inherit (configFlake) narHash; - - importExtraModule = - extra: - (import extra { icedosLib = finalIcedosLib; }) - // { - _repoInfo = { - inherit narHash; - url = "path:${extra}"; - skipModuleAsInput = true; - }; - meta.name = extra; - }; - - extraModules = - if (pathExists "${configFlake}/extra-modules") then - map importExtraModule ( - flatten ( - icedosLib.scanModules { - path = "${configFlake}/extra-modules"; - filename = "icedos.nix"; - } - ) - ) - else - [ ]; - in - getExternalModuleOutputs (flatten extraModules); + # Get config flake and load extra modules + configFlake = _getConfigFlake; + inherit (configFlake) narHash; + extraModules = _loadExtraModules { inherit configFlake narHash; }; - nixosModules = - params: (outputsFromDeps.nixosModules params) ++ (outputsFromExtraModules.nixosModules params); + # Get outputs from extra modules + extraOutputs = getExternalModuleOutputs (flatten extraModules); + + # Combine nixos modules from both external and extra sources + nixosModules = params: (externalOutputs.nixosModules params) ++ (extraOutputs.nixosModules params); - outputs = outputsFromDeps // { + # Final combined outputs + outputs = externalOutputs // { inherit nixosModules; - inputs = outputsFromDeps.inputs ++ outputsFromExtraModules.inputs; - outputs = outputsFromDeps.outputs ++ outputsFromExtraModules.outputs; - nixosModulesText = outputsFromDeps.nixosModulesText ++ outputsFromExtraModules.nixosModulesText; + inputs = externalOutputs.inputs ++ extraOutputs.inputs; + outputs = externalOutputs.outputs ++ extraOutputs.outputs; + nixosModulesText = externalOutputs.nixosModulesText ++ extraOutputs.nixosModulesText; }; in outputs; From bab10605494bca1ade6e927ef2024329ccb06a12 Mon Sep 17 00:00:00 2001 From: IceDBorn Date: Sun, 8 Feb 2026 19:20:54 +0200 Subject: [PATCH 3/6] feat(build): add new experimental feature pipe-operators --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 04c8c345..705242ab 100644 --- a/build.sh +++ b/build.sh @@ -78,7 +78,7 @@ while [[ $# -gt 0 ]]; do esac done -export NIX_CONFIG="experimental-features = flakes nix-command" +export NIX_CONFIG="experimental-features = flakes nix-command pipe-operators" mkdir -p "$ICEDOS_DIR" From 36f4805a73dbd5540bdc0794093befb2de5d4075 Mon Sep 17 00:00:00 2001 From: IceDBorn Date: Sun, 8 Feb 2026 19:21:23 +0200 Subject: [PATCH 4/6] fix(readme): update install instructions for first install --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa78984a..cbde9080 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ To get started with the default template, run the following commands: ```bash git clone https://github.com/icedos/template icedos cd icedos -nix run . --boot --first-install +nix --extra-experimental-features "flakes nix-command pipe-operators" run . --boot ``` ## ⚙️ Configuration From 30934339f43eb8c3bf5e8c859ee908b17bc64304 Mon Sep 17 00:00:00 2001 From: IceDBorn Date: Sun, 8 Feb 2026 19:23:31 +0200 Subject: [PATCH 5/6] chore(readme): remove not needed first install flag --- build.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/build.sh b/build.sh index 705242ab..f947513d 100644 --- a/build.sh +++ b/build.sh @@ -11,7 +11,6 @@ action="switch" globalBuildArgs=() nhBuildArgs=() nixBuildArgs=() -isFirstInstall="" set -e set -o pipefail @@ -62,10 +61,6 @@ while [[ $# -gt 0 ]]; do globalBuildArgs=("$@") break ;; - --first-install) - isFirstInstall=1 - shift - ;; --logs) export ICEDOS_LOGGING=1 trace="--show-trace" @@ -150,7 +145,7 @@ echo "Building from path $ICEDOS_BUILD_DIR" cd $ICEDOS_BUILD_DIR # Build the system configuration -if (( ${#nixBuildArgs[@]} != 0 )) || [[ "$isFirstInstall" == 1 ]]; then +if (( ${#nixBuildArgs[@]} != 0 )); then sudo nixos-rebuild $action --flake .#"$(cat /etc/hostname)" $trace ${nixBuildArgs[*]} ${globalBuildArgs[*]} exit 0 fi From 862ad4d0e02b0293955d4bef2319cae13b8688ef Mon Sep 17 00:00:00 2001 From: IceDBorn Date: Sun, 8 Feb 2026 20:03:15 +0200 Subject: [PATCH 6/6] fix(build): core changes being one revision behind --- build.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/build.sh b/build.sh index f947513d..e8cc62c3 100644 --- a/build.sh +++ b/build.sh @@ -15,6 +15,8 @@ nixBuildArgs=() set -e set -o pipefail +previous_arguments=$@ + while [[ $# -gt 0 ]]; do case $1 in --boot) @@ -88,12 +90,11 @@ if [ "$update_repos" == "1" ]; then refresh="--refresh" fi -if [ "$update_core" == "1" ]; then - ( - set -e - cd "$ICEDOS_CONFIG_ROOT" - nix flake update - ) +if [[ "$update_core" == "1" && -z "$skip_update_core" ]]; then + cd "$ICEDOS_CONFIG_ROOT" + nix flake update + exec env skip_update_core=1 nix run . -- $previous_arguments + exit 0 fi # Generate flake inputs @@ -116,7 +117,7 @@ ICEDOS_STAGE="genflake" nix eval $trace --file "$ICEDOS_ROOT/lib/genflake.nix" - nixfmt "$ICEDOS_STATE_DIR/$FLAKE" if [ "$export_full_config" == "1" ]; then - ICEDOS_STAGE="genflake" nix eval $trace --file "./lib/genflake.nix" evaluatedConfig | nixfmt | jq -r . > .cache/full-config.json + ICEDOS_STAGE="genflake" nix eval $trace --file "$ICEDOS_ROOT/lib/genflake.nix" evaluatedConfig | nixfmt | jq -r . > .cache/full-config.json jsonfmt .cache/full-config.json -w toml2json ./config.toml > .cache/config.json