1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-11-08 19:46:05 +01:00
home-manager/modules/targets/darwin/copyapps.nix
Thiago Kenji Okada fca3fedcdc targets/darwin: do not use sudo to check for App Management
This is writing `.DS_Store` files as root in "~/Applications/Home
Manager Apps/<app>", and causing errors during the `rsync` call since it
will try to delete any files that is not present in the app bundle.

Fix #8067.
2025-10-28 08:54:13 -05:00

143 lines
5.6 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.targets.darwin.copyApps;
in
{
options.targets.darwin.copyApps = {
enable =
lib.mkEnableOption "copying macOS applications to the user environment (works with Spotlight)"
// {
default =
pkgs.stdenv.hostPlatform.isDarwin && (lib.versionAtLeast config.home.stateVersion "25.11");
defaultText = lib.literalExpression ''pkgs.stdenv.hostPlatform.isDarwin && (lib.versionAtLeast config.home.stateVersion "25.11")'';
};
enableChecks = lib.mkEnableOption "enable App Management checks" // {
default = true;
};
directory = lib.mkOption {
type = lib.types.str;
default = "Applications/Home Manager Apps";
description = "Path to link apps relative to the home directory.";
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = !config.targets.darwin.linkApps.enable;
message = "`targets.darwin.copyApps.enable` conflicts with `targets.darwin.linkApps.enable`. Please disable one of them.";
}
(lib.hm.assertions.assertPlatform "targets.darwin.copyApps" pkgs lib.platforms.darwin)
];
home.activation = {
checkAppManagementPermission = lib.mkIf cfg.enableChecks (
lib.hm.dag.entryBefore [ "copyApps" ]
# bash
''
ensureAppManagement() {
for appBundle in '${cfg.directory}/'*.app; do
if [[ -d "$appBundle" ]]; then
if ! run /usr/bin/touch "$appBundle/.DS_Store" &> /dev/null; then
return 1
fi
fi
done
return 0
}
if ! ensureAppManagement; then
if [[ "$(/bin/launchctl managername)" != Aqua ]]; then
# It is possible to grant the App Management permission to `sshd-keygen-wrapper`, however
# there are many pitfalls like requiring the primary user to grant the permission and to
# be logged in when home-manager is run over SSH and it will still fail sometimes...
printf >&2 '\e[1;31merror: permission denied when trying to update apps over SSH, aborting activation\e[0m\n'
printf >&2 'Apps could not be updated as home-manager requires Full Disk Access to work over SSH.\n'
printf >&2 'You can either:\n'
printf >&2 '\n'
printf >&2 ' grant Full Disk Access to all programs run over SSH\n'
printf >&2 '\n'
printf >&2 'or\n'
printf >&2 '\n'
printf >&2 ' run home-manager in a graphical session.\n'
printf >&2 '\n'
printf >&2 'The option "Allow full disk access for remote users" can be found by\n'
printf >&2 'navigating to System Settings > General > Sharing > Remote Login\n'
printf >&2 'and then pressing on the i icon next to the switch.\n'
exit 1
else
# The TCC service required to modify notarised app bundles is `kTCCServiceSystemPolicyAppBundles`
# and we can reset it to ensure the user gets another prompt
run /usr/bin/tccutil reset SystemPolicyAppBundles > /dev/null
if ! ensureAppManagement; then
printf >&2 '\e[1;31merror: permission denied when trying to update apps, aborting activation\e[0m\n'
printf >&2 'home-manager requires permission to update your apps, please accept the notification\n'
printf >&2 'and grant the permission for your terminal emulator in System Settings.\n'
printf >&2 '\n'
printf >&2 'If you did not get a notification, you can navigate to System Settings > Privacy & Security > App Management.\n'
exit 1
fi
fi
fi
''
);
copyApps = lib.hm.dag.entryAfter [ "installPackages" ] (
let
applications = pkgs.buildEnv {
name = "home-manager-applications";
paths = config.home.packages;
pathsToLink = "/Applications";
};
in
# bash
''
targetFolder='${cfg.directory}'
echo "setting up ~/$targetFolder..." >&2
ourLink () {
local link
link=$(readlink "$1")
[ -L "$1" ] && [ "''${link#*-}" = 'home-manager-applications/Applications' ]
}
if [ -e "$targetFolder" ] && ourLink "$targetFolder"; then
run rm "$targetFolder"
fi
run mkdir -p "$targetFolder"
rsyncFlags=(
# mtime is standardized in the nix store, which would leave only file size to distinguish files.
# Thus we need checksums, despite the speed penalty.
--checksum
# Converts all symlinks pointing outside of the copied tree (thus unsafe) into real files and directories.
# This neatly converts all the symlinks pointing to application bundles in the nix store into
# real directories, without breaking any relative symlinks inside of application bundles.
# This is good enough, because the make-symlinks-relative.sh setup hook converts all $out internal
# symlinks to relative ones.
--copy-unsafe-links
--archive
--delete
--chmod=+w
--no-group
--no-owner
)
run ${lib.getExe pkgs.rsync} "''${rsyncFlags[@]}" ${applications}/Applications/ "$targetFolder"
''
);
};
};
}