Advice needed installing doom-emacs

I have recently trying to use NixOS on the desktop, so I am still migrating my dotfiles to NixOS and home-manager. Everything is going quite ok for now, but I have some doubts about how to install doom-emacs. Following the install instructions, I should clone the repository to $HOME/.emacs.d. I guess that there is nothing inherently wrong in just running git clone and call it a day (it would work), but I would like to integrate it in my configuration so I don’t have to worry about it next time I have to install it. I have tried searching for better ways to do it, but it seems like it is out of the scope of the NixOS tools (since it happens in $HOME) and I am not sure if there is a way to do it using home-manager.

To more experienced users (or people already using doom-emacs): how would you proceed? Any pointers and ideas are welcome!

1 Like

If you really want to install it declaratively there is GitHub - vlaci/nix-doom-emacs: doom-emacs packaged for Nix

2 Likes

Why did you italicize “really” - is there a question as to the wisdom of doing it?

TIA!

Doom already has a declarative package manager (straight.el), so most people (including Henrik himself) don’t manage doom through nix.

4 Likes

Ok, I will just leave this answer here in case anyone is trying something similar. The essence of it can be summed up in @mjlbach’s last comment:

[…] most people (including Henrik himself) don’t manage doom through nix.

That is not only because it is in a certain way redundant to use Nix and straight.el; it is because doom-emacs needs to be cloned into ~/.emacs.d and that directory must be writable. I could not find a good way to do it using Nix, so in a quick way to bootstrap it in case I install this config in a new machine, I decided to set up my ~/.doom.d directory through home-manager this way:

home.file.".doom.d" = {
  source = path/to/my/git/repo;
  recursive = true;
  onChange = readFile path/to/reload;
};

The notable thing is the onChange option being set to a script I made to reload the configuration. Initially, this script was simply a call to doom sync, but I added a simple check to detect if there was no configuration. If not, the script clones the repository and installs everything:

#!/bin/sh
DOOM="$HOME/.emacs.d"

if [ ! -d "$DOOM" ]; then
	git clone https://github.com/hlissner/doom-emacs.git $DOOM
	$DOOM/bin/doom -y install
fi

$DOOM/bin/doom sync

It works but I still want to polish some round edges:

  • When creating a new generation, the output from this onChange script is not shown to the user, and if it has to install emacs-doom it takes a long time with no feedback.
  • The current code simply clones the repository; I should try to pin the version/revision somehow to ensure that the install is the same.
3 Likes

I am grateful, this is helpful!

A couple of questions:

  • Did you figure out how to pin versions of the doom?
  • How would I upgrade doom with this setup?
    Seems like for now deleting ~/.emacs.d would trigger new install and that would pull in upgrads, is that correct?
  • Do you have your dotfiles available anywhere for the reference?

Did you figure out how to pin versions of the doom?

I did not, but I guess that it should suffice with checking out a certain commit of the emacs-doom repo in the example script I showed before.

How would I upgrade doom with this setup? Seems like for now deleting ~/.emacs.d would trigger new install and that would pull in upgrads, is that correct?

You would also need to change the ~/.doom.d directory in some way - any change will do, just enough to trigger a new onChange event. But I guess that would be enough, yes.

Do you have your dotfiles available anywhere for the reference?

I do, in fact. I hope they are not too convoluted and they can be helpful.

1 Like

Doom’s Emacs directory doesn’t strictly have to be writable. You can set the DOOMLOCALDIR to a writable location instead, that is the only part that has to be writable and it can be in an arbitrary location.

Also, once the Emacs directory has been updated via git, running doom sync -u is the same as doom upgrade.

I’ve got the following in my home.nix (irrelevant bits elided):

{ pkgs, config, lib, ... }:

{
  home = {
    sessionPath = [ "${config.xdg.configHome}/emacs/bin" ];
    sessionVariables = {
      DOOMDIR = "${config.xdg.configHome}/doom-config";
      DOOMLOCALDIR = "${config.xdg.configHome}/doom-local";
    };
  };

  xdg = {
    enable = true;
    configFile = {
      "doom-config/config.el".text = "…";
      "doom-config/init.el".text = "…";
      "doom-config/packages.el".text = "…";
      "emacs" = {
        source = builtins.fetchGit "https://github.com/hlissner/doom-emacs";
        onChange = "${pkgs.writeShellScript "doom-change" ''
          export DOOMDIR="${config.home.sessionVariables.DOOMDIR}"
          export DOOMLOCALDIR="${config.home.sessionVariables.DOOMLOCALDIR}"
          if [ ! -d "$DOOMLOCALDIR" ]; then
            ${config.xdg.configHome}/emacs/bin/doom -y install
          else
            ${config.xdg.configHome}/emacs/bin/doom -y sync -u
          fi
        ''}";
      };
    };
  };

  home.packages = with pkgs; [
    # DOOM Emacs dependencies
    binutils
    (ripgrep.override { withPCRE2 = true; })
    gnutls
    fd
    imagemagick
    zstd
    nodePackages.javascript-typescript-langserver
    sqlite
    editorconfig-core-c
    emacs-all-the-icons-fonts
  ];
}
5 Likes

I know this is an old post but I’m pretty sure I copied your home.nix correctly and while all of the files are being place where they should be, I’m not seeing that onChange script get run on updates (nixos-rebuild --switch).

Any advice?

In modern versions of Home Manager the onChange script needs everything it call in its PATH, else the activation script will fail. Doom will need git, for example. You can have a look at my current home.nix to see what I mean:

However, the explicit setting of PATH in those scripts is misleading… I actually need to remove those :smile:, the important bit is actually much earlier. Ignore that, there definitely needs to be an emacs in the PATH in order for the doom utility to work. I wrap Emacs with all Doom’s dependencies’ bin folders added to its PATH: home.nix#L24-L114.

My setup is currently a bit unwieldy to read, I’m afraid. I’ve yet to split everything into neat little modules. I’m also using Chemacs, which adds a bit of indirection, but don’t pay it much mind. This setup has worked for me for years.

Keep in mind that unless something actually changed in your init.el or package.el, i.e. you manually changed some of its code or an interpolated store path changed (which can happen after a nixpkgs update), nothing will have changed, so Nix/Home Manager will have no reason to run the onChange script.

Hope that helps, and feel free to ask me if it’s still unclear.

1 Like

Reading more closely… Unless you’ve installed Home Manager as a NixOS module, and are importing home.nix into /etc/nixos/configuration.nix, you’ll need to run home-manager switch to action changes in home.nix.

nixos-rebuild, by default, doesn’t know anything about Home Manager and won’t handle home.nix unless you use the NixOS module version of Home Manager. I actually just set that up for myself: feat: use home-manager’s NixOS module.

1 Like

I want to say that my setup of home manager is working fine because pasting in your old configuration did yield the two doom folders in my .config. I’ll post my config a bit later and also double check.

This is such an excellent response. I’ll make this my project for tonight and report back.

1 Like

You were right to suggest that by the way. It was complaining about emacs not being in my path and re-running it would be successful (because then there’d be no change detected).

Dec 15 20:18:09 nixos hm-activate-yoni[205986]: Activating onFilesChange
Dec 15 20:18:09 nixos hm-activate-yoni[206080]: Error: failed to run Emacs with command ‘emacs’
Dec 15 20:18:09 nixos hm-activate-yoni[206080]: Are you sure Emacs is installed and in your $PATH?

1 Like

There’s more I need to borrow from your config but I think I got it working.

It installed everything, finished in 4m27s and I think the install script just sat there so that the rebuild command timed out…but it did everything it was supposed to. Here’s a small excerpt of my config if you’re interested: extract of my home-manager configuration · GitHub

I know I added more paths than I needed, I’ll have to clean that up.

PS: I never understood why we were cloning doom emacs to ~/.emacs.d and I see you modified that in your config. I’m gonna steal that too.

/edit and now I see why you declare each individual file…because right now my config only updates when the repo updates rather than when I change the configuration.

/edit2

For anyone else that might stumble upon this in the future, the other big thing to do is set the DOOMPROFILELOADFILE environment variable to a writable location. Me, personally, I set it to ~/.config/doom-local/load.el during the onChange trigger as well as in home.sessionVariables.

This works unless you’re already using a profiles.el in which case it won’t try to generate a load.el file.

The only things left to sort out are the strange timeout issue, which I can recover from by running doom sync and (process:20426): GLib-GIO-CRITICAL **: 02:10:16.839: g_settings_schema_source_lookup: assertion 'source != NULL' failed this warning here. Sometimes too it’ll timeout midway during a git fetch and I’ll have to delete that repo’s folder and doom sync will fix it.

1 Like