Commit 834a83c

HPCesia <me@hpcesia.com>
2026-09-23 12:23:40
deps: update den to latest
Assisted-by: crush:deepseek-v4.1-flash
1 parent de7ea35
Changed files (3)
modules/users/schema.nix
@@ -2,43 +2,82 @@
   lib,
   config,
   ...
-}: {
+}: let
+  # Identity fields other than `displayName`, whose default differs between the
+  # shared registry (the registry key) and the entity (the injected value).
+  identityFields = {
+    options = {
+      email = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = null;
+        description = "Email address for the user";
+      };
+      avatar = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        description = "Avatar for the user";
+      };
+      sshKeys = lib.mkOption {
+        type = lib.types.listOf lib.types.str;
+        default = [];
+      };
+      gpgKeys = lib.mkOption {
+        type = lib.types.listOf (lib.types.either lib.types.path lib.types.str);
+        default = [];
+        description = "OpenPGP public keys (paths or armored key text) for this user";
+      };
+    };
+  };
+
+  identityType = lib.types.submodule {
+    imports = [identityFields];
+    options.displayName = lib.mkOption {
+      type = lib.types.str;
+      description = "Display name for the user";
+    };
+  };
+
+  sharedIdentity = lib.mkOptionType {
+    name = "den-shared-user-identity";
+    check = v: builtins.isAttrs v;
+    merge = loc: defs: let
+      shared =
+        if builtins.length loc < 2
+        then {}
+        else let
+          username = lib.elemAt loc (builtins.length loc - 2);
+        in
+          (config.den.users.${username} or {}).identity or {displayName = username;};
+    in
+      identityType.merge loc ([
+          {
+            file = "<den.users>";
+            value = lib.mkDefault shared;
+          }
+        ]
+        ++ defs);
+  };
+in {
   # Shared user schema across every host
   options.den.users = lib.mkOption {
     type = lib.types.attrsOf (
-      lib.types.submodule ({name, ...}: {
+      lib.types.submodule {
         freeformType = lib.types.attrsOf lib.types.anything;
         options = {
           admin = lib.mkEnableOption "Whether to add user to admin user";
           identity = lib.mkOption {
-            type = lib.types.submodule {
-              options = {
-                displayName = lib.mkOption {
+            type = lib.types.submodule (
+              {name, ...}: let
+                userName = name;
+              in {
+                imports = [identityFields];
+                options.displayName = lib.mkOption {
                   type = lib.types.str;
-                  default = name;
+                  default = userName;
                   description = "Display name for the user";
                 };
-                email = lib.mkOption {
-                  type = lib.types.nullOr lib.types.str;
-                  default = null;
-                  description = "Email address for the user";
-                };
-                avatar = lib.mkOption {
-                  type = lib.types.nullOr lib.types.path;
-                  default = null;
-                  description = "Avatar for the user";
-                };
-                sshKeys = lib.mkOption {
-                  type = lib.types.listOf lib.types.str;
-                  default = [];
-                };
-                gpgKeys = lib.mkOption {
-                  type = lib.types.listOf (lib.types.either lib.types.path lib.types.str);
-                  default = [];
-                  description = "OpenPGP public keys (paths or armored key text) for this user";
-                };
-              };
-            };
+              }
+            );
             default = {};
           };
           system = lib.mkOption {
@@ -56,7 +95,7 @@
             default = {};
           };
         };
-      })
+      }
     );
     default = {};
     description = ''
@@ -67,23 +106,12 @@
     '';
   };
 
-  config.den.schema.user = {
-    # Per-user name is only recoverable from the freeform merge's option path.
-    options._module.freeformType = lib.mkOption {
-      apply = t:
-        if t == null
-        then null
-        else
-          lib.mkOptionType {
-            name = "den-shared-user-freeform";
-            check = builtins.isAttrs;
-            # Freeform merge never runs without any definitions.
-            merge = loc: defs:
-              lib.removeAttrs (
-                lib.recursiveUpdate (config.den.users.${lib.last loc} or {}) (t.merge loc defs)
-              ) ["ixSharedSentinel"];
-          };
-    };
-    config.ixSharedSentinel = true;
+  config.den.schema.user.options.identity = lib.mkOption {
+    type = sharedIdentity;
+    default = {};
+    description = ''
+      User identity fields, merged with the shared `den.users.<name>.identity`
+      registry entry as a lowest-priority definition.
+    '';
   };
 }
modules/users/theme.nix
@@ -19,22 +19,22 @@
     "themes"
   ];
 
-  # Navigated refs lack name/meta; derive them from __provider so
+  # Navigated refs lack name/meta; derive them from __aspectChain so
   # guard-time hasAspect matches what the walker assigns.
   normalizeRef = v:
     if
       builtins.isAttrs v
       && !(v ? name)
-      && builtins.isList (v.__provider or [])
-      && v.__provider != []
+      && builtins.isList (v.__aspectChain or [])
+      && v.__aspectChain != []
     then
       v
       // {
-        name = lib.last v.__provider;
+        name = lib.last v.__aspectChain;
         meta =
           (v.meta or {})
           // {
-            provider = lib.init v.__provider;
+            aspect-chain = lib.init v.__aspectChain;
           };
       }
     else v;
flake.lock
@@ -17,11 +17,11 @@
     },
     "den": {
       "locked": {
-        "lastModified": 1786475005,
-        "narHash": "sha256-gp7Q7rMEjsqAyW4Uwz+sms8j+XegD5P/lzRCuMW2fNI=",
+        "lastModified": 1790179224,
+        "narHash": "sha256-dxUKiY5abOjTkazki74sA39yJxq0o1Aprh1lsQKNqPY=",
         "owner": "denful",
         "repo": "den",
-        "rev": "e8e8de1e32646456cfc613f152175a7a548508ec",
+        "rev": "ba9ee3a5e8ad1f77ba8e0341b224c4a1d5999787",
         "type": "github"
       },
       "original": {