mirror of
https://github.com/nix-community/home-manager.git
synced 2025-11-08 19:46:05 +01:00
targets/darwin: init copyapps
This is basically a better version of `targets.darwin.linkApps` that copy apps instead of linking (hence the name). While this is a convoluted approach, it works with Spotlight, where the previous approach doesn't. This is also the approach adopted by nix-darwin, see PR: https://github.com/nix-darwin/nix-darwin/pull/1396. There are a few particularities about this implementation, for one the flags we use in rsync are different since we are not using root to copy the apps to `~/Applications`. This may or may not cause some issues with specific applications so further testing will be needed. Also the check for App Management permission needs root (via sudo), so this check is gated behind a flag that can be disabled if needed. Fix: #1341.
This commit is contained in:
parent
44ca573665
commit
4b846fa3aa
3 changed files with 158 additions and 2 deletions
145
modules/targets/darwin/copyapps.nix
Normal file
145
modules/targets/darwin/copyapps.nix
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
{
|
||||||
|
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 (needs sudo; may ask sudo twice with nix-darwin)"
|
||||||
|
// {
|
||||||
|
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 = "This modules conflicts with `targets.darwin.linkApps`.";
|
||||||
|
}
|
||||||
|
(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/sudo /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 `darwin-rebuild` 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 `darwin-rebuild` 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 `darwin-rebuild` 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 '`darwin-rebuild` 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"
|
||||||
|
''
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
{ lib, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
{
|
{
|
||||||
meta.maintainers = with lib.maintainers; [ midchildan ];
|
meta.maintainers = with lib.maintainers; [ midchildan ];
|
||||||
|
|
@ -7,6 +12,7 @@
|
||||||
./user-defaults
|
./user-defaults
|
||||||
./fonts.nix
|
./fonts.nix
|
||||||
./keybindings.nix
|
./keybindings.nix
|
||||||
|
./copyapps.nix
|
||||||
./linkapps.nix
|
./linkapps.nix
|
||||||
./search.nix
|
./search.nix
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ in
|
||||||
{
|
{
|
||||||
options.targets.darwin.linkApps = {
|
options.targets.darwin.linkApps = {
|
||||||
enable = lib.mkEnableOption "linking macOS applications to the user environment" // {
|
enable = lib.mkEnableOption "linking macOS applications to the user environment" // {
|
||||||
default = pkgs.stdenv.hostPlatform.isDarwin;
|
default = pkgs.stdenv.hostPlatform.isDarwin && (lib.versionOlder config.home.stateVersion "25.11");
|
||||||
|
defaultText = lib.literalExpression ''pkgs.stdenv.hostPlatform.isDarwin && (lib.versionOlder config.home.stateVersion "25.11")'';
|
||||||
};
|
};
|
||||||
|
|
||||||
directory = lib.mkOption {
|
directory = lib.mkOption {
|
||||||
|
|
@ -23,6 +24,10 @@ in
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
assertions = [
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !config.targets.darwin.copyApps.enable;
|
||||||
|
message = "This modules conflicts with `targets.darwin.copyApps`.";
|
||||||
|
}
|
||||||
(lib.hm.assertions.assertPlatform "targets.darwin.linkApps" pkgs lib.platforms.darwin)
|
(lib.hm.assertions.assertPlatform "targets.darwin.linkApps" pkgs lib.platforms.darwin)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue