From 202eca2d03c376b5bfb05b20a9881f9c597af380 Mon Sep 17 00:00:00 2001 From: "Finn@MiBook" Date: Mon, 21 Sep 2026 15:27:07 +0200 Subject: [PATCH] fix(homepage): merge service groups by name to stop manual entries being dropped homepage-dashboard resolves services.yaml groups by name using first-match lookup, so the module-contributed "Services" group and manual-services.nix's "Services" group (two separate top-level YAML list entries) caused the second one to be silently discarded. Merge by group name before handing the list to services.homepage-dashboard.services. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01JbLevsLozTCcCWe1SoUGe1 --- modules/environments/homepage/default.nix | 142 ++++++++++++---------- 1 file changed, 78 insertions(+), 64 deletions(-) diff --git a/modules/environments/homepage/default.nix b/modules/environments/homepage/default.nix index 4a55015..73faec3 100644 --- a/modules/environments/homepage/default.nix +++ b/modules/environments/homepage/default.nix @@ -12,79 +12,93 @@ let manualServices = import ./manual-services.nix; manualWidgets = import ./manual-widgets.nix; - groupedServices = - lib.foldl' - ( - acc: entry: - acc - // { - ${entry.group} = (acc.${entry.group} or [ ]) ++ [ entry ]; - } - ) - { } - config.my.homepage.services; - - homepageServices = lib.mapAttrsToList ( - group: entries: - { - ${group} = map ( - entry: - let - service = builtins.removeAttrs entry [ - "group" - "name" - ]; - in - { - ${entry.name} = lib.filterAttrs (_: value: value != null) service; - } - ) entries; + groupedServices = lib.foldl' ( + acc: entry: + acc + // { + ${entry.group} = (acc.${entry.group} or [ ]) ++ [ entry ]; } - ) groupedServices; + ) { } config.my.homepage.services; + + homepageServices = lib.mapAttrsToList (group: entries: { + ${group} = map ( + entry: + let + service = builtins.removeAttrs entry [ + "group" + "name" + ]; + in + { + ${entry.name} = lib.filterAttrs (_: value: value != null) service; + } + ) entries; + }) groupedServices; + + # homepage-dashboard keeps only the first services.yaml list entry with a + # given group name (first match wins), so module-contributed and manual + # groups sharing a name (e.g. "Services") must be merged here rather than + # simply concatenated, or the later ones are silently dropped. + mergeGroups = + lists: + let + merged = lib.foldl' ( + acc: groupAttrs: + let + groupName = builtins.head (builtins.attrNames groupAttrs); + in + acc // { ${groupName} = (acc.${groupName} or [ ]) ++ groupAttrs.${groupName}; } + ) { } lists; + in + lib.mapAttrsToList (name: entries: { ${name} = entries; }) merged; in { - options.my.homepage.services = with lib; mkOption { - type = types.listOf ( - types.submodule { - options = { - group = mkOption { - type = types.str; - description = "Homepage service group"; - }; + options.my.homepage.services = + with lib; + mkOption { + type = types.listOf ( + types.submodule { + options = { + group = mkOption { + type = types.str; + description = "Homepage service group"; + }; - name = mkOption { - type = types.str; - description = "Homepage service name"; - }; + name = mkOption { + type = types.str; + description = "Homepage service name"; + }; - description = mkOption { - type = types.str; - description = "Homepage service description"; - }; + description = mkOption { + type = types.str; + description = "Homepage service description"; + }; - href = mkOption { - type = types.str; - description = "Homepage service URL"; - }; + href = mkOption { + type = types.str; + description = "Homepage service URL"; + }; - icon = mkOption { - type = types.nullOr types.str; - default = null; - description = "Optional Homepage service icon"; + icon = mkOption { + type = types.nullOr types.str; + default = null; + description = "Optional Homepage service icon"; + }; }; - }; - } - ); - default = [ ]; - description = "Merged homepage service metadata contributed by repo modules."; - }; + } + ); + default = [ ]; + description = "Merged homepage service metadata contributed by repo modules."; + }; - options.my.homepage.widgets = with lib; mkOption { - type = types.listOf types.attrs; - default = [ ]; - description = "Widget definitions passed directly to services.homepage-dashboard.widgets. Each entry is an attrset like { resources = { cpu = true; memory = true; }; }."; - }; + options.my.homepage.widgets = + with lib; + mkOption { + type = types.listOf types.attrs; + default = [ ]; + description = "Widget definitions passed directly to services.homepage-dashboard.widgets. Each entry is an attrset like { resources = { cpu = true; memory = true; }; }."; + }; options.my.profiles.homepage = with lib; { enable = mkEnableOption "getHomepage.dev Dashboard"; @@ -98,7 +112,7 @@ in listenPort = dashboardPort; allowedHosts = "${dashboardHost}:${toString dashboardPort},192.168.178.65:${toString dashboardPort},localhost:${toString dashboardPort},127.0.0.1:${toString dashboardPort},jupiter:${toString dashboardPort},jupiter.solar.internal:${toString dashboardPort}"; bookmarks = import ./bookmarks.nix; - services = homepageServices ++ manualServices; + services = mergeGroups (homepageServices ++ manualServices); widgets = config.my.homepage.widgets ++ manualWidgets; };