A shared home-manager configuration between NixOS and nix-darwin?

I’m trying to define a shared home-manager configuration in a home.nix file, and combine it with more options in separate files for nixos and darwin. I asked llms and tried home-manager = import ./home.nix { inherit inputs; } // { more options} but that doesn’t work.

home.nix:

    {
      inputs,
      ...
    }:
    let
      firefox-profile = "h9ep31cd.default";
      emoji-font = "Apple Color Emoji";
    in
    {
      extraSpecialArgs = { inherit inputs; };
      useGlobalPkgs = true;
      useUserPackages = true;
      backupFileExtension = "backup";
      users.user =
        {
          config,
          inputs,
          ...
        }:
        {
          imports = [ inputs.zen-browser.homeModules.twilight ];
          home = {
            stateVersion = "26.05";
            file.".config/zen/h9ep31cd.default/chrome".source =
              config.lib.file.mkOutOfStoreSymlink "/home/user/.local/share/chezmoi/Firefox/";
          };
          programs.zen-browser = {
            enable = true;
            profiles = {
              default = {
                id = 0;
                name = "Default";
                isDefault = true;
                path = firefox-profile;
                settings = {
                  # Misc Settings
                  "xpinstall.signatures.required" = false; # Don't require signatures on addons to install them. Allows sideloading addons.
                  "accessibility.typeaheadfind.manual" = false; # Disable pressing "/" key for quick find
                  "toolkit.legacyUserProfileCustomizations.stylesheets" = true;
                  "font.name-list.emoji" = emoji-font;
                  "ui.key.menuAccessKey" = 0; # Disable Alt + B from opening Menu bar
                  "apz.allow_double_tap_zooming" = false; # Don't double tap the trackpad to zoom in the page;
                  "dom.forms.autocomplete.formautofill" = false;
                  "intl.date_time.pattern_override.time_short" = "h:mm a";
                  "network.http.max-connections" = 1500;
                  "network.http.max-persistent-connections-per-server" = 64;
                  "browser.gesture.swipe.up" = "";
                  "browser.gesture.swipe.down" = "";
                  "browser.gesture.swipe.left" = "";
                  "browser.gesture.swipe.right" = "";
                  "browser.startup.homepage" = "chrome://browser/content/blanktab.html";
                  "browser.bookmarks.showMobileBookmarks" = true;
                  "browser.aboutConfig.showWarning" = false;
                  "browser.urlbar.showSearchTerms.featureGate" = true; # Show the search query in the URL bar instead of the URL (only for the default search engine).
                  "browser.urlbar.trimURLs" = false; # Show whole URLs in the URL bar.
                  "browser.tabs.closeWindowWithLastTab" = true;
                  "browser.tabs.loadBookmarksInTabs" = true; # Open bookmarks in new tabs instead of in the current one.
                  # Open new tab next to current one instead of at the rightmost.
                  "browser.tabs.insertAfterCurrent" = true;
                  "browser.tabs.insertRelatedAfterCurrent" = true;
                  "browser.quitShortcut.disabled" = true;
    
                  # Dev tools
                  "devtools.debugger.remote-enabled" = true;
                  "devtools.chrome.enabled" = true;
                  "devtools.inspector.three-pane-enabled" = false;
    
                  # Attempt to make addons work in restricted domains
                  "extensions.webextensions.restrictedDomains" = "";
                  "extensions.quarantinedDomains.enabled" = false;
                  "privacy.resistFingerprinting.block_mozAddonManager" = true;
    
                  # Right click menu
                  "browser.ml.linkPreview.enabled" = false;
                  "devtools.accessibility.enabled" = false;
                  "extensions.formautofill.creditCards.enabled" = false;
                  "privacy.query_stripping.strip_on_share.enabled" = false;
                  "browser.ml.chat.enabled" = false;
                  "browser.ml.chat.menu" = false;
                  "browser.search.visualSearch.featureGate" = false;
                  "browser.tabs.splitView.enabled" = false;
    
                  # Zen Browser specific options:
                  "zen.theme.content-element-separation" = 0; # disable border around zen window
                  "zen.tabs.close-on-back-with-no-history" = false;
                  "zen.urlbar.replace-newtab" = false;
                  "zen.window-sync.enabled" = false;
                  "zen.welcome-screen.seen" = true;
    
                  # Temp preferences
                  "browser.settings-redesign.enabled" = true;
                };
              };
              secondary = {
                id = 1;
                name = "Secondary";
                path = "o9fiaukr.2nd Profile";
                settings."zen.welcome-screen.seen" = true;
              };
            };
          };
          programs.thunderbird = {
            enable = false;
            profiles."dexxqztk.Default User" = {
              isDefault = true;
              settings = {
                "mail.minimizeToTray.startMinimized" = true;
                "mail.biff.show_tray_icon_always" = true;
                "mail.minimizeToTray.supportedDesktops" = "kde,gnome,pop:gnome,xfce,mate,hyprland";
              };
            };
          };
        };
    }

nixos.nix:

        home-manager = {
        sharedModules = [ inputs.plasma-manager.homeModules.plasma-manager ];
        users.user =
          {
            config,
            pkgs,
            ...
          }:
          {
            home = {
              file.".local/share/fonts".source = config.lib.file.mkOutOfStoreSymlink "/home/user/Sync/Fonts/";
              pointerCursor = {
                gtk.enable = true;
                package = pkgs.apple-cursor;
                name = "macOS";
                size = 22;
                x11.enable = true;
                x11.defaultCursor = "macOS";
              };
            };
            services.darkman = {
              enable = false;
              lightModeScripts.gtk-theme = ''
                ${pkgs.dconf}/bin/dconf write \
                    /org/gnome/desktop/interface/color-scheme "'prefer-light'"
              '';
              darkModeScripts.gtk-theme = ''
                ${pkgs.dconf}/bin/dconf write \
                    /org/gnome/desktop/interface/color-scheme "'prefer-dark'"
              '';
              settings = {
                usegeoclue = false;
                dbusserver = true;
                portal = true;
              };
            };
          };

Well yes, you’re supposed to use extraSpecialArgs.
Options go in options, config goes in config, other modules go in imports.
Avoid using import, ever.

As always, share the error along with the code.

I still don’t get it, can you show me what to do here?

Reread my last sentence, please.

with the home.nix specified above, when I try this I get the error The option home-manager.users.user.home.stateVersion was accessed but has no value defined (it’s in home.nix).

  home-manager = import ./home.nix { inherit inputs; } // {
    # home-manager = {
    sharedModules = [ inputs.plasma-manager.homeModules.plasma-manager ];
    users.user =
      {
        config,
        pkgs,
        ...
      }:
      {
        home = {
          file.".local/share/fonts".source = config.lib.file.mkOutOfStoreSymlink "/home/user/Sync/Fonts/";
          pointerCursor = {
            gtk.enable = true;
            package = pkgs.apple-cursor;
            name = "macOS";
            size = 22;
            x11.enable = true;
            x11.defaultCursor = "macOS";
          };
        };
        services.darkman = {
          enable = false;
          lightModeScripts.gtk-theme = ''
            ${pkgs.dconf}/bin/dconf write \
                /org/gnome/desktop/interface/color-scheme "'prefer-light'"
          '';
          darkModeScripts.gtk-theme = ''
            ${pkgs.dconf}/bin/dconf write \
                /org/gnome/desktop/interface/color-scheme "'prefer-dark'"
          '';
          settings = {
            lat = 42.3;
            long = -71.1;
            usegeoclue = false;
            dbusserver = true;
            portal = true;
          };
        };
      };
  };

So, did you read the error? Have you tried providing a value for home-manager.users.user.home.stateVersion?

@NobbZ … it worked.

But why? Why did I need to specify it again if its already in home.nix? Or is there something else, perhaps some sort of mistake or error?

Your mistake is using //, you should never use this in the module system, same with import.

// does not recursively update. Even lib.recursiveUpdate is massively incorrect though, it will not respect order or priority as modules do.

Just write out the config.
If you want to reference another file, add it to imports (unrelated to import).
If you want to merge multiple config attrsets, use lib.mkMerge - well imports handles this for you, but if you want to have e.g. conditionals then it may be cleaner to use lib.mkMerge.

1 Like

Even lib.recursiveUpdate is massively incorrect though, it will not respect order or priority as modules do.

What do you mean “massively incorrect”?

It does not respect module metadata and does not understand module merging rules.

I added it to imports and it works fine, but how would I do it with lib.mkMerge?

If you have 2 files, you do not use mkMerge you use imports, or in case of HM as a system module, maybe home-manager.sharedModules.

For the sake of completeness though, your code from above, shortened a bit, using mkMerge, it is not recommended to use it like this, this is for demonstration only!

home-manager = lib.mkMerge [
  (import ./home.nix { inherit inputs; })
  {
    sharedModules = [ inputs.plasma-manager.homeModules.plasma-manager ];
    users.user = …
  }
];
3 Likes

Anyway the solution was simple, just append the file to imports: imports = [ ./home.nix]; and then specify more options (that don’t conflict).