diff --git a/modules/system/activation-scripts.nix b/modules/system/activation-scripts.nix index a5cdc24d..9ab1b7b7 100644 --- a/modules/system/activation-scripts.nix +++ b/modules/system/activation-scripts.nix @@ -62,6 +62,23 @@ in config = { + assertions = + map + (userActivationOption: { + assertion = !config.system.activationScripts ? ${userActivationOption}; + message = '' + The `system.activationScripts.${userActivationOption}` option has + been removed, as all activation now takes place as `root`. Please + restructure your custom activation scripts appropriately, + potentially using `sudo` if you need to run commands as a user. + ''; + }) + [ + "extraUserActivation" + "preUserActivation" + "postUserActivation" + ]; + system.activationScripts.script.text = '' #! ${stdenv.shell} set -e @@ -77,9 +94,9 @@ in ${cfg.activationScripts.preActivation.text} - # We run `etcChecks` again just in case someone runs `activate` - # directly without `activate-user`. ${cfg.activationScripts.etcChecks.text} + ${cfg.activationScripts.createRun.text} + ${cfg.activationScripts.checks.text} ${cfg.activationScripts.extraActivation.text} ${cfg.activationScripts.groups.text} ${cfg.activationScripts.users.text} @@ -114,45 +131,11 @@ in fi ''; - # FIXME: activationScripts.checks should be system level - system.activationScripts.userScript.text = '' - #! ${stdenv.shell} - set -e - set -o pipefail - - PATH="${activationPath}" - export PATH - - systemConfig=@out@ - - _status=0 - trap "_status=1" ERR - - # Ensure a consistent umask. - umask 0022 - - ${cfg.activationScripts.preUserActivation.text} - - # This should be running at the system level, but as user activation runs first - # we run it here with sudo - ${cfg.activationScripts.createRun.text} - ${cfg.activationScripts.checks.text} - ${cfg.activationScripts.etcChecks.text} - ${cfg.activationScripts.extraUserActivation.text} - - ${cfg.activationScripts.postUserActivation.text} - - exit $_status - ''; - # Extra activation scripts, that can be customized by users # don't use this unless you know what you are doing. system.activationScripts.extraActivation.text = mkDefault ""; system.activationScripts.preActivation.text = mkDefault ""; system.activationScripts.postActivation.text = mkDefault ""; - system.activationScripts.extraUserActivation.text = mkDefault ""; - system.activationScripts.preUserActivation.text = mkDefault ""; - system.activationScripts.postUserActivation.text = mkDefault ""; }; } diff --git a/modules/system/base.nix b/modules/system/base.nix index 40c3699b..1bdef63f 100644 --- a/modules/system/base.nix +++ b/modules/system/base.nix @@ -4,26 +4,26 @@ system.activationScripts.createRun.text = '' if [[ $(stat -c '%a' /etc/synthetic.conf) != "644" ]]; then echo "fixing permissions on /etc/synthetic.conf..." - sudo chmod 644 /etc/synthetic.conf + chmod 644 /etc/synthetic.conf fi if [[ $(grep -c '^run\b' /etc/synthetic.conf) -gt 1 ]]; then echo "found duplicate run entries in /etc/synthetic.conf, removing..." - sudo sed -i "" -e '/^run\tprivate\/var\/run$/d' /etc/synthetic.conf + sed -i "" -e '/^run\tprivate\/var\/run$/d' /etc/synthetic.conf fi if ! grep -q '^run\b' /etc/synthetic.conf 2>/dev/null; then echo "setting up /run via /etc/synthetic.conf..." - printf 'run\tprivate/var/run\n' | sudo tee -a /etc/synthetic.conf >/dev/null + printf 'run\tprivate/var/run\n' | tee -a /etc/synthetic.conf >/dev/null fi - sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true + /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true if [[ ! -L /run ]]; then printf >&2 'error: apfs.util failed to symlink /run, aborting activation\n' printf >&2 'To create a symlink from /run to /var/run, please run:\n' printf >&2 '\n' - printf >&2 "$ printf 'run\tprivate/var/run\n' | sudo tee -a /etc/synthetic.conf\n" + printf >&2 "$ printf 'run\tprivate/var/run\n' | tee -a /etc/synthetic.conf\n" printf >&2 '$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t\n' printf >&2 '\n' printf >&2 'The current contents of /etc/synthetic.conf is:\n' diff --git a/modules/system/default.nix b/modules/system/default.nix index 8351dcc6..29a477eb 100644 --- a/modules/system/default.nix +++ b/modules/system/default.nix @@ -95,7 +95,51 @@ in nativeBuildInputs = [ pkgs.shellcheck ]; activationScript = cfg.activationScripts.script.text; - activationUserScript = cfg.activationScripts.userScript.text; + + # This is for compatibility with older `darwin-rebuild`s and + # third‐party deployment tools. + # + # TODO: Remove this in 25.11. + activationUserScript = '' + #! ${pkgs.stdenv.shell} + # nix-darwin: deprecated + + # Hack to handle upgrades. + if + [[ -e /run/current-system/activate-user ]] \ + && ! grep -q '^# nix-darwin: deprecated$' \ + /run/current-system/activate-user + then + exit + fi + + printf >&2 '\e[1;31mwarning: `activate-user` is deprecated and will be removed in 25.11\e[0m\n' + printf >&2 'This is usually due to the use of a non‐standard activation/deployment\n' + printf >&2 'tool. If you maintain one of these tools, our advice is:\n' + printf >&2 '\n' + printf >&2 ' You can identify a post‐user‐activation configuration by the absence\n' + printf >&2 ' of `activate-user` or the second line of the script being\n' + printf >&2 ' `# nix-darwin: deprecated`.\n' + printf >&2 '\n' + printf >&2 ' We recommend running `$systemConfig/sw/bin/darwin-rebuild activate`\n' + printf >&2 ' to activate built configurations; for a pre‐user‐activation\n' + printf >&2 ' configuration this should be run as a normal user, and for a\n' + printf >&2 ' post‐user‐activation configuration it should be run as `root`.\n' + printf >&2 '\n' + printf >&2 ' If you can’t or don’t want to use `darwin-rebuild activate`, then you\n' + printf >&2 ' should skip running `activate-user` for post‐user‐activation\n' + printf >&2 ' configurations and continue running `activate` as `root`.\n' + printf >&2 '\n' + printf >&2 ' In 25.11, `darwin-rebuild` will stop running `activate-user` and this\n' + printf >&2 ' transition script will be deleted; you should be able to safely\n' + printf >&2 ' remove all related logic by then.\n' + printf >&2 '\n' + printf >&2 'Otherwise, you should report this to the deployment tool developers. If\n' + printf >&2 'you don’t use a third‐party deployment tool, please open a bug report\n' + printf >&2 'at and include as much\n' + printf >&2 'detail about your setup as possible.\n' + ''; + inherit (cfg) darwinLabel; darwinVersionJson = (pkgs.formats.json {}).generate "darwin-version.json" ( @@ -131,7 +175,6 @@ in unset activationScript echo "$activationUserScript" > $out/activate-user - substituteInPlace $out/activate-user --subst-var out chmod u+x $out/activate-user unset activationUserScript diff --git a/modules/users/default.nix b/modules/users/default.nix index 25e87d10..1926983b 100644 --- a/modules/users/default.nix +++ b/modules/users/default.nix @@ -155,7 +155,7 @@ in homeDirectory=$(dscl . -read /Users/nobody NFSHomeDirectory) homeDirectory=''${homeDirectory#NFSHomeDirectory: } - if ! sudo dscl . -change /Users/nobody NFSHomeDirectory "$homeDirectory" "$homeDirectory" &> /dev/null; then + if ! dscl . -change /Users/nobody NFSHomeDirectory "$homeDirectory" "$homeDirectory" &> /dev/null; then if [[ "$(launchctl managername)" != Aqua ]]; then printf >&2 '\e[1;31merror: users cannot be %s over SSH without Full Disk Access, aborting activation\e[0m\n' "$2" printf >&2 'The user %s could not be %s as `darwin-rebuild` was not executed with Full Disk Access over SSH.\n' "$1" "$2" @@ -176,7 +176,7 @@ in # and we can reset it to ensure the user gets another prompt tccutil reset SystemPolicySysAdminFiles > /dev/null - if ! sudo dscl . -change /Users/nobody NFSHomeDirectory "$homeDirectory" "$homeDirectory" &> /dev/null; then + if ! dscl . -change /Users/nobody NFSHomeDirectory "$homeDirectory" "$homeDirectory" &> /dev/null; then printf >&2 '\e[1;31merror: permission denied when trying to %s user %s, aborting activation\e[0m\n' "$2" "$1" printf >&2 '`darwin-rebuild` requires permissions to administrate your computer,\n' printf >&2 'please accept the dialog that pops up.\n' diff --git a/pkgs/darwin-uninstaller/configuration.nix b/pkgs/darwin-uninstaller/configuration.nix index ce6be6ca..391f9a1e 100644 --- a/pkgs/darwin-uninstaller/configuration.nix +++ b/pkgs/darwin-uninstaller/configuration.nix @@ -1,4 +1,4 @@ -{ lib, pkgs, ... }: +{ lib, config, pkgs, ... }: with lib; @@ -15,13 +15,17 @@ with lib; # Restore any unmanaged `nix-daemon`. nix.enable = false; - system.activationScripts.postUserActivation.text = mkAfter '' - nix-channel --remove darwin || true - ''; - system.activationScripts.postActivation.text = mkAfter '' nix-channel --remove darwin || true + ${lib.optionalString (config.system.primaryUser != null) '' + sudo \ + --user=${lib.escapeShellArg config.system.primaryUser} \ + --set-home \ + -- nix-channel --remove darwin \ + || true + ''} + if [[ -L /Applications/Nix\ Apps ]]; then rm /Applications/Nix\ Apps fi diff --git a/pkgs/nix-tools/darwin-rebuild.sh b/pkgs/nix-tools/darwin-rebuild.sh index 8f207a7a..14c56e56 100644 --- a/pkgs/nix-tools/darwin-rebuild.sh +++ b/pkgs/nix-tools/darwin-rebuild.sh @@ -190,6 +190,7 @@ if [ "$action" = switch ] || [ "$action" = build ] || [ "$action" = check ] || [ -- "$flake#$flakeAttr.system" \ | jq -r '.[0].outputs.out') fi + fi if [ "$action" = list ] || [ "$action" = rollback ]; then @@ -210,6 +211,17 @@ fi if [ -z "$systemConfig" ]; then exit 0; fi +# TODO: Remove this backwards‐compatibility hack in 25.11. + +if + [[ -x $systemConfig/activate-user ]] \ + && ! grep -q '^# nix-darwin: deprecated$' "$systemConfig/activate-user" +then + hasActivateUser=1 +else + hasActivateUser= +fi + if [ "$action" = switch ]; then if [ "$USER" != root ] && [ ! -w $(dirname "$profile") ]; then sudo nix-env -p "$profile" --set "$systemConfig" @@ -219,7 +231,9 @@ if [ "$action" = switch ]; then fi if [ "$action" = switch ] || [ "$action" = activate ] || [ "$action" = rollback ]; then - "$systemConfig/activate-user" + if [[ -n $hasActivateUser ]]; then + "$systemConfig/activate-user" + fi if [ "$USER" != root ]; then sudo "$systemConfig/activate" @@ -234,5 +248,13 @@ fi if [ "$action" = check ]; then export checkActivation=1 - "$systemConfig/activate-user" + if [[ -n $hasActivateUser ]]; then + "$systemConfig/activate-user" + else + if [ "$USER" != root ]; then + sudo "$systemConfig/activate" + else + "$systemConfig/activate" + fi + fi fi diff --git a/tests/activation-scripts.nix b/tests/activation-scripts.nix index e7d08569..70c4245d 100644 --- a/tests/activation-scripts.nix +++ b/tests/activation-scripts.nix @@ -1,10 +1,6 @@ { config, pkgs, ... }: { - system.activationScripts.preUserActivation.text = "echo hook preUserActivation"; - system.activationScripts.extraUserActivation.text = "echo hook extraUserActivation"; - system.activationScripts.postUserActivation.text = "echo hook postUserActivation"; - system.activationScripts.preActivation.text = "echo hook preActivation"; system.activationScripts.extraActivation.text = "echo hook extraActivation"; system.activationScripts.postActivation.text = "echo hook postActivation"; @@ -14,11 +10,6 @@ awk '/echo hook / {i++ ; print i " => " $0}' "$2" | grep "$1" } - echo checking activation hooks in /activate-user >&2 - countHooks "1 => echo hook preUserActivation" ${config.out}/activate-user - countHooks "2 => echo hook extraUserActivation" ${config.out}/activate-user - countHooks "3 => echo hook postUserActivation" ${config.out}/activate-user - echo checking activation hooks in /activate >&2 countHooks "1 => echo hook preActivation" ${config.out}/activate countHooks "2 => echo hook extraActivation" ${config.out}/activate