nix-darwin icon indicating copy to clipboard operation
nix-darwin copied to clipboard

applications are only linked for system packages, not user packages

Open BrianHicks opened this issue 5 years ago • 24 comments

When I do this:

{ users.users.brianhicks.packages = [ pkgs.emacsMacport ]; }

I expect Emacs.app to be available somewhere—maybe in ~/Applications/Nix Apps. But it's not! However, if I do this:

{ environment.systemPackages = [ pkgs.emacsMacport ]; }

Then ~/Applications/Nix Apps/Emacs.app gets created.

This is important to me because I manage my packages through home-manager, where I create a big ol' bundle of emacs packages all together. I'd rather not have to move all that configuration out of home-manager just to use graphical Emacs!

According to @rvolosatovs in https://github.com/rycee/home-manager/pull/702#issuecomment-489431636, it's because these lines specifically (and only) include the system packages:

https://github.com/LnL7/nix-darwin/blob/3ba909eb2dd3ddd893604f83048fa76cc2e47e74/modules/system/applications.nix#L17-L21

BrianHicks avatar May 05 '19 19:05 BrianHicks

@LnL7 I don't have a great idea of how hard this would be to fix. Do you think it would be something someone newish (read: me) could take on? Would a patch for this behavior be welcome?

BrianHicks avatar May 14 '19 12:05 BrianHicks

The main problem is that there's no link between the user running darwin-rebuild and users entries. It's probably better to link per user applications instead and move system packages to /Applications.

LnL7 avatar May 15 '19 18:05 LnL7

I'm having some trouble understanding what you're saying about the running user. Why does this matter? Shouldn't stuff end up in /$USER_HOME/Applications regardless of who is running it?

Regardless, is that a change you're open to? I'm happy to look into it but I would rather not waste everyone's time by making something that would be doomed before I started 😁

BrianHicks avatar May 24 '19 20:05 BrianHicks

OK, I've taken more time to think about it today, and just want to check my understanding. I think we'd need to:

  1. change https://github.com/LnL7/nix-darwin/blob/3ba909eb2dd3ddd893604f83048fa76cc2e47e74/modules/system/applications.nix to symlink into /Applications/Nix Apps instead of ~/Applications or ~/Applications/Nix Apps.
  2. Figure out some way of removing the old symlinks in ~.
  3. add similar app linking logic in https://github.com/LnL7/nix-darwin/blob/3ba909eb2dd3ddd893604f83048fa76cc2e47e74/modules/users/default.nix somewhere. This would put applications in users.<user?>.packages in ~/Applications or ~/Applications/Nix Apps like the system packages are now.

The first seems pretty approachable; I'm sure I could handle it. But I'm not sure if the second or third even make sense. @LnL7, what do you think? Would that be a good way to move forward on this?

(to be clear, if it's "yes" I plan to make PRs to do this!)

BrianHicks avatar Jul 17 '19 13:07 BrianHicks

Yeah that's what I was suggesting.

LnL7 avatar Jul 18 '19 19:07 LnL7

Great, thank you. Sorry for the noise; I wanted to make sure I understood you completely before I sent in a PR. :)

On Jul 18, 2019, at 2:59 PM, Daiderd Jordan [email protected] wrote:

Yeah that's what I was suggesting.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/LnL7/nix-darwin/issues/139?email_source=notifications&email_token=AACWYSLJ54ZKV62VBADHAZTQADDS3A5CNFSM4HK37DL2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2JTTQQ#issuecomment-512965058, or mute the thread https://github.com/notifications/unsubscribe-auth/AACWYSO5ZD7T64654SUZBLTQADDS3ANCNFSM4HK37DLQ.

BrianHicks avatar Jul 18 '19 20:07 BrianHicks

For future reference, you can override the applications link like:

system.build.applications = pkgs.lib.mkForce (pkgs.buildEnv {
  name = "applications";
  paths = config.environment.systemPackages ++ config.home-manager.users.myusername.home.packages;
  pathsToLink = "/Applications";
});

It'd be nice if it was a config option though to avoid the mkForce.

jsravn avatar Jul 23 '20 16:07 jsravn

@jsravn Nice, thanks for sharing that. It's a shame the larger change to place system-wide app links under /Applications and per-user app links under ~/Applications didn't end up getting seen through, because it feels like the ideal outcome.

purcell avatar Jul 23 '20 21:07 purcell

Seems like spotlight struggles to index the links in ~/Applications however. I'm pretty sure it worked for me at one point but it's not working anymore. Any ideas?

jsravn avatar Jul 24 '20 10:07 jsravn

I modified the activation script to create aliases which get picked up by spotlight.

    system.activationScripts.applications.text = pkgs.lib.mkForce (''
      echo "setting up ~/Applications/NixApps..."
      mkdir -p ~/Applications
      rm -rf ~/Applications/NixApps
      mkdir -p ~/Applications/NixApps
      chown myusername ~/Applications/NixApps
      find ${config.system.build.applications}/Applications -maxdepth 1 -type l | while read f; do
        echo "Linking $f"
        src=$(/usr/bin/stat -f%Y $f)
        osascript -e "tell app \"Finder\" to make alias file at POSIX file \"/Users/myusername/Applications/NixApps/\" to POSIX file \"$src\"";
      done
    '');

It can probably be made a bit nicer but it works.

jsravn avatar Jul 24 '20 11:07 jsravn

@jsravn I found that some alias files were ending up with different names over time, e.g. with numeric suffixes. Making the name of the alias file explicit as follows seems to help:

  system.activationScripts.applications.text = pkgs.lib.mkForce (''
      echo "setting up ~/Applications/Nix..."
      rm -rf ~/Applications/Nix
      mkdir -p ~/Applications/Nix
      chown ${me} ~/Applications/Nix
      find ${config.system.build.applications}/Applications -maxdepth 1 -type l | while read f; do
        src="$(/usr/bin/stat -f%Y $f)"
        appname="$(basename $src)"
        osascript -e "tell app \"Finder\" to make alias file at POSIX file \"/Users/${me}/Applications/Nix/\" to POSIX file \"$src\" with properties {name: \"$appname\"}";
    done
  '');

(Note that ${me} is just a local attribute with my username.)

purcell avatar Jul 30 '20 23:07 purcell

Nice one! @LnL7 would you accept a PR with this change?

jsravn avatar Aug 25 '20 10:08 jsravn

Relying on applescript seems pretty undesirable to me, especially for headless usecases. But should be pretty easy to create an alias using CoreFoundation if there's no binary that create one on the system already.

LnL7 avatar Aug 26 '20 17:08 LnL7

Hello. I’ve encountered this issue today. At the moment I’ve applied a fix from above. I’d like to fix it permanently though. I’ll continue Brian’s PR unless you have time to fix it @LnL7. What are your thoughts?

akoppela avatar Oct 27 '20 10:10 akoppela

So on Catalina, I tried the above suggestions of using aliases created through AppleScript instead of symlinks, but Spotlight still couldn't find the apps Nix installed into ~/Applications. I even tried creating the symlinks and aliases in /Applications, but still nothing. So now I'm just copying the entire app directory over! Wasteful but at least it shows up in Spotlight!

  system.activationScripts.applications.text = pkgs.lib.mkForce (''
    rm -rf ~/Applications/Nix\ Apps
    mkdir -p ~/Applications/Nix\ Apps
    for app in $(find ${config.system.build.applications}/Applications -maxdepth 1 -type l); do
      src="$(/usr/bin/stat -f%Y "$app")"
      cp -r "$src" ~/Applications/Nix\ Apps
    done
  '');

andreykaipov avatar Dec 21 '20 08:12 andreykaipov

Copying (or moving?) the entire app to /Applications is actually the approach taken by brew cask. I suspect there's a good reason for that so I do the same as you do in my nix-darwin config.

kaii-zen avatar Dec 21 '20 12:12 kaii-zen

Copying the applications solved the issue for me too. I added the -L flag to follow symlinks too (this helped copy the alacritty app properly, for example).

wangkev avatar Dec 26 '20 19:12 wangkev

So now I'm just copying the entire app directory over!

@andreykaipov @wangkev If you comment out the program to uninstall, and run nix-collect-garbage is the copied app directory will be removed or not?

I don't want leave garbage files/links/directories after uninstalling them.

0ihsan avatar Dec 27 '20 11:12 0ihsan

@ihsanturk If I understand the question correctly - the copied apps aren't removed even when you uninstall Nix because Nix doesn't know what directories you create in your custom activation script, so you don't have to worry about commenting them out in your config before running garbage collection. As part of my uninstall script I have to manually remove the created directory.

Also I've since changed the above script to compare the hashes of <app>/Contents/MacOS to prevent unnecessary copying. It makes for a rather large snippet, but you can view it here.

andreykaipov avatar Dec 28 '20 04:12 andreykaipov

@ihsanturk Yes. the app will be removed. It's in the script.

system.activationScripts.applications.text = pkgs.lib.mkForce (''
  rm -rf ~/Applications/Nix\ Apps
  mkdir -p ~/Applications/Nix\ Apps
  for app in $(find ${config.system.build.applications}/Applications -maxdepth 1 -type l); do
    src="$(/usr/bin/stat -f%Y "$app")"
    cp -r "$src" ~/Applications/Nix\ Apps
  done
'');

The script starts with rm -rf ~/Applications/Nix\ Apps which removes all previously copied apps. Then later in the script it copies new list of apps.

akoppela avatar Dec 29 '20 05:12 akoppela

Thanks! Those were my two wishes:

... compare the hashes of /Contents/MacOS to prevent unnecessary copying.

The script starts with rm -rf ~/Applications/Nix\ Apps which removes all previously copied apps.

Now I will integrate them. I hope these will be solved more generally by Nix itself.

0ihsan avatar Dec 29 '20 07:12 0ihsan

I really don't understand why nix-darwin puts anything in the home directory. These are system packages, shouldn't they go to /Applications? I would assume ~/Applications would be used by home-manager, but apparently it can't (on mac) because of the conflict.

kubukoz avatar Feb 06 '21 17:02 kubukoz

@akoppela Thank you!

↓ FYI I improved your snippet by resolving the symlinks in bulk, and using APFS's copy-on-write.

  system.activationScripts.applications.text = let
    env = pkgs.buildEnv {
      name = "system-applications";
      paths = config.environment.systemPackages;
      pathsToLink = "/Applications";
    };
  in mkForce ''
    # Set up applications.
    echo "setting up ~/Applications..." >&2

    rm -rf ~/Applications/Nix\ Apps
    mkdir -p ~/Applications/Nix\ Apps

    find ${env}/Applications -maxdepth 1 -type l -exec readlink '{}' + |
        while read src; do
          /bin/cp -cr "$src" ~/Applications/Nix\ Apps
        done
  '';

sei40kr avatar Oct 27 '21 18:10 sei40kr

Folders symlinked to ~/Applications do not show up in spotlight, and copying them seems like a waste of disk space. Not to mention that it can take a while too if you have a lot of packages. I found a workaround using APFS aliases, those show up in spotlight and barely take any disk space.

This is my activation script:

  # Nix-darwin does not link installed applications to the user environment. This means apps will not show up
  # in spotlight, and when launched through the dock they come with a terminal window. This is a workaround.
  # Upstream issue: https://github.com/LnL7/nix-darwin/issues/214
  system.activationScripts.applications.text = lib.mkForce ''
    echo "setting up ~/Applications..." >&2
    applications="$HOME/Applications"
    nix_apps="$applications/Nix Apps"

    # Needs to be writable by the user so that home-manager can symlink into it
    if ! test -d "$applications"; then
        mkdir -p "$applications"
        chown ${username}: "$applications"
        chmod u+w "$applications"
    fi

    # Delete the directory to remove old links
    rm -rf "$nix_apps"
    mkdir -p "$nix_apps"
    find ${config.system.build.applications}/Applications -maxdepth 1 -type l -exec readlink '{}' + |
        while read src; do
            # Spotlight does not recognize symlinks, it will ignore directory we link to the applications folder.
            # It does understand MacOS aliases though, a unique filesystem feature. Sadly they cannot be created
            # from bash (as far as I know), so we use the oh-so-great Apple Script instead.
            /usr/bin/osascript -e "
                set fileToAlias to POSIX file \"$src\" 
                set applicationsFolder to POSIX file \"$nix_apps\"
                tell application \"Finder\"
                    make alias file to fileToAlias at applicationsFolder
                    # This renames the alias; 'mpv.app alias' -> 'mpv.app'
                    set name of result to \"$(rev <<< "$src" | cut -d'/' -f1 | rev)\"
                end tell
            " 1>/dev/null
        done
  '';

I have done something similar for home-manager, which integrates with this just fine.

Commit from my dotfiles: https://github.com/IvarWithoutBones/dotfiles/commit/0b3faad8bd1d0e1af6103caf59b206666ab742f4

IvarWithoutBones avatar Aug 29 '22 18:08 IvarWithoutBones