-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mac Application launchers not symlinked on darwin #1341
Comments
I should add that applications are already symlinked into https://github.com/LnL7/nix-darwin/blob/master/modules/system/applications.nix |
Here's what I added to my configuration to make this work EDIT : the example has been further simplified {
# Install MacOS applications to the user environment if the targetPlatform is Darwin
home.file."Applications/home-manager".source = let
apps = pkgs.buildEnv {
name = "home-manager-applications";
paths = config.home.packages;
pathsToLink = "/Applications";
};
in mkIf pkgs.stdenv.targetPlatform.isDarwin "${apps}/Applications";
} |
This task is now handled in my PR #1460 |
I'm seeing a conflict between the above code and
This is because |
You are right. The logic in nix-darwin to symlink applications to if [ ! -e ~/Applications -o -L ~/Applications ]; then
ln -sfn ${cfg.build.applications}/Applications ~/Applications
elif [ ! -e ~/Applications/Nix\ Apps -o -L ~/Applications/Nix\ Apps ]; then
ln -sfn ${cfg.build.applications}/Applications ~/Applications/Nix\ Apps
else
echo "warning: ~/Applications and ~/Applications/Nix Apps are directories, skipping App linking..." >&2
fi AFAIK there are many applications that create folders within Maybe @LnL7 could provide more input? |
On a brand new Mac ~/Applications already existed in my case |
This disables the generation of the application directory until conflicting behavior with nix-darwin is resolved. See #1341 (comment)
I've disabled the feature in the above commit until we've come to an agreement with nix-darwin 🙂 |
This disables the generation of the application directory until conflicting behavior with nix-darwin is resolved. See nix-community#1341 (comment)
I don't see a reason why this should also be disabled for Darwin users who don't use nix-darwin, could you make it an option instead? |
I might have something unusual about my setup, but I've found that symlinks don't get picked up by spotlight, so symlinking the app dir (or the individual apps) don't cause them to show up in my spotlight search (eg cmd-space + "emacs" only shows web results). After some debugging, I found that spotlight ignores symlinks but will index aliases. Aliases are kind of awful to work with - you can't seem to create them from the CLI without pinging finder via AppleScript / osascript. I've added the following to my config which seems to make things work: home.activation = {
aliasApplications = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
app_folder=$(echo ~/Applications);
for app in $(find "$genProfilePath/home-path/Applications" -type l); do
$DRY_RUN_CMD rm -f $app_folder/$(basename $app)
$DRY_RUN_CMD osascript -e "tell app \"Finder\"" -e "make new alias file at POSIX file \"$app_folder\" to POSIX file \"$app\"" -e "set name of result to \"$(basename $app)\"" -e "end tell"
done
'';
}; (in the context of my dot files: https://github.com/nuance/dotfiles/blob/master/nix/environments/macos.nix#L11-L19) Happy to make a PR for this or let someone else use this code if it looks reasonable. |
An alternative solution I have (sort of) working locally is to |
I don't have a Mac any more, but isn't it sufficient to symlink from |
It is not. As @nuance mentioned, Finder ignores symlink for whatever stupid reason. Although you could try aliasing the whole directory instead of the indiviual apps perhaps. |
For what it’s worth, wasn’t able to figure out how to alias a directory
with applescript, but I’m barely proficient at it so it’s possible I’m
missing something simple. Aliasing the folder definitely seems a little
cleaner and deals with removing applications, which my solution currently
does not (the alias will point to whatever previous install exists, which
might cause interesting behavior in the event nix GCs it...).
…On Tue, Jan 19, 2021 at 1:58 PM Atemu ***@***.***> wrote:
It is not. As @nuance <https://github.com/nuance> mentioned, Finder
ignores symlink for whatever stupid reason.
Although you could try aliasing the whole directory instead of the
indiviual apps perhaps.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1341 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAACBDAXV4SEIBOL5CGJ2L3S2XI3VANCNFSM4OCG3S7A>
.
|
I'm guessing that people who're having problems with Spotlight installed Nix using the post-Catalina installer. The newer installer creates the Nix store on a separate filesystem, which prevents files there from being indexed by Spotlight. There are 2 known solutions for this:
However, there are problems with both one way or another. The same issue came up on Homebrew numerous times, and they never found a satisfactory answer. |
With the symlink approach, things should work just as it did before. However, I haven't upgraded from Mojave, so I can't be 100% sure whether it would work on Catalina and later. But the problem is, Spotlight will pick up all applications from the Nix store and not only the ones in the current profile. This means it's very difficult to tell which one to choose when you have multiple profile generations in your Nix store: With the alias approach, you won't have the same problem as long as the Nix store is out of reach of Spotlight. However, the alias won't be categorized under "Applications" and will get pushed far down the list: Comparing the two, the alias approach slightly looks more usable if it turns out to work reliably. There are past reports of the "right click → Open With" functionality not working when using the alias approach, but I haven't encountered that problem so far so it may have been resolved. Looking at Homebrew's past struggles with aliases, the main issue seemed to be about keeping aliases in a healthy state between upgrades. Home Manager might not suffer from this problem because it can delete the |
FWIW, I've been using the If it turns out that doesn't work with many other apps, we could also resort to just copying the .apps. (hard- or reflinks would be cross-device unfortunately.) |
I've just tried the Here are the steps I followed to check:
Copying the apps could be tricky as well because if I understand correctly, apps built in nixpkgs have no guarantee of being relocatable. |
Yeah doing it with MacVim doesn't work for me either. Weird that it does work for Emacs though. |
Currently I copy the applications, and it's working for me so far. home.activation = {
copyApplications = let
apps = pkgs.buildEnv {
name = "home-manager-applications";
paths = config.home.packages;
pathsToLink = "/Applications";
};
in lib.hm.dag.entryAfter [ "writeBoundary" ] ''
baseDir="$HOME/Applications/Home Manager Apps"
if [ -d "$baseDir" ]; then
rm -rf "$baseDir"
fi
mkdir -p "$baseDir"
for appFile in ${apps}/Applications/*; do
target="$baseDir/$(basename "$appFile")"
$DRY_RUN_CMD cp ''${VERBOSE_ARG:+-v} -fHRL "$appFile" "$baseDir"
$DRY_RUN_CMD chmod ''${VERBOSE_ARG:+-v} -R +w "$target"
done
'';
}; I think if home manager were to change to handle this use case, it might add an option for copying targets rather than linking them, but how could it do this safely? It would need to be able to overwrite targets that already exist. |
Like this? I'm not sure how well reverting back to a symlink could work but atm HM just says the dir is in the way of the symlink and needs to be removed first which I think is fine. |
This disables the generation of the application directory until conflicting behavior with nix-darwin is resolved. See nix-community#1341 (comment)
Thank you for your contribution! I marked this issue as stale due to inactivity. If this remains inactive for another 7 days, I will close this issue. Please read the relevant sections below before commenting. If you are the original author of the issue
If you are not the original author of the issue
Memorandum on closing issuesIf you have nothing of substance to add, please refrain from commenting and allow the bot close the issue. Also, don't be afraid to manually close an issue, even if it holds valuable information. Closed issues stay in the system for people to search, read, cross-reference, or even reopen--nothing is lost! Closing obsolete issues is an important way to help maintainers focus their time and effort. |
@Samuel-Martineau As for Firefox, you may use this project which provides current binaries of several editions thereof; you can also <shameless_plug>use my nixcasks</shameless_plug> which provides many Homebrew Casks as Nix derivations, Firefox is among them (I use LibreWolf via nixcasks, and it works, so FF will probably too). |
I have re-implemented the functionailty of the Lisp utility written by @hraban in bash. Not sure if this furthers the possibility of including in home-manager or if licensing issues still preclude this. #!/bin/bash
# Ensure you have these tools installed: jq, plutil, rsync, mktemp, grep, cut, realpath
copyable_app_props=(
"CFBundleDevelopmentRegion"
"CFBundleDocumentTypes"
"CFBundleGetInfoString"
"CFBundleIconFile"
"CFBundleIdentifier"
"CFBundleInfoDictionaryVersion"
"CFBundleName"
"CFBundleShortVersionString"
"CFBundleURLTypes"
"NSAppleEventsUsageDescription"
"NSAppleScriptEnabled"
"NSDesktopFolderUsageDescription"
"NSDocumentsFolderUsageDescription"
"NSDownloadsFolderUsageDescription"
"NSPrincipalClass"
"NSRemovableVolumesUsageDescription"
"NSServices"
"UTExportedTypeDeclarations"
)
dry_run=${DRY_RUN:-false}
debug_sh=${DEBUGSH:-false}
# Function definitions
rootp() {
[[ "$(id -u)" == "0" ]]
}
sh_cmd() {
if $dry_run; then
echo "Dry-run: $*"
else
if $debug_sh; then set -x; fi
"$@"
if $debug_sh; then set +x; fi
fi
}
sync_icons() {
from=$1
to=$2
from_resources="${from}/Contents/Resources/"
to_resources="${to}/Contents/Resources/"
find "$to_resources" -name "*.icns" -delete
rsync --include "*.icns" --exclude "*" --recursive "$from_resources" "$to_resources"
}
copy_paths() {
from=$1
to=$2
paths=("${@:3}")
keys=$(jq -n '$ARGS.positional' --args "${paths[@]}")
jqfilter="to_entries |[.[]| select(.key as \$item| \$keys | index(\$item) >= 0) ] | from_entries"
temp_dir=$(mktemp -d)
trap 'rm -rf "$temp_dir"' EXIT
pushd $temp_dir >/dev/null
cp "$from" "orig"
chmod u+w "orig"
cp "$to" "bare-wrapper"
chmod u+w "bare-wrapper"
plutil -convert json -- "orig"
plutil -convert json -- "bare-wrapper"
jq --argjson keys "$keys" "$jqfilter" <"orig" >"filtered"
cat "bare-wrapper" "filtered" | jq -s add >"final"
plutil -convert xml1 -- "final"
cp "final" "$to"
popd >/dev/null
}
sync_dock() {
# Make sure all environment variables are cleared that might affect dockutil
unset SUDO_USER
# Array of applications to sync
declare -a apps=("$@")
# Iterate through each provided app
for app_path in "${apps[@]}"; do
if [[ -d "$app_path" ]]; then
# Extract the name of the app from the path
app_name=$(basename "$app_path")
app_name=${app_name%.*} # Remove the '.app' extension
resolved_path=$(realpath "$app_path")
# Find the current Dock item for the app, if it exists
current_dock_item=$(dockutil --list --no-restart | grep "/$app_name.app" | awk -F"\t" '{print $1}')
if [[ -n "$current_dock_item" ]]; then
# The app is currently in the Dock, attempt to replace it
echo "Updating $app_name in Dock..."
if $dry_run; then
echo "Dry-run: dockutil --add \"$resolved_path\" --replacing \"$current_dock_item\""
else
dockutil --add "$resolved_path" --replacing "$current_dock_item" --no-restart
fi
else
# The app is not in the Dock; you might choose to add it or do nothing
echo "$app_name is not currently in the Dock."
fi
else
echo "Warning: Provided path '$app_path' is not valid."
fi
done
# Restart the Dock to apply changes
if ! $dry_run; then
killall Dock
else
echo "Dry-run: would now restart Dock."
fi
}
mktrampoline() {
app=$1
trampoline=$2
if [[ ! -d $app ]]; then
echo "app path is not directory."
return 1
fi
cmd="do shell script \"open '$app'\""
sh_cmd /usr/bin/osacompile -o "$trampoline" -e "$cmd"
sync_icons "$app" "$trampoline"
copy_paths "$(realpath "$app/Contents/Info.plist")" "$(realpath "$trampoline/Contents/Info.plist")" "${copyable_app_props[@]}"
}
sync_trampolines() {
[[ ! -d $1 ]] && echo "Source directory does not exist" && return 1
[[ ! -d $2 ]] && mkdir -p "$2"
apps=("$1"/*.app)
for app in "${apps[@]}"; do
trampoline="${2}/$(basename "$app")"
mktrampoline "$app" "$trampoline"
done
sync_dock "${apps[@]}"
}
# Main switch case to process commands
case $1 in
mktrampoline)
mktrampoline "$2" "$3"
;;
sync-trampolines)
sync_trampolines "$2" "$3"
;;
*)
echo "Usage: $0 {mktrampoline|sync-trampolines}"
exit 1
;;
esac |
I've cleaned the script up a bit and added a home-manager module. # Utilities not in nixpkgs.
plutil="/usr/bin/plutil"
killall="/usr/bin/killall"
osacompile="/usr/bin/osacompile"
copyable_app_props=(
"CFBundleDevelopmentRegion"
"CFBundleDocumentTypes"
"CFBundleGetInfoString"
"CFBundleIconFile"
"CFBundleIdentifier"
"CFBundleInfoDictionaryVersion"
"CFBundleName"
"CFBundleShortVersionString"
"CFBundleURLTypes"
"NSAppleEventsUsageDescription"
"NSAppleScriptEnabled"
"NSDesktopFolderUsageDescription"
"NSDocumentsFolderUsageDescription"
"NSDownloadsFolderUsageDescription"
"NSPrincipalClass"
"NSRemovableVolumesUsageDescription"
"NSServices"
"UTExportedTypeDeclarations"
)
function sync_icons() {
local from="$1"
local to="$2"
from_resources="$from/Contents/Resources/"
to_resources="$to/Contents/Resources/"
find "$to_resources" -name "*.icns" -delete
rsync --include "*.icns" --exclude "*" --recursive "$from_resources" "$to_resources"
}
function copy_paths() {
local from="$1"
local to="$2"
local paths=("${@:3}")
keys=$(jq -n '$ARGS.positional' --args "${paths[@]}")
jqfilter="to_entries |[.[]| select(.key as \$item| \$keys | index(\$item) >= 0) ] | from_entries"
temp_dir=$(mktemp -d)
trap 'rm -rf "$temp_dir"' EXIT
pushd $temp_dir >/dev/null
cp "$from" "orig"
chmod u+w "orig"
cp "$to" "bare-wrapper"
chmod u+w "bare-wrapper"
$plutil -convert json -- "orig"
$plutil -convert json -- "bare-wrapper"
jq --argjson keys "$keys" "$jqfilter" <"orig" >"filtered"
cat "bare-wrapper" "filtered" | jq -s add >"final"
$plutil -convert xml1 -- "final"
cp "final" "$to"
popd >/dev/null
}
function sync_dock() {
# Make sure all environment variables are cleared that might affect dockutil
unset SUDO_USER
# Array of applications to sync
declare -a apps=("$@")
# Iterate through each provided app
for app_path in "${apps[@]}"; do
if [ -d "$app_path" ]; then
# Extract the name of the app from the path
app_name=$(basename "$app_path")
app_name=${app_name%.*} # Remove the '.app' extension
resolved_path=$(realpath "$app_path")
# Find the current Dock item for the app, if it exists
current_dock_item=$(dockutil --list --no-restart | grep "$app_name.app" | awk -F "\t" '{print $1}' || echo "")
if [ -n "$current_dock_item" ]; then
# The app is currently in the Dock, attempt to replace it
echo "Updating $app_name in Dock..."
dockutil --add "$resolved_path" --replacing "$current_dock_item" --no-restart
else
# The app is not in the Dock; you might choose to add it or do nothing
echo "$app_name is not currently in the Dock."
fi
else
echo "Warning: Provided path $app_path is not valid."
fi
done
# Restart the Dock to apply changes
$killall Dock
}
function mktrampoline() {
local app="$1"
local trampoline="$2"
if [[ ! -d $app ]]; then
echo "app path is not directory."
return 1
fi
cmd="do shell script \"open '$app'\""
$osacompile -o "$trampoline" -e "$cmd"
sync_icons "$app" "$trampoline"
copy_paths "$(realpath "$app/Contents/Info.plist")" "$(realpath "$trampoline/Contents/Info.plist")" "${copyable_app_props[@]}"
}
function sync_trampolines() {
[[ ! -d "$1" ]] && echo "Source directory does not exist" && return 1
if [[ -d "$2" ]]; then
rm -rf "$2"
fi
mkdir -p "$2"
apps=("$1"/*.app)
for app in "${apps[@]}"; do
trampoline="$2/$(basename "$app")"
mktrampoline "$app" "$trampoline"
done
sync_dock "${apps[@]}"
} { config, lib, pkgs, ... }:
with lib;
{
config = mkIf pkgs.stdenv.hostPlatform.isDarwin {
# Install MacOS applications to the user Applications folder. Also update Docked applications
home.extraActivationPath = with pkgs; [
rsync
dockutil
gawk
];
home.activation.trampolineApps = hm.dag.entryAfter [ "writeBoundary" ] ''
${builtins.readFile ./lib-bash/trampoline-apps.sh}
fromDir="$HOME/Applications/Home Manager Apps"
toDir="$HOME/Applications/Home Manager Trampolines"
sync_trampolines "$fromDir" "$toDir"
'';
};
} |
…ically enable launching apps from spotlight From: nix-community/home-manager#1341 (comment)
@Atemu what was Anki complaining about when aliasing the Contents folder? I set up an activation script to do this just now; it seems far simpler than creating trampoline apps, and Anki, Firefox and Emacs all seem to be working great for me. Edit: while the apps do run, code signing breaks. I'm happy with this tradeoff on my system, but it might not be valid for everyone. |
I'm using the aliasing approach and needed to make a small change to get apps with spaces in their names working properly. I update the IFS to not care about spaces.
|
Better version with mkalias:
|
@pshirshov I had some problems with your script with VS Code (because of the spaces in the "file name") I updated it to this:
|
Anyway, linking is broken. It's better to just make copies: LnL7/nix-darwin#214 (comment) |
Copying has been discussed upthread and it's less reliable in the wild than creating a link of some kind (whether symlink, trapoline, alias, ... is up for debate). There isn't a clear winner so far. |
Is this still actually an issue? I haven't done anything specific in my config to link application files, for |
Spotlight and Launchpad (usually) don't pick up on apps inside a symlink inside the application directory. Support for Spotlight is nice when you want keyboard-only navigation without any external tools, just |
Right - but is that something HM can actually fix, or is that a spotlight issue? |
There are reasonable workarounds. |
That's what this thread is about :) welcome to the jungle. |
From my experience your solution has been working fine @hraban . Ideally yeah just to keep a single source of truth you'd want a mount or some kind of symlink, etc. but trampolining it feels pretty unobtrusive as it stands. I haven't noticed any weird stuff. |
Interesting... So for example, Raycast can find the symlinks that Home Manager and nix-darwin create by default. If I understand correctly, Spotlight normally can't see those, and requires extra trampolines? With @hraban's solution for aliasing, will Spotlight find those trampolines? |
Symlinks don't work from my experience. Aliases are the way to go. From Apples point of view, I would assume, that this is not a bug but a feature. Would be great if there would be a possibility in HM to fix this. |
Someone pointed me at this - sharing in case it helps someone |
I have found that the following is required for me. A couple of changes from the most recent above:
# Need to create aliases because Launchbar doesn't look through symlinks.
home.activation.link-apps = lib.hm.dag.entryAfter [ "linkGeneration" ] ''
new_nix_apps="${config.home.homeDirectory}/Applications/Nix"
rm -rf "$new_nix_apps"
mkdir -p "$new_nix_apps"
find -H -L "$genProfilePath/home-files/Applications" -name "*.app" -type d -print | while read -r app; do
real_app=$(readlink -f "$app")
app_name=$(basename "$app")
target_app="$new_nix_apps/$app_name"
echo "Alias '$real_app' to '$target_app'"
${pkgs.mkalias}/bin/mkalias "$real_app" "$target_app"
done
''; |
I have just discovered that you should replace |
I've tried @AntonyBlakey's solution. Using The minor issue I had is that it may also pick up and link
Specifying the |
Issue description
I noticed that when I install an application on macOS that includes a GUI launcher (emacs in my case) using nix-darwin,
Emacs.app
is symlinked into~/Applications/Nix Apps
so that things like Spotlight can find and launch Emacs without requiring me to have a dedicated terminal open to start emacs.I think home-manager should adopt this behaviour and symlink its apps into
~/Applications/Home Manager Apps
or something like that to greatly improve the experience of launching GUI apps on macOS/darwin.Meta
Not sure what other useful information to add, this is more of a feature request than a bug.
The text was updated successfully, but these errors were encountered: