├── README.md └── macOSUpgradeHooks.sh /README.md: -------------------------------------------------------------------------------- 1 | # macOSUpgradeHooks 2 | 3 | Did you know that you can have macOS run a script whenever you update or upgrade the system? Well... you can't. But you can if you adopt "Upgrade Hooks" for macOS. An upgrade hook tells macOS to execute a script when an OS update or upgrade has started, completed, or migrated. 4 | 5 | ### With an upgrade hook: 6 | 7 | - The upgrade hook will be run as root 8 | - Executes when the following events occur: 9 | - Staged upgrade (OS upgrade/update is staged and pending restart) 10 | - Upgrade completion (OS update/upgrade has finished and the loginwindow is loaded) 11 | - templateMigrator completed (automated user login in after the update/upgrade is complete) 12 | - Upgrade actions will not wait until the hook has completely executed 13 | 14 | 15 | ### How to setup your upgrade hooks 16 | 17 | Input your script code info the following functions that you would like to be called during each event. 18 | 19 | The customizable events are `PreUpgrade`, `PostUpgrade`, and `MigrationComplete` 20 | 21 | ### How to install macOS upgrade hooks 22 | 23 | `sudo /bin/bash /path/to/macOSUpgradeHooks.sh` 24 | 25 | ### How to uninstall 26 | 27 | `sudo /private/var/db/.AppleUpgradeHooks.sh "uninstall"` 28 | 29 | ##### Notes 30 | Tested on both major (10.14.x - 10.15.x) and minor (10.15 - 10.15.x) macOS release updates 31 | -------------------------------------------------------------------------------- /macOSUpgradeHooks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # macOSUpgradeHooks 3 | # Description: Execute a script when an OS update or upgrade has started, completed, or migrated 4 | # Source: https://github.com/kennyb-222/macOSUpgradeHooks/ 5 | # Author: Kenny Botelho 6 | 7 | # Upgrade Hooks 8 | PreUpgrade() { 9 | #!/bin/bash 10 | ## preupgrade 11 | 12 | # add commands here or call some script(s) 13 | /bin/bash /path/to/PreUpgrade.sh 14 | 15 | return 0 16 | } 17 | 18 | PostUpgrade() { 19 | #!/bin/bash 20 | ## postupgrade 21 | 22 | # add commands here or call some script(s) 23 | /bin/bash /path/to/PostUpgrade.sh 24 | 25 | return 0 26 | } 27 | 28 | MigrationComplete() { 29 | #!/bin/bash 30 | ## migrationcomplete 31 | 32 | # add commands here or call some script(s) 33 | /bin/bash /path/to/MigrationComplete.sh 34 | 35 | return 0 36 | } 37 | 38 | ##################################### 39 | # DO NOT MODIFY BELOW THIS LINE # 40 | ##################################### 41 | 42 | Install() { 43 | # Copy script to destination path 44 | cp "$0" ${ScriptPath} 45 | 46 | # Create LaunchDaemon to watch for "Stagged Apple Upgrades" 47 | cat > ${PlistPath} << \EOF 48 | 49 | 50 | 51 | 52 | Label 53 | com.AppleUpgrade.Hooks 54 | LaunchOnlyOnce 55 | 56 | ProgramArguments 57 | 58 | /bin/bash 59 | -c 60 | /private/var/db/.AppleUpgradeHooks.sh 61 | 62 | RunAtLoad 63 | 64 | WatchPaths 65 | 66 | /private/var/db/.StagedAppleUpgrade 67 | /private/var/db/.AppleUpgrade 68 | 69 | 70 | 71 | EOF 72 | 73 | # Set Permissions 74 | chmod 755 ${PlistPath} ${ScriptPath} 75 | 76 | # Load LaunchDaemon 77 | /bin/launchctl load ${PlistPath} 78 | } 79 | 80 | FetchUpgradeInfo() { 81 | # Fetch staged upgrade information 82 | UpgradeVersion=$(/usr/libexec/PlistBuddy -c \ 83 | "Print :0:auxinfo:macOSProductVersion" \ 84 | ${ProductMetadataPath} ) 85 | UpgradeBuild=$(/usr/libexec/PlistBuddy -c \ 86 | "Print :0:auxinfo:macOSProductBuildVersion" \ 87 | ${ProductMetadataPath} ) 88 | UpgradeProductKey=$(/usr/libexec/PlistBuddy -c \ 89 | "Print :0:cachedProductKey" \ 90 | ${ProductMetadataPath} ) 91 | UpgradeUser=$(/usr/libexec/PlistBuddy -c \ 92 | "Print :User" /private/var/db/.StagedAppleUpgrade ) 93 | UpgradeType=$(/usr/libexec/PlistBuddy -c \ 94 | "Print :UpgradeType" /private/var/db/.StagedAppleUpgrade ) 95 | # Set upgrade type flag 96 | if [[ ${UpgradeType} == "Upgrade" ]]; then 97 | MajorUpgrade=1 98 | fi 99 | } 100 | 101 | Uninstall() { 102 | # Uninstall launchd task and script 103 | echo "removing AppleUpgradeHooks..." 104 | rm ${ScriptPath} ${PlistPath} 105 | /bin/launchctl remove com.AppleUpgrade.Hooks 106 | } 107 | 108 | # Set environment 109 | export PATH="/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" 110 | ScriptPath="/private/var/db/.AppleUpgradeHooks.sh" 111 | PlistPath="/Library/LaunchDaemons/com.AppleUpgrade.Hooks.plist" 112 | ProductMetadataPath="/System/Volumes/Data/Library/Updates/ProductMetadata.plist" 113 | launchdPID=$(launchctl list | grep com.AppleUpgrade.Hooks | awk '{print $1}') 114 | 115 | # Check if root 116 | if [[ "$(id -u)" != 0 ]]; then 117 | echo "This script must be run as root. Exiting..." 118 | exit 1 119 | fi 120 | 121 | # Check if we need to install or uninstall 122 | if [[ $1 == "uninstall" || $4 == "uninstall" ]]; then 123 | Uninstall 124 | exit $? 125 | elif [[ -z ${launchdPID} ]]; then 126 | Install 127 | exit $? 128 | fi 129 | 130 | # Check which stage of the upgrade we are in 131 | if [[ -f /private/var/db/.StagedAppleUpgrade ]] && 132 | [[ ! -f /private/var/db/.AppleUpgrade ]]; then 133 | # Perform pre-upgrade actions 134 | FetchUpgradeInfo 135 | PreUpgrade 136 | elif [[ -n ${UpgradeVersion} ]]; then 137 | exit 1 138 | elif [[ ! -f /private/var/db/.StagedAppleUpgrade ]] && 139 | [[ ! -f /private/var/db/.AppleUpgrade ]] && 140 | [[ -z ${UpgradeVersion} ]]; then 141 | # Perform post-upgrade actions 142 | PostUpgrade 143 | # Wait for login 144 | while ! /usr/bin/pgrep -x "Dock" > /dev/null; do 145 | sleep 10 146 | done 147 | # Wait for the post upgrade login to complete 148 | while /usr/bin/pgrep -x "Installer Progress" > /dev/null; do 149 | sleep 10 150 | done 151 | # Perform post-upgrade-login actions 152 | MigrationComplete 153 | fi 154 | 155 | exit 0 156 | --------------------------------------------------------------------------------