1932 lines
87 KiB
Bash
Executable File
1932 lines
87 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# shellcheck disable=SC2001
|
|
# this is to use sed in the case statements
|
|
# shellcheck disable=SC2034
|
|
# this is due to the dynamic variable assignments used in the localization strings
|
|
|
|
|
|
###############
|
|
## VARIABLES ##
|
|
###############
|
|
|
|
# script name
|
|
script_name="erase-install"
|
|
|
|
# Version of this script
|
|
version="25.0"
|
|
|
|
# URL for downloading installinstallmacos.py
|
|
installinstallmacos_url="https://raw.githubusercontent.com/grahampugh/macadmin-scripts/main/installinstallmacos.py"
|
|
installinstallmacos_checksum="08ceb0187bd648e040c8ba23f79192f7d91b1250dbff47107c29cb2bca1ce433"
|
|
|
|
# Directory in which to place the macOS installer. Overridden with --path
|
|
installer_directory="/Applications"
|
|
|
|
# Default working directory (may be overridden by the --workdir parameter)
|
|
workdir="/Library/Management/erase-install"
|
|
|
|
# URL for downloading macadmins python (with tag version) for standalone script running
|
|
macadmins_python_version="v.3.9.5.09222021234106"
|
|
macadmins_python_url="https://api.github.com/repos/macadmins/python/releases/tags/$macadmins_python_version"
|
|
macadmins_python_path="/Library/ManagedFrameworks/Python/Python3.framework/Versions/Current/bin/python3"
|
|
|
|
# Dialog helper apps
|
|
jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
|
|
depnotify_app="/Applications/Utilities/DEPNotify.app"
|
|
depnotify_log="/var/tmp/depnotify.log"
|
|
depnotify_confirmation_file="/var/tmp/com.depnotify.provisioning.done"
|
|
depnotify_download_url="https://files.nomad.menu/DEPNotify.pkg"
|
|
|
|
|
|
###################
|
|
## LOCALIZATIONS ##
|
|
###################
|
|
|
|
# Grab currently logged in user to set the language for Dialogue messages
|
|
current_user=$(/usr/sbin/scutil <<< "show State:/Users/ConsoleUser" | /usr/bin/awk -F': ' '/[[:space:]]+Name[[:space:]]:/ { if ( $2 != "loginwindow" ) { print $2 }}')
|
|
language=$(/usr/libexec/PlistBuddy -c 'print AppleLanguages:0' "/Users/${current_user}/Library/Preferences/.GlobalPreferences.plist")
|
|
if [[ $language = de* ]]; then
|
|
user_language="de"
|
|
elif [[ $language = nl* ]]; then
|
|
user_language="nl"
|
|
elif [[ $language = fr* ]]; then
|
|
user_language="fr"
|
|
else
|
|
user_language="en"
|
|
fi
|
|
|
|
# Dialogue localizations - download window
|
|
dialog_dl_title_en="Downloading macOS"
|
|
dialog_dl_title_de="macOS wird heruntergeladen"
|
|
dialog_dl_title_nl="macOS downloaden"
|
|
dialog_dl_title_fr="Téléchargement de macOS"
|
|
|
|
dialog_dl_desc_en="We need to download the macOS installer to your computer; this will take several minutes."
|
|
dialog_dl_desc_de="Der macOS Installer wird heruntergeladen, dies dauert mehrere Minuten."
|
|
dialog_dl_desc_nl="We moeten het macOS besturingssysteem downloaden, dit duurt enkele minuten."
|
|
dialog_dl_desc_fr="Nous devons télécharger le programme d'installation de macOS sur votre ordinateur, cela prendra plusieurs minutes."
|
|
|
|
# Dialogue localizations - erase lockscreen
|
|
dialog_erase_title_en="Erasing macOS"
|
|
dialog_erase_title_de="macOS wiederherstellen"
|
|
dialog_erase_title_nl="macOS herinstalleren"
|
|
dialog_erase_title_fr="Effacement de macOS"
|
|
|
|
dialog_erase_desc_en="Preparing the installer may take up to 30 minutes. Once completed your computer will reboot and continue the reinstallation."
|
|
dialog_erase_desc_de="Das Vorbereiten des Installationsprogramms kann bis zu 30 Minuten dauern. Nach Abschluss wird Ihr Computer neu gestartet und die Neuinstallation fortgesetzt."
|
|
dialog_erase_desc_nl="Het voorbereiden van het installatieprogramma kan tot 30 minuten duren. Zodra het proces is voltooid, wordt uw computer opnieuw opgestart en wordt de herinstallatie voortgezet."
|
|
dialog_erase_desc_fr="La préparation de l'installation peut prendre jusqu'à 30 minutes. Une fois terminée, votre ordinateur redémarrera et poursuivra la réinstallation."
|
|
|
|
# Dialogue localizations - reinstall lockscreen
|
|
dialog_reinstall_title_en="Upgrading macOS"
|
|
dialog_reinstall_title_de="Upgrading macOS"
|
|
dialog_reinstall_title_nl="macOS upgraden"
|
|
dialog_reinstall_title_fr="Mise à niveau de macOS"
|
|
|
|
dialog_reinstall_status_en="Preparing macOS for installation"
|
|
dialog_reinstall_status_de="Vorbereiten von macOS für die Installation"
|
|
dialog_reinstall_status_nl="MacOS voorbereiden voor installatie"
|
|
dialog_reinstall_status_fr="Préparation de macOS pour l'installation"
|
|
|
|
dialog_reinstall_heading_en="Please wait as we prepare your computer for upgrading macOS."
|
|
dialog_reinstall_heading_de="Bitte warten, das Upgrade macOS wird ausgeführt."
|
|
dialog_reinstall_heading_nl="Even geduld terwijl we uw computer voorbereiden voor de upgrade van macOS."
|
|
dialog_reinstall_heading_fr="Veuillez patienter pendant que nous préparons votre ordinateur pour la mise à niveau de macOS."
|
|
|
|
dialog_reinstall_desc_en="This process may take up to 30 minutes. Once completed your computer will reboot and begin the upgrade."
|
|
dialog_reinstall_desc_de="Dieser Prozess benötigt bis zu 30 Minuten. Der Mac startet anschliessend neu und beginnt mit dem Update."
|
|
dialog_reinstall_desc_nl="Dit proces duurt ongeveer 30 minuten. Zodra dit is voltooid, wordt uw computer opnieuw opgestart en begint de upgrade."
|
|
dialog_reinstall_desc_fr="Ce processus peut prendre jusqu'à 30 minutes. Une fois terminé, votre ordinateur redémarrera et commencera la mise à niveau."
|
|
|
|
# Dialogue localizations - confirmation window (erase)
|
|
dialog_erase_confirmation_desc_en="Please confirm that you want to ERASE ALL DATA FROM THIS DEVICE and reinstall macOS"
|
|
dialog_erase_confirmation_desc_de="Bitte bestätigen, dass Sie ALLE DATEN VON DIESEM GERÄT LÖSCHEN und macOS neu installieren wollen"
|
|
dialog_erase_confirmation_desc_nl="Weet je zeker dat je ALLE GEGEVENS VAN DIT APPARAAT WILT WISSEN en macOS opnieuw installeert?"
|
|
dialog_erase_confirmation_desc_fr="Veuillez confirmer que vous souhaitez EFFACER TOUTES LES DONNÉES DE CET APPAREIL et réinstaller macOS"
|
|
|
|
# Dialogue localizations - confirmation window (reinstall)
|
|
dialog_reinstall_confirmation_desc_en="Please confirm that you want to upgrade macOS on this system now"
|
|
dialog_reinstall_confirmation_desc_de="Bitte bestätigen Sie, dass Sie macOS auf diesem System jetzt aktualisieren möchten"
|
|
dialog_reinstall_confirmation_desc_nl="Bevestig dat u macOS op dit systeem nu wilt updaten"
|
|
dialog_reinstall_confirmation_desc_fr="Veuillez confirmer que vous voulez mettre à jour macOS sur ce système maintenant."
|
|
|
|
# Dialogue localizations - confirmation window status
|
|
dialog_confirmation_status_en="Press Cmd + Ctrl + C to Cancel"
|
|
dialog_confirmation_status_de="Drücken Sie Cmd + Ctrl + C zum Abbrechen"
|
|
dialog_confirmation_status_nl="Druk op Cmd + Ctrl + C om te Annuleren"
|
|
dialog_confirmation_status_fr="Appuyez sur Cmd + Ctrl + C pour annuler"
|
|
|
|
# Dialogue buttons
|
|
dialog_confirmation_button_en="Confirm"
|
|
dialog_confirmation_button_de="Bestätigen"
|
|
dialog_confirmation_button_nl="Bevestig"
|
|
dialog_confirmation_button_fr="Confirmer"
|
|
|
|
dialog_cancel_button_en="Stop"
|
|
dialog_cancel_button_de="Abbrechen"
|
|
dialog_cancel_button_nl="Annuleren"
|
|
dialog_cancel_button_fr="Annuler"
|
|
|
|
dialog_enter_button_en="Enter"
|
|
dialog_enter_button_de="Eingeben"
|
|
dialog_enter_button_nl="Enter"
|
|
dialog_enter_button_fr="Entrer"
|
|
|
|
# Dialogue localizations - free space check
|
|
dialog_check_desc_en="The macOS upgrade cannot be installed as there is not enough space left on the drive."
|
|
dialog_check_desc_de="Das Upgrade von macOS kann nicht installiert werden, da nicht genügend Speicherplatz auf dem Laufwerk vorhanden ist."
|
|
dialog_check_desc_nl="De upgrade van macOS kan niet worden geïnstalleerd omdat er niet genoeg ruimte is op de schijf."
|
|
dialog_check_desc_fr="La mise à niveau de macOS ne peut pas être installée car il n'y a pas assez d'espace disponible sur ce volume."
|
|
|
|
# Dialogue localizations - power check
|
|
dialog_power_title_en="Waiting for AC Power Connection"
|
|
dialog_power_title_de="Warten auf AC-Netzteil"
|
|
dialog_power_title_nl="Wachten op stroomadapter"
|
|
dialog_power_title_fr="En attente de l'alimentation secteur"
|
|
|
|
dialog_power_desc_en="Please connect your computer to power using an AC power adapter. This process will continue if AC power is detected within the next:"
|
|
dialog_power_desc_de="Bitte schließen Sie Ihren Computer mit einem AC-Netzteil an das Stromnetz an. Dieser Prozess wird fortgesetzt, sobald die AC-Stromversorgung innerhalb der folgende Zeitdauer erkannt wird:"
|
|
dialog_power_desc_nl="Sluit uw computer aan met de stroomadapter. Zodra deze is gedetecteerd gaat het proces verder binnen de volgende:"
|
|
dialog_power_desc_fr="Veuillez connecter votre ordinateur à un adaptateur secteur. Ce processus se poursuivra une fois que l'alimentation secteur sera détectée dans la suivante:"
|
|
|
|
dialog_nopower_desc_en="Exiting. AC power was not connected after waiting for:"
|
|
dialog_nopower_desc_de="Beenden. Die Stromversorgung wurde nach einer Wartezeit nicht hergestellt:"
|
|
dialog_nopower_desc_nl="Afsluiten. De wisselstroom was niet aangesloten na het wachten op:"
|
|
dialog_nopower_desc_fr="Sortie. Le courant alternatif n'a pas été connecté après avoir attendu:"
|
|
|
|
# Dialogue localizations - ask for short name
|
|
dialog_short_name_en="Please enter an account name to start the reinstallation process"
|
|
dialog_short_name_de="Bitte geben Sie einen Kontonamen ein, um die Neuinstallation zu starten"
|
|
dialog_short_name_nl="Voer een accountnaam in om het installatieproces te starten"
|
|
dialog_short_name_fr="Veuillez entrer un nom de compte pour démarrer le processus de réinstallation"
|
|
|
|
# Dialogue localizations - ask for password
|
|
dialog_not_volume_owner_en="Account is not a Volume Owner! Please login using one of the following accounts and try again"
|
|
dialog_not_volume_owner_de="Konto ist kein Volume-Besitzer! Bitte melden Sie sich mit einem der folgenden Konten an und versuchen Sie es erneut"
|
|
dialog_not_volume_owner_nl="Account is geen volume-eigenaar! Log in met een van de volgende accounts en probeer het opnieuw"
|
|
dialog_not_volume_owner_fr="Le compte n'est pas propriétaire du volume! Veuillez vous connecter en utilisant l'un des comptes suivants et réessayer"
|
|
|
|
# Dialogue localizations - invalid user
|
|
dialog_user_invalid_en="This account cannot be used to to perform the reinstall"
|
|
dialog_user_invalid_de="Dieses Konto kann nicht zur Durchführung der Neuinstallation verwendet werden"
|
|
dialog_user_invalid_nl="Dit account kan niet worden gebruikt om de herinstallatie uit te voeren"
|
|
dialog_user_invalid_fr="Ce compte ne peut pas être utilisé pour effectuer la réinstallation"
|
|
|
|
# Dialogue localizations - invalid password
|
|
dialog_invalid_password_en="ERROR: The password entered is NOT the login password for"
|
|
dialog_invalid_password_de="ERROR: Das eingegebene Kennwort ist NICHT das Anmeldekennwort für"
|
|
dialog_invalid_password_nl="FOUT: Het ingevoerde wachtwoord is NIET het inlogwachtwoord voor"
|
|
dialog_invalid_password_fr="ERREUR : Le mot de passe entré n'est PAS le mot de passe de connexion pour"
|
|
|
|
# Dialogue localizations - not a volume owner
|
|
dialog_get_password_en="Please enter the password for the account"
|
|
dialog_get_password_de="Bitte geben Sie das Passwort für das Konto ein"
|
|
dialog_get_password_nl="Voer het wachtwoord voor het account in"
|
|
dialog_get_password_fr="Veuillez saisir le mot de passe du compte"
|
|
|
|
# icon for download window
|
|
dialog_dl_icon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/SidebarDownloadsFolder.icns"
|
|
|
|
# icon for confirmation dialog
|
|
dialog_confirmation_icon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"
|
|
|
|
# set localisation variables
|
|
dialog_dl_title=dialog_dl_title_${user_language}
|
|
dialog_dl_desc=dialog_dl_desc_${user_language}
|
|
dialog_erase_title=dialog_erase_title_${user_language}
|
|
dialog_erase_desc=dialog_erase_desc_${user_language}
|
|
dialog_reinstall_title=dialog_reinstall_title_${user_language}
|
|
dialog_reinstall_heading=dialog_reinstall_heading_${user_language}
|
|
dialog_reinstall_desc=dialog_reinstall_desc_${user_language}
|
|
dialog_reinstall_status=dialog_reinstall_status_${user_language}
|
|
dialog_erase_confirmation_title=dialog_erase_confirmation_title_${user_language}
|
|
dialog_erase_confirmation_desc=dialog_erase_confirmation_desc_${user_language}
|
|
dialog_confirmation_status=dialog_confirmation_status_${user_language}
|
|
dialog_confirmation_button=dialog_confirmation_button_${user_language}
|
|
dialog_reinstall_confirmation_title=dialog_reinstall_confirmation_title_${user_language}
|
|
dialog_reinstall_confirmation_desc=dialog_reinstall_confirmation_desc_${user_language}
|
|
dialog_cancel_button=dialog_cancel_button_${user_language}
|
|
dialog_enter_button=dialog_enter_button_${user_language}
|
|
dialog_check_desc=dialog_check_desc_${user_language}
|
|
dialog_power_desc=dialog_power_desc_${user_language}
|
|
dialog_nopower_desc=dialog_nopower_desc_${user_language}
|
|
dialog_power_title=dialog_power_title_${user_language}
|
|
dialog_short_name=dialog_short_name_${user_language}
|
|
dialog_user_invalid=dialog_user_invalid_${user_language}
|
|
dialog_get_password=dialog_get_password_${user_language}
|
|
dialog_invalid_password=dialog_invalid_password_${user_language}
|
|
dialog_not_volume_owner=dialog_not_volume_owner_${user_language}
|
|
|
|
|
|
###############
|
|
## FUNCTIONS ##
|
|
###############
|
|
|
|
ask_for_password() {
|
|
# required for Silicon Macs
|
|
/usr/bin/osascript <<END
|
|
set nameentry to text returned of (display dialog "${!dialog_get_password} ($account_shortname)" default answer "" with hidden answer buttons {"${!dialog_enter_button}", "${!dialog_cancel_button}"} default button 1 with icon 2)
|
|
END
|
|
}
|
|
|
|
ask_for_shortname() {
|
|
# required for Silicon Macs
|
|
/usr/bin/osascript <<END
|
|
set nameentry to text returned of (display dialog "${!dialog_short_name}" default answer "" buttons {"${!dialog_enter_button}", "${!dialog_cancel_button}"} default button 1 with icon 2)
|
|
END
|
|
}
|
|
|
|
check_free_space() {
|
|
# determine if the amount of free and purgable drive space is sufficient for the upgrade to take place.
|
|
free_disk_space=$(osascript -l 'JavaScript' -e "ObjC.import('Foundation'); var freeSpaceBytesRef=Ref(); $.NSURL.fileURLWithPath('/').getResourceValueForKeyError(freeSpaceBytesRef, 'NSURLVolumeAvailableCapacityForImportantUsageKey', null); Math.round(ObjC.unwrap(freeSpaceBytesRef[0]) / 1000000000)") # with thanks to Pico
|
|
|
|
if [[ $free_disk_space -ge $min_drive_space ]]; then
|
|
echo " [check_free_space] OK - $free_disk_space GB free/purgeable disk space detected"
|
|
else
|
|
echo " [check_free_space] ERROR - $free_disk_space GB free/purgeable disk space detected"
|
|
if [[ -f "$jamfHelper" ]]; then
|
|
"$jamfHelper" -windowType "utility" -description "${!dialog_check_desc}" -alignDescription "left" -icon "$dialog_confirmation_icon" -button1 "OK" -defaultButton "0" -cancelButton "1"
|
|
else
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "${!dialog_check_desc}" "" "OK" stop &
|
|
fi
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_installer_pkg_is_valid() {
|
|
# check InstallAssistant pkg validity
|
|
# packages generated by installinstallmacos.py have the format InstallAssistant-version-build.pkg
|
|
# Extracting an actual version from the package is slow as the entire package must be unpackaged
|
|
# to read the PackageInfo file.
|
|
# We are here YOLOing the filename instead. Of course it could be spoofed, but that would not be
|
|
# in anyone's interest to attempt as it will just make the script eventually fail.
|
|
echo " [check_installer_pkg_is_valid] Checking validity of $existing_installer_pkg."
|
|
installer_pkg_build=$( basename "$existing_installer_pkg" | sed 's|.pkg||' | cut -d'-' -f 3 )
|
|
system_build=$( /usr/bin/sw_vers -buildVersion )
|
|
|
|
compare_build_versions "$system_build" "$installer_pkg_build"
|
|
|
|
if [[ $first_build_newer == "yes" ]]; then
|
|
echo " [check_installer_pkg_is_valid] Installer: $installer_pkg_build < System: $system_build : invalid build."
|
|
working_installer_pkg="$existing_installer_pkg"
|
|
invalid_installer_found="yes"
|
|
else
|
|
echo " [check_installer_pkg_is_valid] Installer: $installer_pkg_build >= System: $system_build : valid build."
|
|
working_installer_pkg="$existing_installer_pkg"
|
|
invalid_installer_found="no"
|
|
fi
|
|
|
|
working_macos_app="$existing_installer_app"
|
|
}
|
|
|
|
check_installer_is_valid() {
|
|
# check installer validity:
|
|
# The Build version in the app Info.plist is often older than the advertised build,
|
|
# so it's not a great check for validity
|
|
# check if running --erase, where we might be using the same build.
|
|
# The actual build number is found in the SharedSupport.dmg in com_apple_MobileAsset_MacSoftwareUpdate.xml (Big Sur and greater).
|
|
# This is new from Big Sur, so we include a fallback to the Info.plist file just in case.
|
|
echo " [check_installer_is_valid] Checking validity of $existing_installer_app."
|
|
|
|
# first ensure that some earlier instance is not still mounted as it might interfere with the check
|
|
[[ -d "/Volumes/Shared Support" ]] && diskutil unmount force "/Volumes/Shared Support"
|
|
# now attempt to mount
|
|
if [[ -f "$existing_installer_app/Contents/SharedSupport/SharedSupport.dmg" ]]; then
|
|
if hdiutil attach -quiet -noverify "$existing_installer_app/Contents/SharedSupport/SharedSupport.dmg" ; then
|
|
echo " [check_installer_is_valid] Mounting $existing_installer_app/Contents/SharedSupport/SharedSupport.dmg"
|
|
sleep 1
|
|
build_xml="/Volumes/Shared Support/com_apple_MobileAsset_MacSoftwareUpdate/com_apple_MobileAsset_MacSoftwareUpdate.xml"
|
|
if [[ -f "$build_xml" ]]; then
|
|
echo " [check_installer_is_valid] Using Build value from com_apple_MobileAsset_MacSoftwareUpdate.xml"
|
|
installer_build=$(/usr/libexec/PlistBuddy -c "Print :Assets:0:Build" "$build_xml")
|
|
sleep 1
|
|
diskutil unmount force "/Volumes/Shared Support"
|
|
else
|
|
echo " [check_installer_is_valid] ERROR: com_apple_MobileAsset_MacSoftwareUpdate.xml not found. Check the mount point at /Volumes/Shared Support"
|
|
fi
|
|
else
|
|
echo " [check_installer_is_valid] Mounting SharedSupport.dmg failed"
|
|
fi
|
|
else
|
|
# if that fails, fallback to the method for 10.15 or less, which is less accurate
|
|
echo " [check_installer_is_valid] Using DTSDKBuild value from Info.plist"
|
|
if [[ -f "$existing_installer_app/Contents/Info.plist" ]]; then
|
|
installer_build=$( /usr/bin/defaults read "$existing_installer_app/Contents/Info.plist" DTSDKBuild )
|
|
else
|
|
echo " [check_installer_is_valid] Installer Info.plist could not be found!"
|
|
fi
|
|
fi
|
|
if [[ ! $installer_build ]]; then
|
|
echo " [check_installer_is_valid] Build of existing installer could not be found!"
|
|
exit 1
|
|
fi
|
|
|
|
system_build=$( /usr/bin/sw_vers -buildVersion )
|
|
|
|
compare_build_versions "$system_build" "$installer_build"
|
|
if [[ $first_build_major_newer == "yes" || $first_build_minor_newer == "yes" ]]; then
|
|
echo " [check_installer_is_valid] Installer: $installer_build < System: $system_build : invalid build."
|
|
invalid_installer_found="yes"
|
|
elif [[ $first_build_patch_newer == "yes" ]]; then
|
|
echo " [check_installer_is_valid] Installer: $installer_build < System: $system_build : build might work but if it fails, please obtain a newer installer."
|
|
warning_issued="yes"
|
|
invalid_installer_found="no"
|
|
else
|
|
echo " [check_installer_is_valid] Installer: $installer_build >= System: $system_build : valid build."
|
|
invalid_installer_found="no"
|
|
fi
|
|
|
|
working_macos_app="$existing_installer_app"
|
|
}
|
|
|
|
check_newer_available() {
|
|
# Download installinstallmacos.py and MacAdmins python
|
|
get_installinstallmacos
|
|
get_relocatable_python
|
|
if [[ ! -f "$python_path" ]]; then
|
|
# fall back to python2
|
|
python_path=$(which python)
|
|
fi
|
|
|
|
# build arguments for installinstallmacos
|
|
installinstallmacos_args=()
|
|
installinstallmacos_args+=("--workdir")
|
|
installinstallmacos_args+=("$workdir")
|
|
installinstallmacos_args+=("--list")
|
|
if [[ $catalogurl ]]; then
|
|
echo " [check_newer_available] Non-standard catalog URL selected"
|
|
installinstallmacos_args+=("--catalogurl")
|
|
installinstallmacos_args+=("$catalogurl")
|
|
elif [[ $seedprogram ]]; then
|
|
echo " [check_newer_available] Non-standard seedprogram selected"
|
|
installinstallmacos_args+=("--seed")
|
|
installinstallmacos_args+=("$seedprogram")
|
|
fi
|
|
if [[ $beta == "yes" ]]; then
|
|
echo " [check_newer_available] Beta versions included"
|
|
installinstallmacos_args+=("--beta")
|
|
fi
|
|
if [[ $pkg_installer ]]; then
|
|
echo " [check_newer_available] checking against package installers"
|
|
installinstallmacos_args+=("--pkg")
|
|
fi
|
|
|
|
# run installinstallmacos.py with list and then interrogate the plist
|
|
# TEST
|
|
echo
|
|
echo " [check_newer_available] This command is now being run:"
|
|
echo
|
|
echo " installinstallmacos.py ${installinstallmacos_args[*]}"
|
|
|
|
if "$python_path" "$workdir/installinstallmacos.py" "${installinstallmacos_args[@]}" > /dev/null; then
|
|
i=0
|
|
newer_build_found="no"
|
|
if [[ -f "$workdir/softwareupdate.plist" ]]; then
|
|
while available_build=$( /usr/libexec/PlistBuddy -c "Print :result:$i:build" "$workdir/softwareupdate.plist" 2>/dev/null); do
|
|
compare_build_versions "$available_build" "$installer_build"
|
|
if [[ "$first_build_newer" == "yes" ]]; then
|
|
newer_build_found="yes"
|
|
fi
|
|
i=$((i+1))
|
|
done
|
|
else
|
|
echo " [check_newer_available] ERROR reading output from installinstallmacos.py, cannot continue"
|
|
exit 1
|
|
fi
|
|
[[ $newer_build_found != "yes" ]] && echo " [check_newer_available] No newer builds found"
|
|
else
|
|
echo " [check_newer_available] ERROR running installinstallmacos.py, cannot continue"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_password() {
|
|
# Check that the password entered matches actual password
|
|
# required for Silicon Macs
|
|
# thanks to Dan Snelson for the idea
|
|
user="$1"
|
|
password="$2"
|
|
password_matches=$( /usr/bin/dscl /Search -authonly "$user" "$password" )
|
|
|
|
if [[ -z "$password_matches" ]]; then
|
|
echo " [check_password] Success: the password entered is the correct login password for $user."
|
|
password_check="pass"
|
|
else
|
|
echo " [check_password] ERROR: The password entered is NOT the login password for $user."
|
|
password_check="fail"
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "${!dialog_user_invalid}: $user" "" "OK" 2 &
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_power_status() {
|
|
# Check if device is on battery or AC power
|
|
# If not, and our power_wait_timer is above 1, allow user to connect to power for specified time period
|
|
# Acknowledgements: https://github.com/kc9wwh/macOSUpgrade/blob/master/macOSUpgrade.sh
|
|
|
|
# default power_wait_timer to 60 seconds
|
|
[[ ! $power_wait_timer ]] && power_wait_timer=60
|
|
|
|
power_wait_timer_friendly=$( printf '%02dh:%02dm:%02ds\n' $((power_wait_timer/3600)) $((power_wait_timer%3600/60)) $((power_wait_timer%60)) )
|
|
|
|
if /usr/bin/pmset -g ps | /usr/bin/grep "AC Power" > /dev/null ; then
|
|
echo " [check_power_status] OK - AC power detected"
|
|
else
|
|
echo " [check_power_status] WARNING - No AC power detected"
|
|
if [[ "$power_wait_timer" -gt 0 ]]; then
|
|
if [[ -f "$jamfHelper" ]]; then
|
|
# use jamfHelper if possible
|
|
"$jamfHelper" -windowType "utility" -title "${!dialog_power_title}" -description "${!dialog_power_desc} ${power_wait_timer_friendly}" -alignDescription "left" -icon "$dialog_confirmation_icon" &
|
|
wait_for_power "jamfHelper"
|
|
else
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "${!dialog_power_desc} ${power_wait_timer_friendly}" "" "OK" stop &
|
|
wait_for_power "osascript"
|
|
fi
|
|
else
|
|
echo " [check_power_status] ERROR - No AC power detected after ${power_wait_timer_friendly}, cannot continue."
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
compare_build_versions() {
|
|
first_build="$1"
|
|
second_build="$2"
|
|
|
|
first_build_darwin=${first_build:0:2}
|
|
second_build_darwin=${second_build:0:2}
|
|
first_build_letter=${first_build:2:1}
|
|
second_build_letter=${second_build:2:1}
|
|
first_build_minor=${first_build:3}
|
|
second_build_minor=${second_build:3}
|
|
first_build_minor_no=${first_build_minor//[!0-9]/}
|
|
second_build_minor_no=${second_build_minor//[!0-9]/}
|
|
first_build_minor_beta=${first_build_minor//[0-9]/}
|
|
second_build_minor_beta=${second_build_minor//[0-9]/}
|
|
echo " [compare_build_versions] Comparing (1) $first_build with (2) $second_build"
|
|
if [[ "$first_build" == "$second_build" ]]; then
|
|
echo " [compare_build_versions] $first_build = $second_build"
|
|
builds_match="yes"
|
|
return
|
|
elif [[ $first_build_darwin -gt $second_build_darwin ]]; then
|
|
echo " [compare_build_versions] $first_build > $second_build"
|
|
first_build_newer="yes"
|
|
first_build_major_newer="yes"
|
|
return
|
|
elif [[ $first_build_letter > $second_build_letter && $first_build_darwin -eq $second_build_darwin ]]; then
|
|
echo " [compare_build_versions] $first_build > $second_build"
|
|
first_build_newer="yes"
|
|
first_build_minor_newer="yes"
|
|
return
|
|
elif [[ ! $first_build_minor_beta && $second_build_minor_beta && $first_build_letter == "$second_build_letter" && $first_build_darwin -eq $second_build_darwin ]]; then
|
|
echo " [compare_build_versions] $first_build > $second_build (production > beta)"
|
|
first_build_newer="yes"
|
|
first_build_patch_newer="yes"
|
|
return
|
|
elif [[ ! $first_build_minor_beta && ! $second_build_minor_beta && $first_build_minor_no -lt 1000 && $second_build_minor_no -lt 1000 && $first_build_minor_no -gt $second_build_minor_no && $first_build_letter == "$second_build_letter" && $first_build_darwin -eq $second_build_darwin ]]; then
|
|
echo " [compare_build_versions] $first_build > $second_build"
|
|
first_build_newer="yes"
|
|
first_build_patch_newer="yes"
|
|
return
|
|
elif [[ ! $first_build_minor_beta && ! $second_build_minor_beta && $first_build_minor_no -ge 1000 && $second_build_minor_no -ge 1000 && $first_build_minor_no -gt $second_build_minor_no && $first_build_letter == "$second_build_letter" && $first_build_darwin -eq $second_build_darwin ]]; then
|
|
echo " [compare_build_versions] $first_build > $second_build (both betas)"
|
|
first_build_newer="yes"
|
|
first_build_patch_newer="yes"
|
|
return
|
|
elif [[ $first_build_minor_beta && $second_build_minor_beta && $first_build_minor_no -ge 1000 && $second_build_minor_no -ge 1000 && $first_build_minor_no -gt $second_build_minor_no && $first_build_letter == "$second_build_letter" && $first_build_darwin -eq $second_build_darwin ]]; then
|
|
echo " [compare_build_versions] $first_build > $second_build (both betas)"
|
|
first_build_patch_newer="yes"
|
|
first_build_newer="yes"
|
|
return
|
|
fi
|
|
|
|
}
|
|
|
|
confirm() {
|
|
if [[ $use_depnotify == "yes" ]]; then
|
|
# DEPNotify dialog option
|
|
echo " [$script_name] Opening DEPNotify confirmation message (language=$user_language)"
|
|
if [[ $erase == "yes" ]]; then
|
|
dn_title="${!dialog_erase_title}"
|
|
dn_desc="${!dialog_erase_confirmation_desc}"
|
|
else
|
|
dn_title="${!dialog_reinstall_title}"
|
|
dn_desc="${!dialog_reinstall_confirmation_desc}"
|
|
fi
|
|
dn_status="${!dialog_confirmation_status}"
|
|
dn_icon="$dialog_confirmation_icon"
|
|
dn_button="${!dialog_confirmation_button}"
|
|
dn_quit_key="c"
|
|
dep_notify
|
|
dn_pid=$(pgrep -l "DEPNotify" | cut -d " " -f1)
|
|
# wait for the confirmation button to be pressed or for the user to cancel
|
|
until [[ "$dn_pid" = "" ]]; do
|
|
sleep 1
|
|
dn_pid=$(pgrep -l "DEPNotify" | cut -d " " -f1)
|
|
done
|
|
# DEPNotify creates a bom file if the user presses the confirmation button
|
|
# but not if they cancel
|
|
if [[ -f "$depnotify_confirmation_file" ]]; then
|
|
confirmation=2
|
|
else
|
|
confirmation=0
|
|
fi
|
|
# now clear the button, quit key and dialog
|
|
dep_notify_quit
|
|
elif [[ -f "$jamfHelper" ]]; then
|
|
# jamfHelper dialog option
|
|
echo " [$script_name] Opening jamfHelper confirmation message (language=$user_language)"
|
|
if [[ $erase == "yes" ]]; then
|
|
jh_title="${!dialog_erase_title}"
|
|
jh_desc="${!dialog_erase_confirmation_desc}"
|
|
else
|
|
jh_title="${!dialog_reinstall_title}"
|
|
jh_desc="${!dialog_reinstall_confirmation_desc}"
|
|
fi
|
|
"$jamfHelper" -windowType utility -title "$jh_title" -alignHeading center -alignDescription natural -description "$jh_desc" -lockHUD -icon "$dialog_confirmation_icon" -button1 "${!dialog_cancel_button}" -button2 "${!dialog_confirmation_button}" -defaultButton 1 -cancelButton 1 2> /dev/null
|
|
confirmation=$?
|
|
else
|
|
# osascript dialog option
|
|
echo " [$script_name] Opening osascript dialog for confirmation (language=$user_language)"
|
|
if [[ $erase == "yes" ]]; then
|
|
osa_desc="${!dialog_erase_confirmation_desc}"
|
|
else
|
|
osa_desc="${!dialog_reinstall_confirmation_desc}"
|
|
fi
|
|
answer=$(
|
|
/usr/bin/osascript <<-END
|
|
set nameentry to button returned of (display dialog "$osa_desc" buttons {"${!dialog_confirmation_button}", "${!dialog_cancel_button}"} default button "${!dialog_cancel_button}" with icon 2)
|
|
END
|
|
)
|
|
if [[ "$answer" == "${!dialog_confirmation_button}" ]]; then
|
|
confirmation=2
|
|
else
|
|
confirmation=0
|
|
fi
|
|
fi
|
|
if [[ "$confirmation" == "0"* ]]; then
|
|
echo " [$script_name] User DECLINED erase-install or reinstall"
|
|
exit 0
|
|
elif [[ "$confirmation" == "2"* ]]; then
|
|
echo " [$script_name] User CONFIRMED erase-install or reinstall"
|
|
else
|
|
echo " [$script_name] User FAILED to confirm erase-install or reinstall"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
create_launchdaemon_to_remove_workdir () {
|
|
# Name of LaunchDaemon
|
|
plist_label="com.github.grahampugh.erase-install.remove"
|
|
launch_daemon="/Library/LaunchDaemons/$plist_label.plist"
|
|
# Create the plist
|
|
/usr/bin/defaults write "$launch_daemon" Label -string "$plist_label"
|
|
/usr/bin/defaults write "$launch_daemon" ProgramArguments -array \
|
|
-string /bin/rm \
|
|
-string -Rf \
|
|
-string "$workdir" \
|
|
-string "$launch_daemon"
|
|
/usr/bin/defaults write "$launch_daemon" RunAtLoad -boolean yes
|
|
/usr/bin/defaults write "$launch_daemon" LaunchOnlyOnce -boolean yes
|
|
|
|
/usr/sbin/chown root:wheel "$launch_daemon"
|
|
/bin/chmod 644 "$launch_daemon"
|
|
}
|
|
|
|
dep_notify() {
|
|
# configuration taken from https://github.com/jamf/DEPNotify-Starter
|
|
DEP_NOTIFY_CONFIG_PLIST="/Users/$current_user/Library/Preferences/menu.nomad.DEPNotify.plist"
|
|
# /usr/bin/defaults write "$DEP_NOTIFY_CONFIG_PLIST" pathToPlistFile "$DEP_NOTIFY_USER_INPUT_PLIST"
|
|
STATUS_TEXT_ALIGN="center"
|
|
/usr/bin/defaults write "$DEP_NOTIFY_CONFIG_PLIST" statusTextAlignment "$STATUS_TEXT_ALIGN"
|
|
chown "$current_user":staff "$DEP_NOTIFY_CONFIG_PLIST"
|
|
|
|
# Configure the window's look
|
|
echo "Command: Image: $dn_icon" >> "$depnotify_log"
|
|
echo "Command: MainTitle: $dn_title" >> "$depnotify_log"
|
|
echo "Command: MainText: $dn_desc" >> "$depnotify_log"
|
|
if [[ $dn_button ]]; then
|
|
echo "Command: ContinueButton: $dn_button" >> "$depnotify_log"
|
|
fi
|
|
|
|
if ! pgrep DEPNotify ; then
|
|
# Opening the app after initial configuration
|
|
if [[ "$window_type" == "fs" ]]; then
|
|
sudo -u "$current_user" open -a "$depnotify_app" --args -path "$depnotify_log" -fullScreen
|
|
else
|
|
sudo -u "$current_user" open -a "$depnotify_app" --args -path "$depnotify_log"
|
|
fi
|
|
fi
|
|
|
|
# set message below progress bar
|
|
echo "Status: $dn_status" >> "$depnotify_log"
|
|
|
|
# set alternaitve quit key (default is X)
|
|
if [[ $dn_quit_key ]]; then
|
|
echo "Command: QuitKey: $dn_quit_key" >> "$depnotify_log"
|
|
fi
|
|
|
|
}
|
|
|
|
dep_notify_progress() {
|
|
# function for DEPNotify to show progress while the installer is being downloaded or prepared
|
|
last_progress_value=0
|
|
current_progress_value=0
|
|
|
|
if [[ "$1" == "startosinstall" ]]; then
|
|
# Wait for the preparing process to start and set the progress bar to 100 steps
|
|
until grep -q "Preparing: \d" "$LOG_FILE" ; do
|
|
sleep 2
|
|
done
|
|
echo "Status: $dn_status - 0%" >> $depnotify_log
|
|
echo "Command: DeterminateManual: 100" >> $depnotify_log
|
|
|
|
# Until at least 100% is reached, calculate the preparing progress and move the bar accordingly
|
|
until [[ $current_progress_value -ge 100 ]]; do
|
|
until [[ $current_progress_value -gt $last_progress_value ]]; do
|
|
current_progress_value=$(tail -1 "$LOG_FILE" | awk 'END{print substr($NF, 1, length($NF)-3)}')
|
|
sleep 2
|
|
done
|
|
echo "Command: DeterminateManualStep: $((current_progress_value-last_progress_value))" >> $depnotify_log
|
|
echo "Status: $dn_status - $current_progress_value%" >> $depnotify_log
|
|
last_progress_value=$current_progress_value
|
|
done
|
|
|
|
elif [[ "$1" == "installinstallmacos" ]]; then
|
|
# Wait for the download to start and set the progress bar to 100 steps
|
|
until grep -q "Total" "$LOG_FILE" ; do
|
|
sleep 2
|
|
done
|
|
echo "Status: $dn_status - 0%" >> $depnotify_log
|
|
echo "Command: DeterminateManual: 100" >> $depnotify_log
|
|
sleep 2
|
|
until [[ $current_progress_value -gt 0 && $current_progress_value -lt 100 ]]; do
|
|
current_progress_value=$(tail -1 "$LOG_FILE" | awk '{print substr($(NF-9), 1, length($NF))}')
|
|
sleep 2
|
|
done
|
|
|
|
# Until at least 100% is reached, calculate the downloading progress and move the bar accordingly
|
|
until [[ $current_progress_value -ge 100 ]]; do
|
|
until [[ $current_progress_value -gt $last_progress_value ]]; do
|
|
current_progress_value=$(tail -1 "$LOG_FILE" | awk '{print substr($(NF-9), 1, length($NF))}')
|
|
sleep 2
|
|
done
|
|
echo "Command: DeterminateManualStep: $((current_progress_value-last_progress_value))" >> $depnotify_log
|
|
echo "Status: $dn_status - $current_progress_value%" >> $depnotify_log
|
|
last_progress_value=$current_progress_value
|
|
done
|
|
|
|
elif [[ "$1" == "fetch-full-installer" ]]; then
|
|
# Wait for the download to start and set the progress bar to 100 steps
|
|
until grep -q "Installing:" "$LOG_FILE" ; do
|
|
sleep 2
|
|
done
|
|
echo "Status: $dn_status - 0%" >> $depnotify_log
|
|
echo "Command: DeterminateManual: 100" >> $depnotify_log
|
|
|
|
# Until at least 100% is reached, calculate the downloading progress and move the bar accordingly
|
|
until [[ $current_progress_value -ge 100 ]]; do
|
|
until [ $current_progress_value -gt $last_progress_value ]; do
|
|
current_progress_value=$(tail -1 "$LOG_FILE" | awk 'END{print substr($NF, 1, length($NF)-3)}')
|
|
sleep 2
|
|
done
|
|
echo "Command: DeterminateManualStep: $((current_progress_value-last_progress_value))" >> $depnotify_log
|
|
echo "Status: $dn_status - $current_progress_value%" >> $depnotify_log
|
|
last_progress_value=$current_progress_value
|
|
done
|
|
fi
|
|
}
|
|
|
|
dep_notify_quit() {
|
|
# quit DEP Notify
|
|
echo "Command: Quit" >> "$depnotify_log"
|
|
# reset all the settings that might be used again
|
|
/bin/rm "$depnotify_log" "$depnotify_confirmation_file" 2>/dev/null
|
|
dn_button=""
|
|
dn_quit_key=""
|
|
dn_cancel=""
|
|
# kill dep_notify_progress background job if it's already running
|
|
if [ -f "/tmp/depnotify_progress_pid" ]; then
|
|
while read -r i; do
|
|
kill -9 "${i}"
|
|
done < /tmp/depnotify_progress_pid
|
|
/bin/rm /tmp/depnotify_progress_pid
|
|
fi
|
|
}
|
|
|
|
find_existing_installer() {
|
|
# Search for an existing download
|
|
# First let's see if this script has been run before and left an installer
|
|
existing_macos_dmg=$( find $workdir/*.dmg -maxdepth 1 -type f -print -quit 2>/dev/null )
|
|
existing_sparseimage=$( find "$workdir/"*.sparseimage -maxdepth 1 -type f -print -quit 2>/dev/null )
|
|
existing_installer_app=$( find "$installer_directory/Install macOS"*.app -maxdepth 1 -type d -print -quit 2>/dev/null )
|
|
existing_installer_pkg=$( find "$workdir/InstallAssistant"*.pkg -maxdepth 1 -type f -print -quit 2>/dev/null )
|
|
|
|
if [[ -f "$existing_macos_dmg" ]]; then
|
|
echo " [find_existing_installer] Installer image found at $existing_macos_dmg."
|
|
hdiutil attach "$existing_macos_dmg"
|
|
existing_installer_app=$( find '/Volumes/'*macOS*/*.app -maxdepth 1 -type d -print -quit 2>/dev/null )
|
|
check_installer_is_valid
|
|
elif [[ -f "$existing_sparseimage" ]]; then
|
|
echo " [find_existing_installer] Installer sparse image found at $existing_sparseimage."
|
|
hdiutil attach "$existing_sparseimage"
|
|
existing_installer_app=$( find '/Volumes/'*macOS*/Applications/*.app -maxdepth 1 -type d -print -quit 2>/dev/null )
|
|
check_installer_is_valid
|
|
elif [[ -d "$existing_installer_app" ]]; then
|
|
echo " [find_existing_installer] Installer found at $existing_installer_app."
|
|
app_is_in_applications_folder="yes"
|
|
check_installer_is_valid
|
|
elif [[ -f "$existing_installer_pkg" ]]; then
|
|
echo " [find_existing_installer] InstallAssistant package found at $existing_installer_pkg."
|
|
check_installer_pkg_is_valid
|
|
else
|
|
echo " [find_existing_installer] No valid installer found."
|
|
if [[ $clear_cache == "yes" ]]; then
|
|
exit
|
|
fi
|
|
fi
|
|
}
|
|
|
|
find_extra_packages() {
|
|
# set install_package_list to blank.
|
|
install_package_list=()
|
|
for file in "$extras_directory"/*.pkg; do
|
|
if [[ $file != *"/*.pkg" ]]; then
|
|
echo " [find_extra_installers] Additional package to install: $file"
|
|
install_package_list+=("--installpackage")
|
|
install_package_list+=("$file")
|
|
fi
|
|
done
|
|
}
|
|
|
|
get_depnotify() {
|
|
# grab installinstallmacos.py if not already there
|
|
# note this does a SHA256 checksum check and will delete the file and exit if this fails
|
|
if [[ -d "$depnotify_app" ]]; then
|
|
echo " [get_depnotify] DEPNotify is installed ($depnotify_app)"
|
|
else
|
|
if [[ ! $no_curl ]]; then
|
|
echo " [get_depnotify] Downloading DEPNotify.app..."
|
|
if /usr/bin/curl -L "$depnotify_download_url" -o "$workdir/DEPNotify.pkg" ; then
|
|
if ! installer -pkg "$workdir/DEPNotify.pkg" -target / ; then
|
|
echo " [get_depnotify] DEPNotify installation failed"
|
|
fi
|
|
else
|
|
echo " [get_depnotify] DEPNotify download failed"
|
|
fi
|
|
fi
|
|
# check it did actually get downloaded
|
|
if [[ -d "$depnotify_app" ]]; then
|
|
echo " [get_depnotify] DEPNotify is installed"
|
|
use_depnotify="yes"
|
|
dep_notify_quit
|
|
else
|
|
echo " [get_depnotify] Could not download DEPNotify.app."
|
|
fi
|
|
fi
|
|
}
|
|
|
|
get_installinstallmacos() {
|
|
# grab installinstallmacos.py if not already there
|
|
# note this does a SHA256 checksum check and will delete the file and exit if this fails
|
|
if [[ ! -f "$workdir/installinstallmacos.py" || $force_installinstallmacos == "yes" ]]; then
|
|
if [[ ! $no_curl ]]; then
|
|
echo " [get_installinstallmacos] Downloading installinstallmacos.py..."
|
|
# delete existing version so curl can create new file
|
|
if [[ -f "$workdir/installinstallmacos.py" ]]; then
|
|
/bin/rm "$workdir/installinstallmacos.py"
|
|
fi
|
|
# use curl -o instead of > redirect, which causes permission error when run with sudo
|
|
/usr/bin/curl -H 'Cache-Control: no-cache' -s "$installinstallmacos_url" -o "$workdir/installinstallmacos.py"
|
|
if echo "$installinstallmacos_checksum $workdir/installinstallmacos.py" | shasum -c; then
|
|
echo " [get_installinstallmacos] downloaded new installinstallmacos.py successfully."
|
|
else
|
|
echo " [get_installinstallmacos] ERROR: downloaded installinstallmacos.py does not match checksum. Possible corrupted file. Deleting file."
|
|
/bin/rm "$workdir/installinstallmacos.py"
|
|
fi
|
|
fi
|
|
fi
|
|
# check it did actually get downloaded
|
|
if [[ ! -f "$workdir/installinstallmacos.py" ]]; then
|
|
echo "Could not download installinstallmacos.py so cannot continue."
|
|
exit 1
|
|
else
|
|
echo " [get_installinstallmacos] installinstallmacos.py is in $workdir"
|
|
iim_downloaded=1
|
|
fi
|
|
|
|
}
|
|
|
|
get_relocatable_python() {
|
|
# grab macadmins python and install it if not already there - used when running this script as a standalone
|
|
if [[ -L "$relocatable_python_path" && -e "$relocatable_python_path" ]]; then
|
|
echo " [get_relocatable_python] Relocatable Python is installed in $workdir"
|
|
python_path="$relocatable_python_path"
|
|
elif [[ -L "$macadmins_python_path" && -e "$macadmins_python_path" ]]; then
|
|
echo " [get_relocatable_python] MacAdmins Python is installed"
|
|
python_path="$macadmins_python_path"
|
|
else
|
|
if [[ ! $no_curl ]]; then
|
|
echo " [get_relocatable_python] Downloading MacAdmins Python package..."
|
|
macadmins_python_pkg=$( /usr/bin/curl -sl -H "Accept: application/vnd.github.v3+json" "$macadmins_python_url" | grep signed | grep url | sed 's|^.*"browser_download_url": ||' | sed 's|\"||g' )
|
|
/usr/bin/curl -L "$macadmins_python_pkg" -o "$workdir/macadmins_python-$macadmins_python_version.pkg"
|
|
installer -pkg "$workdir/macadmins_python-$macadmins_python_version.pkg" -target /
|
|
fi
|
|
# check it did actually get downloaded
|
|
if [[ -L "$macadmins_python_path" && -e "$macadmins_python_path" ]]; then
|
|
echo " [get_relocatable_python] MacAdmins Python is installed"
|
|
python_path="$macadmins_python_path"
|
|
else
|
|
echo " [get_relocatable_python] Could not download MacAdmins Python."
|
|
fi
|
|
fi
|
|
}
|
|
|
|
get_user_details() {
|
|
|
|
account_shortname="$current_user"
|
|
|
|
# check that this user exists
|
|
if ! /usr/sbin/dseditgroup -o checkmember -m "$account_shortname" everyone ; then
|
|
echo " [get_user_details] $account_shortname account cannot be found!"
|
|
user_invalid
|
|
exit 1
|
|
fi
|
|
|
|
# check that the user is a Volume Owner
|
|
user_is_volume_owner=0
|
|
users=$(/usr/sbin/diskutil apfs listUsers /)
|
|
enabled_users=""
|
|
while read -r line ; do
|
|
user=$(/usr/bin/cut -d, -f1 <<< "$line")
|
|
guid=$(/usr/bin/cut -d, -f2 <<< "$line")
|
|
if [[ $(/usr/bin/grep -A2 "$guid" <<< "$users" | /usr/bin/tail -n1 | /usr/bin/awk '{print $NF}') == "Yes" ]]; then
|
|
enabled_users+="$user "
|
|
if [[ "$account_shortname" == "$user" ]]; then
|
|
echo " [get_user_details] $account_shortname is a Volume Owner"
|
|
user_is_volume_owner=1
|
|
fi
|
|
fi
|
|
done <<< "$(/usr/bin/fdesetup list)"
|
|
if [[ $enabled_users != "" && $user_is_volume_owner = 0 ]]; then
|
|
echo " [get_user_details] $account_shortname is not a Volume Owner"
|
|
user_not_volume_owner
|
|
exit 1
|
|
fi
|
|
|
|
# get password and check that the password is correct
|
|
password_attempts=0
|
|
password_check="fail"
|
|
while [[ "$password_check" != "pass" ]] ; do
|
|
account_password=$(ask_for_password)
|
|
if [[ ! "$account_password" ]]; then
|
|
echo " [get_user_details] User cancelled."
|
|
exit 1
|
|
fi
|
|
check_password "$account_shortname" "$account_password"
|
|
|
|
password_attempts=$((password_attempts+1))
|
|
if [[ $password_attempts -ge 5 ]]; then
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "${!dialog_user_invalid}: $user" "" "OK" 2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# if we are performing eraseinstall the user needs to be an admin so let's promote the user
|
|
if [[ $erase == "yes" ]]; then
|
|
if ! /usr/sbin/dseditgroup -o checkmember -m "$account_shortname" admin ; then
|
|
if /usr/sbin/dseditgroup -o edit -a "$account_shortname" admin ; then
|
|
echo " [get_user_details] $account_shortname account has been promoted to admin so that eraseinstall can proceed"
|
|
promoted_user="$account_shortname"
|
|
else
|
|
echo " [get_user_details] $account_shortname account could not be promoted to admin so eraseinstall cannot proceed"
|
|
user_invalid
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
kill_process() {
|
|
process="$1"
|
|
if /usr/bin/pgrep -a "$process" >/dev/null ; then
|
|
/usr/bin/pkill -a "$process" && echo " [$script_name] '$process' ended" || \
|
|
echo " [$script_name] '$process' could not be killed"
|
|
fi
|
|
}
|
|
|
|
move_to_applications_folder() {
|
|
if [[ $app_is_in_applications_folder == "yes" ]]; then
|
|
echo " [move_to_applications_folder] Valid installer already in $installer_directory folder"
|
|
else
|
|
echo " [move_to_applications_folder] Moving installer to $installer_directory folder"
|
|
cp -R "$working_macos_app" $installer_directory/
|
|
existing_installer=$( find /Volumes/*macOS* -maxdepth 2 -type d -name "Install*.app" -print -quit 2>/dev/null )
|
|
if [[ -d "$existing_installer" ]]; then
|
|
echo " [move_to_applications_folder] Mounted installer will be unmounted: $existing_installer"
|
|
existing_installer_mount_point=$(echo "$existing_installer" | cut -d/ -f 1-3)
|
|
diskutil unmount force "$existing_installer_mount_point"
|
|
fi
|
|
rm -f "$existing_macos_dmg" "$existing_sparseimage"
|
|
working_macos_app=$( find "$installer_directory/Install macOS"*.app -maxdepth 1 -type d -print -quit 2>/dev/null )
|
|
echo " [move_to_applications_folder] Installer moved to $installer_directory folder"
|
|
fi
|
|
}
|
|
|
|
open_osascript_dialog() {
|
|
title="$1"
|
|
message="$2"
|
|
button1="$3"
|
|
icon="$4"
|
|
|
|
if [[ $message ]]; then
|
|
/usr/bin/osascript <<-END
|
|
display dialog "$message" ¬
|
|
buttons {"$button1"} ¬
|
|
default button 1 ¬
|
|
with title "$title" ¬
|
|
with icon $icon
|
|
END
|
|
else
|
|
/usr/bin/osascript <<-END
|
|
display dialog "$title" ¬
|
|
buttons {"$button1"} ¬
|
|
default button 1 ¬
|
|
with icon $icon
|
|
END
|
|
fi
|
|
}
|
|
|
|
overwrite_existing_installer() {
|
|
echo " [overwrite_existing_installer] Overwrite option selected. Deleting existing version."
|
|
existing_installer=$( find /Volumes/*macOS* -maxdepth 2 -type d -name "Install*.app" -print -quit 2>/dev/null )
|
|
if [[ -d "$existing_installer" ]]; then
|
|
echo " [$script_name] Mounted installer will be unmounted: $existing_installer"
|
|
existing_installer_mount_point=$(echo "$existing_installer" | cut -d/ -f 1-3)
|
|
diskutil unmount force "$existing_installer_mount_point"
|
|
fi
|
|
rm -f "$existing_macos_dmg" "$existing_sparseimage"
|
|
rm -rf "$existing_installer_app"
|
|
app_is_in_applications_folder=""
|
|
if [[ $clear_cache == "yes" ]]; then
|
|
echo " [overwrite_existing_installer] Cached installers have been removed. Quitting script as --clear-cache-only option was selected"
|
|
exit
|
|
fi
|
|
}
|
|
|
|
run_installinstallmacos() {
|
|
# Download installinstallmacos.py and MacAdmins Python
|
|
get_installinstallmacos
|
|
get_relocatable_python
|
|
|
|
# Use installinstallmacos.py to download the desired version of macOS
|
|
installinstallmacos_args=()
|
|
installinstallmacos_args+=("--workdir")
|
|
installinstallmacos_args+=("$workdir")
|
|
|
|
if [[ $list == "yes" ]]; then
|
|
echo " [run_installinstallmacos] List only mode chosen"
|
|
installinstallmacos_args+=("--list")
|
|
installinstallmacos_args+=("--warnings")
|
|
else
|
|
installinstallmacos_args+=("--ignore-cache")
|
|
fi
|
|
|
|
if [[ $pkg_installer ]]; then
|
|
installinstallmacos_args+=("--pkg")
|
|
else
|
|
installinstallmacos_args+=("--raw")
|
|
fi
|
|
|
|
if [[ $catalogurl ]]; then
|
|
echo " [run_installinstallmacos] Non-standard catalog URL selected"
|
|
installinstallmacos_args+=("--catalogurl")
|
|
installinstallmacos_args+=("$catalogurl")
|
|
elif [[ $seedprogram ]]; then
|
|
echo " [run_installinstallmacos] Non-standard seedprogram selected"
|
|
installinstallmacos_args+=("--seed")
|
|
installinstallmacos_args+=("$seedprogram")
|
|
fi
|
|
|
|
if [[ $beta == "yes" ]]; then
|
|
echo " [run_installinstallmacos] Beta versions included"
|
|
installinstallmacos_args+=("--beta")
|
|
fi
|
|
|
|
if [[ $prechosen_os ]]; then
|
|
echo " [run_installinstallmacos] Checking that selected OS $prechosen_os is available"
|
|
installinstallmacos_args+=("--os")
|
|
installinstallmacos_args+=("$prechosen_os")
|
|
[[ ($erase == "yes" || $reinstall == "yes") && $skip_validation != "yes" ]] && installinstallmacos_args+=("--validate")
|
|
|
|
elif [[ $prechosen_version ]]; then
|
|
echo " [run_installinstallmacos] Checking that selected version $prechosen_version is available"
|
|
installinstallmacos_args+=("--version")
|
|
installinstallmacos_args+=("$prechosen_version")
|
|
[[ ($erase == "yes" || $reinstall == "yes") && $skip_validation != "yes" ]] && installinstallmacos_args+=("--validate")
|
|
|
|
elif [[ $prechosen_build ]]; then
|
|
echo " [run_installinstallmacos] Checking that selected build $prechosen_build is available"
|
|
installinstallmacos_args+=("--build")
|
|
installinstallmacos_args+=("$prechosen_build")
|
|
[[ ($erase == "yes" || $reinstall == "yes") && $skip_validation != "yes" ]] && installinstallmacos_args+=("--validate")
|
|
fi
|
|
|
|
if [[ $samebuild == "yes" ]]; then
|
|
echo " [run_installinstallmacos] Checking that current build $system_build is available"
|
|
installinstallmacos_args+=("--current")
|
|
|
|
elif [[ $sameos == "yes" ]]; then
|
|
echo " [run_installinstallmacos] Checking that current OS $system_os_major.$system_os_version is available"
|
|
if [[ $system_os_major == "10" ]]; then
|
|
installinstallmacos_args+=("--os")
|
|
installinstallmacos_args+=("$system_os_major.$system_os_version")
|
|
else
|
|
installinstallmacos_args+=("--os")
|
|
installinstallmacos_args+=("$system_os_major")
|
|
fi
|
|
if [[ $skip_validation != "yes" ]]; then
|
|
[[ $erase == "yes" || $reinstall == "yes" ]] && installinstallmacos_args+=("--validate")
|
|
fi
|
|
fi
|
|
|
|
if [[ $list != "yes" && ! $prechosen_os && ! $prechosen_version && ! $prechosen_build && ! $samebuild ]]; then
|
|
echo " [run_installinstallmacos] Getting current production version"
|
|
installinstallmacos_args+=("--auto")
|
|
fi
|
|
|
|
# TEST
|
|
echo
|
|
echo " [run_installinstallmacos] This command is now being run:"
|
|
echo
|
|
echo " installinstallmacos.py ${installinstallmacos_args[*]}"
|
|
|
|
# shellcheck disable=SC2086
|
|
if ! "$python_path" "$workdir/installinstallmacos.py" "${installinstallmacos_args[@]}" ; then
|
|
echo " [run_installinstallmacos] Error obtaining valid installer. Cannot continue."
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $list == "yes" ]]; then
|
|
exit 0
|
|
fi
|
|
|
|
# Identify the installer dmg
|
|
downloaded_macos_dmg=$( find $workdir -maxdepth 1 -name 'Install_macOS*.dmg' -type f -print -quit )
|
|
downloaded_sparseimage=$( find $workdir -maxdepth 1 -name 'Install_macOS*.sparseimage' -type f -print -quit )
|
|
downloaded_installer_pkg=$( find $workdir/InstallAssistant*.pkg -maxdepth 1 -type f -print -quit 2>/dev/null )
|
|
|
|
if [[ -f "$downloaded_macos_dmg" ]]; then
|
|
echo " [run_installinstallmacos] Mounting disk image to identify installer app."
|
|
if hdiutil attach "$downloaded_macos_dmg" ; then
|
|
working_macos_app=$( find '/Volumes/'*macOS*/*.app -maxdepth 1 -type d -print -quit 2>/dev/null )
|
|
else
|
|
echo " [run_installinstallmacos] ERROR: could not mount $downloaded_macos_dmg"
|
|
exit 1
|
|
fi
|
|
elif [[ -f "$downloaded_sparseimage" ]]; then
|
|
echo " [run_installinstallmacos] Mounting sparse disk image to identify installer app."
|
|
if hdiutil attach "$downloaded_sparseimage" ; then
|
|
working_macos_app=$( find '/Volumes/'*macOS*/Applications/*.app -maxdepth 1 -type d -print -quit 2>/dev/null )
|
|
else
|
|
echo " [run_installinstallmacos] ERROR: could not mount $downloaded_sparseimage"
|
|
exit 1
|
|
fi
|
|
elif [[ -f "$downloaded_installer_pkg" ]]; then
|
|
echo " [run_installinstallmacos] InstallAssistant package downloaded to $downloaded_installer_pkg."
|
|
working_installer_pkg="$downloaded_installer_pkg"
|
|
else
|
|
echo " [run_installinstallmacos] No disk image found. I guess nothing got downloaded."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
set_seedprogram() {
|
|
if [[ $seedprogram ]]; then
|
|
echo " [set_seedprogram] $seedprogram seed program selected"
|
|
/System/Library/PrivateFrameworks/Seeding.framework/Versions/A/Resources/seedutil enroll "$seedprogram" >/dev/null
|
|
# /usr/sbin/softwareupdate -l -a >/dev/null
|
|
else
|
|
echo " [set_seedprogram] Standard seed program selected"
|
|
/System/Library/PrivateFrameworks/Seeding.framework/Versions/A/Resources/seedutil unenroll >/dev/null
|
|
# /usr/sbin/softwareupdate -l -a >/dev/null
|
|
fi
|
|
sleep 5
|
|
current_seed=$(/System/Library/PrivateFrameworks/Seeding.framework/Versions/A/Resources/seedutil current | grep "Currently enrolled in:" | sed 's|Currently enrolled in: ||')
|
|
echo " [set_seedprogram] Currently enrolled in $current_seed seed program."
|
|
}
|
|
|
|
swu_list_full_installers() {
|
|
# for 10.15.7 and above we can use softwareupdate --list-full-installers
|
|
set_seedprogram
|
|
echo
|
|
/usr/sbin/softwareupdate --list-full-installers
|
|
}
|
|
|
|
swu_fetch_full_installer() {
|
|
# for 10.15+ we can use softwareupdate --fetch-full-installer
|
|
set_seedprogram
|
|
|
|
softwareupdate_args=''
|
|
if [[ $prechosen_version ]]; then
|
|
echo " [swu_fetch_full_installer] Trying to download version $prechosen_version"
|
|
softwareupdate_args+=" --full-installer-version $prechosen_version"
|
|
fi
|
|
# now download the installer
|
|
echo " [swu_fetch_full_installer] Running /usr/sbin/softwareupdate --fetch-full-installer $softwareupdate_args"
|
|
# shellcheck disable=SC2086
|
|
/usr/sbin/softwareupdate --fetch-full-installer $softwareupdate_args
|
|
|
|
# shellcheck disable=SC2181
|
|
if [[ $? == 0 ]]; then
|
|
# Identify the installer
|
|
if find /Applications -maxdepth 1 -name 'Install macOS*.app' -type d -print -quit 2>/dev/null ; then
|
|
existing_installer_app=$( find /Applications -maxdepth 1 -name 'Install macOS*.app' -type d -print -quit 2>/dev/null )
|
|
# if we actually want to use this installer we should check that it's valid
|
|
if [[ $erase == "yes" || $reinstall == "yes" ]]; then
|
|
check_installer_is_valid
|
|
if [[ $invalid_installer_found == "yes" ]]; then
|
|
echo " [swu_fetch_full_installer] The downloaded app is invalid for this computer. Try with --version or without --fetch-full-installer"
|
|
exit 1
|
|
fi
|
|
fi
|
|
else
|
|
echo " [swu_fetch_full_installer] No install app found. I guess nothing got downloaded."
|
|
exit 1
|
|
fi
|
|
else
|
|
echo " [swu_fetch_full_installer] softwareupdate --fetch-full-installer failed. Try without --fetch-full-installer option."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
unpack_pkg_to_applications_folder() {
|
|
# if dealing with a package we now have to extract it and check it's valid
|
|
if [[ -f "$working_installer_pkg" ]]; then
|
|
echo " [unpack_pkg_to_applications_folder] Unpacking $working_installer_pkg into /Applications folder"
|
|
if /usr/sbin/installer -pkg "$working_installer_pkg" -tgt / ; then
|
|
working_macos_app=$( find /Applications -maxdepth 1 -name 'Install macOS*.app' -type d -print -quit 2>/dev/null )
|
|
if [[ -d "$working_macos_app" && "$keep_pkg" != "yes" ]]; then
|
|
echo " [unpack_pkg_to_applications_folder] Deleting $working_installer_pkg"
|
|
rm -f "$working_installer_pkg"
|
|
working_installer_pkg=""
|
|
fi
|
|
else
|
|
echo " [unpack_pkg_to_applications_folder] ERROR - $working_installer_pkg could not be unpacked"
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
user_invalid() {
|
|
# required for Silicon Macs
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "$account_shortname: ${!dialog_user_invalid}" "" "OK" 2
|
|
}
|
|
|
|
user_not_volume_owner() {
|
|
# required for Silicon Macs
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "$account_shortname ${!dialog_not_volume_owner}: ${enabled_users}" "" "OK" 2
|
|
}
|
|
|
|
wait_for_power() {
|
|
process="$1"
|
|
## Loop for "power_wait_timer" seconds until either AC Power is detected or the timer is up
|
|
echo " [wait_for_power] Waiting for AC power..."
|
|
while [[ "$power_wait_timer" -gt 0 ]]; do
|
|
if /usr/bin/pmset -g ps | /usr/bin/grep "AC Power" > /dev/null ; then
|
|
echo " [wait_for_power] OK - AC power detected"
|
|
kill_process "$process"
|
|
return
|
|
fi
|
|
sleep 1
|
|
((power_wait_timer--))
|
|
done
|
|
kill_process "$process"
|
|
if [[ -f "$jamfHelper" ]]; then
|
|
# use jamfHelper if possible
|
|
"$jamfHelper" -windowType "utility" -title "${!dialog_power_title}" -description "${!dialog_nopower_desc} ${power_wait_timer_friendly}" -alignDescription "left" -icon "$dialog_confirmation_icon" -button1 "OK" -defaultButton 1 &
|
|
else
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "${!dialog_nopower_desc} ${power_wait_timer_friendly}" "" "OK" stop &
|
|
fi
|
|
echo " [wait_for_power] ERROR - No AC power detected after waiting for ${power_wait_timer_friendly}, cannot continue."
|
|
exit 1
|
|
}
|
|
|
|
show_help() {
|
|
echo "
|
|
[$script_name] by @GrahamRPugh
|
|
|
|
Common usage:
|
|
[sudo] ./erase-install.sh [--list] [--overwrite] [--move] [--path /path/to]
|
|
[--build XYZ] [--os X.Y] [--version X.Y.Z] [--samebuild] [--sameos]
|
|
[--update] [--beta] [--seedprogram ...] [--erase] [--reinstall]
|
|
[--test-run] [--current-user]
|
|
|
|
[no flags] Finds latest current production, non-forked version
|
|
of macOS, downloads it.
|
|
--force-curl Force the download of installinstallmacos.py from GitHub every run
|
|
regardless of whether there is already a copy on the system.
|
|
Ensures that you are using the latest version.
|
|
--no-curl Prevents the download of installinstallmacos.py in case your
|
|
security team don't like it.
|
|
--list List available updates only using installinstallmacos
|
|
(don't download anything)
|
|
--seed ... Select a non-standard seed program
|
|
--catalogurl ... Select a non-standard catalog URL (overrides seedprogram)
|
|
--samebuild Finds the build of macOS that matches the
|
|
existing system version, downloads it.
|
|
--sameos Finds the version of macOS that matches the
|
|
existing system version, downloads it.
|
|
--os X.Y Finds a specific inputted OS version of macOS if available
|
|
and downloads it if so. Will choose the latest matching build.
|
|
--version X.Y.Z Finds a specific inputted minor version of macOS if available
|
|
and downloads it if so. Will choose the latest matching build.
|
|
--build XYZ Finds a specific inputted build of macOS if available
|
|
and downloads it if so.
|
|
--update Checks that an existing installer on the system is still current,
|
|
if not, it will delete it and download the current installer.
|
|
--replace-invalid Checks that an existing installer on the system is still valid
|
|
i.e. would successfully build on this system. If not, deletes it
|
|
and downloads the current installer.
|
|
--clear-cache-only When used in conjunction with --overwrite, --update or --replace-invalid,
|
|
the existing installer is removed but not replaced. This is useful
|
|
for running the script after an upgrade to clear the working files.
|
|
--cleanup-after-use Creates a LaunchDaemon to delete $workdir after use. Mainly useful
|
|
in conjunction with the --reinstall option.
|
|
--move Moves the downloaded macOS installer to $installer_directory
|
|
--path /path/to Overrides the destination of --move to a specified directory
|
|
--erase After download, erases the current system
|
|
and reinstalls macOS.
|
|
--confirm Displays a confirmation dialog prior to erasing the current
|
|
system and reinstalling macOS.
|
|
--depnotify Uses DEPNotify for dialogs instead of jamfHelper, if installed.
|
|
Only applicable with --reinstall and --erase arguments.
|
|
--reinstall After download, reinstalls macOS without erasing the
|
|
current system
|
|
--overwrite Download macOS installer even if an installer
|
|
already exists in $installer_directory
|
|
--extras /path/to Overrides the path to search for extra packages
|
|
--beta Include beta versions in the search. Works with the no-flag
|
|
(i.e. automatic), --os and --version arguments.
|
|
--check-power Checks for AC power if set.
|
|
--power-wait-limit NN
|
|
Maximum seconds to wait for detection of AC power, if
|
|
--check-power is set. Default is 60.
|
|
--preinstall-command 'some arbitrary command'
|
|
Supply a shell command to run immediately prior to startosinstall
|
|
running. An example might be 'jamf recon -department Spare'.
|
|
Ensure that the command is in quotes.
|
|
|
|
|
|
Parameters for use with Apple Silicon Mac:
|
|
Note that startosinstall requires user authentication on AS Mac. The user
|
|
must have a Secure Token. This script checks for the Secure Token of the
|
|
supplied user. An osascript dialog is used to supply the password, so
|
|
this script cannot be run at the login window or from remote terminal.
|
|
--current-user Authenticate startosinstall using the current user
|
|
--user XYZ Supply a user with which to authenticate startosinstall
|
|
|
|
Experimental features for macOS 10.15+:
|
|
--list-full-installers
|
|
List installers using 'softwareupdate --list-full-installers'
|
|
--fetch-full-installer
|
|
For compatible computers (10.15+) obtain the installer using
|
|
'softwareupdate --fetch-full-installer' method instead of
|
|
using installinstallmacos.py
|
|
|
|
Experimental features for macOS 11+:
|
|
--pkg Downloads a package installer rather than the installer app.
|
|
Can be used with the --reinstall and --erase options.
|
|
|
|
Note: If existing installer is found, this script will not check
|
|
to see if it matches the installed system version. It will
|
|
only check whether it is a valid installer. If you need to
|
|
ensure that the currently installed version of macOS is used
|
|
to wipe the device, use one of the --overwrite, --update or
|
|
--replace-invalid parameters.
|
|
|
|
Parameters useful in testing this script:
|
|
--test-run Run through the script right to the end, but do not actually
|
|
run the 'startosinstall' command. The command that would be
|
|
run is shown in stdout.
|
|
--no-fs Replaces the full-screen jamfHelper window with a smaller dialog,
|
|
so you can still access the desktop while the script runs.
|
|
--no-jamfhelper Ignores a jamfHelper installation, so that you can test (or use)
|
|
osascript dialogs.
|
|
--min-drive-space override the default minimum space required for startosinstall
|
|
to run (45 GB).
|
|
--pythonpath /path/to
|
|
Supply a path to a different python binary.
|
|
Only relevant if using a mode that involves installinstallmacos.py
|
|
--workdir /path/to Supply an alternative working directory. The default is the same
|
|
directory in which erase-install.sh is saved.
|
|
|
|
"
|
|
exit
|
|
}
|
|
|
|
finish() {
|
|
# kill caffeinate
|
|
kill_process "caffeinate"
|
|
|
|
# kill any dialogs if startosinstall ends before a reboot
|
|
kill_process "jamfHelper"
|
|
dep_notify_quit
|
|
kill_process DEPNotify
|
|
|
|
# if we promoted the user then we should demote it again
|
|
if [[ $promoted_user ]]; then
|
|
/usr/sbin/dseditgroup -o edit -d "$promoted_user" admin
|
|
echo " [$script_name] User $promoted_user was demoted back to standard user"
|
|
fi
|
|
}
|
|
|
|
|
|
###############
|
|
## MAIN BODY ##
|
|
###############
|
|
|
|
# ensure the finish function is executed when exit is signaled
|
|
trap "finish" EXIT
|
|
|
|
# Safety mechanism to prevent unwanted wipe while testing
|
|
erase="no"
|
|
reinstall="no"
|
|
|
|
# default minimum drive space in GB
|
|
# Note that the amount of space required varies between macOS installer and system versions.
|
|
# Override this default value with the --min-drive-space option.
|
|
min_drive_space=45
|
|
|
|
while test $# -gt 0 ; do
|
|
case "$1" in
|
|
-l|--list) list="yes"
|
|
;;
|
|
-lfi|--list-full-installers) list_installers="yes"
|
|
;;
|
|
-e|--erase) erase="yes"
|
|
;;
|
|
-r|--reinstall) reinstall="yes"
|
|
;;
|
|
-m|--move) move="yes"
|
|
;;
|
|
-s|--samebuild) samebuild="yes"
|
|
;;
|
|
-t|--sameos) sameos="yes"
|
|
;;
|
|
-o|--overwrite) overwrite="yes"
|
|
;;
|
|
-x|--replace-invalid) replace_invalid_installer="yes"
|
|
;;
|
|
-u|--update) update_installer="yes"
|
|
;;
|
|
-c|--confirm) confirm="yes"
|
|
;;
|
|
--beta) beta="yes"
|
|
;;
|
|
--preservecontainer) preservecontainer="yes"
|
|
;;
|
|
-f|--fetch-full-installer) ffi="yes"
|
|
;;
|
|
--pkg) pkg_installer="yes"
|
|
;;
|
|
--keep-pkg) keep_pkg="yes"
|
|
;;
|
|
--force-curl) force_installinstallmacos="yes"
|
|
;;
|
|
--no-curl) no_curl="yes"
|
|
;;
|
|
--no-fs) no_fs="yes"
|
|
;;
|
|
--skip-validation) skip_validation="yes"
|
|
;;
|
|
--current-user) use_current_user="yes"
|
|
;;
|
|
--user)
|
|
shift
|
|
account_shortname="$1"
|
|
;;
|
|
--test-run) test_run="yes"
|
|
;;
|
|
--clear-cache-only) clear_cache="yes"
|
|
;;
|
|
--cleanup-after-use) cleanup_after_use="yes"
|
|
;;
|
|
--depnotify)
|
|
if [[ -d "$depnotify_app" ]]; then
|
|
use_depnotify="yes"
|
|
dep_notify_quit
|
|
else
|
|
get_depnotify_app="yes"
|
|
fi
|
|
;;
|
|
--no-jamfhelper) jamfHelper=""
|
|
;;
|
|
--check-power)
|
|
check_power="yes"
|
|
;;
|
|
--power-wait-limit)
|
|
shift
|
|
power_wait_timer="$1"
|
|
;;
|
|
--min-drive-space)
|
|
shift
|
|
min_drive_space="$1"
|
|
;;
|
|
--seed|--seedprogram)
|
|
shift
|
|
seedprogram="$1"
|
|
;;
|
|
--catalogurl)
|
|
shift
|
|
catalogurl="$1"
|
|
;;
|
|
--path)
|
|
shift
|
|
installer_directory="$1"
|
|
;;
|
|
--pythonpath)
|
|
shift
|
|
python_path="$1"
|
|
;;
|
|
--extras)
|
|
shift
|
|
extras_directory="$1"
|
|
;;
|
|
--os)
|
|
shift
|
|
prechosen_os="$1"
|
|
;;
|
|
--version)
|
|
shift
|
|
prechosen_version="$1"
|
|
;;
|
|
--build)
|
|
shift
|
|
prechosen_build="$1"
|
|
;;
|
|
--workdir)
|
|
shift
|
|
workdir="$1"
|
|
;;
|
|
--preinstall-command)
|
|
shift
|
|
preinstall_command="$1"
|
|
echo "Preinstall: $preinstall_command"
|
|
;;
|
|
--power-wait-limit*)
|
|
power_wait_timer=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--min-drive-space*)
|
|
min_drive_space=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--seedprogram*)
|
|
seedprogram=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--catalogurl*)
|
|
catalogurl=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--path*)
|
|
installer_directory=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--pythonpath*)
|
|
python_path=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--extras*)
|
|
extras_directory=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--os*)
|
|
prechosen_os=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--version*)
|
|
prechosen_version=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--build*)
|
|
prechosen_build=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--workdir*)
|
|
workdir=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
--preinstall-command*)
|
|
preinstall_command=$(echo "$1" | sed -e 's|^[^=]*=||g')
|
|
;;
|
|
-h|--help) show_help
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
echo
|
|
echo " [$script_name] v$version script execution started: $(date)"
|
|
|
|
# not giving an option for fetch-full-installer mode for now... /Applications is the path
|
|
if [[ $ffi ]]; then
|
|
installer_directory="/Applications"
|
|
fi
|
|
|
|
# ensure installer_directory (--path) and workdir exists
|
|
if [[ ! -d "$installer_directory" ]]; then
|
|
echo " [$script_name] Making installer directory at $installer_directory"
|
|
/bin/mkdir -p "$installer_directory"
|
|
fi
|
|
if [[ ! -d "$workdir" ]]; then
|
|
echo " [$script_name] Making working directory at $workdir"
|
|
/bin/mkdir -p "$workdir"
|
|
fi
|
|
|
|
# all output from now on is written also to a log file
|
|
LOG_FILE="$workdir/erase-install.log"
|
|
exec > >(tee "${LOG_FILE}") 2>&1
|
|
|
|
# ensure computer does not go to sleep while running this script
|
|
pid=$$
|
|
echo " [$script_name] Caffeinating this script (pid=$pid)"
|
|
/usr/bin/caffeinate -dimsu -w $pid &
|
|
caffeinate_pid=$!
|
|
|
|
# bundled python directory
|
|
relocatable_python_path="$workdir/Python.framework/Versions/Current/bin/python3"
|
|
|
|
# place any extra packages that should be installed as part of the erase-install into this folder. The script will find them and install.
|
|
# https://derflounder.wordpress.com/2017/09/26/using-the-macos-high-sierra-os-installers-startosinstall-tool-to-install-additional-packages-as-post-upgrade-tasks/
|
|
extras_directory="$workdir/extras"
|
|
|
|
# variable to prevent installinstallmacos getting downloaded twice
|
|
iim_downloaded=0
|
|
|
|
# if getting a list from softwareupdate then we don't need to make any OS checks
|
|
if [[ $list_installers ]]; then
|
|
swu_list_full_installers
|
|
echo
|
|
exit
|
|
fi
|
|
|
|
# some options vary based on installer versions
|
|
system_version=$( /usr/bin/sw_vers -productVersion )
|
|
system_os_major=$( echo "$system_version" | cut -d '.' -f 1 )
|
|
system_os_version=$( echo "$system_version" | cut -d '.' -f 2 )
|
|
|
|
# check for power and drive space if invoking erase or reinstall options
|
|
if [[ $erase == "yes" || $reinstall == "yes" ]]; then
|
|
# announce that the Test Run mode is implemented
|
|
if [[ $test_run == "yes" ]]; then
|
|
echo
|
|
echo "*** TEST-RUN ONLY! ***"
|
|
echo "* This script will perform all tasks up to the point of erase or reinstall,"
|
|
echo "* but will not actually erase or reinstall."
|
|
echo "* Remove the --test-run argument to perform the erase or reinstall."
|
|
echo "**********************"
|
|
echo
|
|
fi
|
|
|
|
# get DEPNotify if specified
|
|
if [[ $get_depnotify_app == "yes" ]]; then
|
|
get_depnotify
|
|
fi
|
|
|
|
# check there is enough space
|
|
check_free_space
|
|
|
|
# check for power
|
|
[[ "$check_power" == "yes" ]] && check_power_status
|
|
fi
|
|
|
|
# Look for the installer, download it if it is not present
|
|
echo " [$script_name] Looking for existing installer app or pkg"
|
|
find_existing_installer
|
|
|
|
if [[ $invalid_installer_found == "yes" && -d "$working_macos_app" && $replace_invalid_installer == "yes" ]]; then
|
|
overwrite_existing_installer
|
|
elif [[ $invalid_installer_found == "yes" && ($pkg_installer && ! -f "$working_installer_pkg") && $replace_invalid_installer == "yes" ]]; then
|
|
echo " [$script_name] Deleting invalid installer package"
|
|
rm -f "$working_macos_app"
|
|
if [[ $clear_cache == "yes" ]]; then
|
|
echo " [$script_name] Quitting script as --clear-cache-only option was selected."
|
|
# kill caffeinate
|
|
kill_process "caffeinate"
|
|
exit
|
|
fi
|
|
elif [[ $update_installer == "yes" && -d "$working_macos_app" && $overwrite != "yes" ]]; then
|
|
echo " [$script_name] Checking for newer installer"
|
|
check_newer_available
|
|
if [[ $newer_build_found == "yes" ]]; then
|
|
echo " [$script_name] Newer installer found so overwriting existing installer"
|
|
overwrite_existing_installer
|
|
elif [[ $clear_cache == "yes" ]]; then
|
|
echo " [$script_name] Quitting script as --clear-cache-only option was selected."
|
|
# kill caffeinate
|
|
kill_process "caffeinate"
|
|
exit
|
|
fi
|
|
elif [[ $update_installer == "yes" && ($pkg_installer && -f "$working_installer_pkg") && $overwrite != "yes" ]]; then
|
|
echo " [$script_name] Checking for newer installer package"
|
|
check_newer_available
|
|
if [[ $newer_build_found == "yes" ]]; then
|
|
echo " [$script_name] Newer installer found so deleting existing installer package"
|
|
rm -f "$working_macos_app"
|
|
fi
|
|
if [[ $clear_cache == "yes" ]]; then
|
|
echo " [$script_name] Quitting script as --clear-cache-only option was selected."
|
|
# kill caffeinate
|
|
kill_process "caffeinate"
|
|
exit
|
|
fi
|
|
elif [[ $overwrite == "yes" && -d "$working_macos_app" && ! $list ]]; then
|
|
overwrite_existing_installer
|
|
elif [[ $overwrite == "yes" && ($pkg_installer && -f "$working_installer_pkg") && ! $list ]]; then
|
|
echo " [$script_name] Deleting invalid installer package"
|
|
rm -f "$working_installer_pkg"
|
|
if [[ $clear_cache == "yes" ]]; then
|
|
echo " [$script_name] Quitting script as --clear-cache-only option was selected."
|
|
# kill caffeinate
|
|
kill_process "caffeinate"
|
|
exit
|
|
fi
|
|
elif [[ $invalid_installer_found == "yes" && ($erase == "yes" || $reinstall == "yes") && $skip_validation != "yes" ]]; then
|
|
echo " [$script_name] ERROR: Invalid installer is present. Run with --overwrite option to ensure that a valid installer is obtained."
|
|
# kill caffeinate
|
|
kill_process "caffeinate"
|
|
exit 1
|
|
fi
|
|
|
|
# Silicon Macs require a username and password to run startosinstall
|
|
# We therefore need to be logged in to proceed, if we are going to erase or reinstall
|
|
# This goes before the download so users aren't waiting for the prompt for username
|
|
arch=$(/usr/bin/arch)
|
|
if [[ "$arch" == "arm64" && ($erase == "yes" || $reinstall == "yes") ]]; then
|
|
if ! pgrep -q Finder ; then
|
|
echo " [$script_name] ERROR! The startosinstall binary requires a user to be logged in."
|
|
echo
|
|
# kill caffeinate
|
|
kill_process "caffeinate"
|
|
exit 1
|
|
fi
|
|
get_user_details
|
|
fi
|
|
|
|
if [[ (! -d "$working_macos_app" && ! -f "$working_installer_pkg") || $list ]]; then
|
|
# if erasing or reinstalling, open a dialog to state that the download is taking place.
|
|
if [[ $erase == "yes" || $reinstall == "yes" ]]; then
|
|
if [[ $use_depnotify == "yes" ]]; then
|
|
echo " [$script_name] Opening DEPNotify download message (language=$user_language)"
|
|
dn_title="${!dialog_dl_title}"
|
|
dn_desc="${!dialog_dl_desc}"
|
|
dn_status="${!dialog_dl_title}"
|
|
dn_icon="$dialog_dl_icon"
|
|
dep_notify
|
|
elif [[ -f "$jamfHelper" ]]; then
|
|
echo " [$script_name] Opening jamfHelper download message (language=$user_language)"
|
|
"$jamfHelper" -windowType hud -windowPosition ul -title "${!dialog_dl_title}" -alignHeading center -alignDescription left -description "${!dialog_dl_desc}" -lockHUD -icon "$dialog_dl_icon" -iconSize 100 &
|
|
else
|
|
echo " [$script_name] Opening osascript dialog (language=$user_language)"
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "${!dialog_dl_title}" "${!dialog_dl_desc}" "OK" 2 &
|
|
fi
|
|
fi
|
|
|
|
# now run installinstallmacos or softwareupdate
|
|
if [[ $ffi ]]; then
|
|
if [[ ($system_os_major -eq 10 && $system_os_version -ge 15) || $system_os_major -ge 11 ]]; then
|
|
echo " [$script_name] OS version is $system_os_major.$system_os_version so can run with --fetch-full-installer option"
|
|
if [[ $use_depnotify == "yes" ]]; then
|
|
# display progress if DEPNotify used
|
|
dep_notify_progress fetch-full-installer >/dev/null 2>&1 &
|
|
echo $! >> /tmp/depnotify_progress_pid
|
|
fi
|
|
swu_fetch_full_installer
|
|
else
|
|
echo " [$script_name] OS version is $system_os_major.$system_os_version so cannot run with --fetch-full-installer option. Falling back to installinstallmacos.py"
|
|
if [[ $use_depnotify == "yes" ]]; then
|
|
# display progress if DEPNotify used
|
|
dep_notify_progress installinstallmacos >/dev/null 2>&1 &
|
|
echo $! >> /tmp/depnotify_progress_pid
|
|
fi
|
|
run_installinstallmacos
|
|
fi
|
|
else
|
|
if [[ $use_depnotify == "yes" ]]; then
|
|
# display progress if DEPNotify used
|
|
dep_notify_progress installinstallmacos >/dev/null 2>&1 &
|
|
echo $! >> /tmp/depnotify_progress_pid
|
|
fi
|
|
run_installinstallmacos
|
|
fi
|
|
fi
|
|
|
|
if [[ -d "$working_macos_app" ]]; then
|
|
echo " [$script_name] Installer is at: $working_macos_app"
|
|
fi
|
|
|
|
# Move to $installer_directory if move_to_applications_folder flag is included
|
|
# Not allowed for fetch_full_installer option
|
|
if [[ $move == "yes" && ! $ffi ]]; then
|
|
echo " [$script_name] Invoking --move option"
|
|
if [[ $use_depnotify == "yes" ]]; then
|
|
echo "Status: Moving installer to Applications folder" >> $depnotify_log
|
|
fi
|
|
if [[ -f "$working_installer_pkg" ]]; then
|
|
unpack_pkg_to_applications_folder
|
|
else
|
|
move_to_applications_folder
|
|
fi
|
|
fi
|
|
|
|
# Once finished downloading (and optionally moving), kill the jamfHelper or DEPNotify
|
|
if [[ $use_depnotify == "yes" ]]; then
|
|
echo " [$script_name] Closing DEPNotify download message (language=$user_language)"
|
|
dep_notify_quit
|
|
elif [[ -f "$jamfHelper" ]]; then
|
|
echo " [$script_name] Closing jamfHelper download message (language=$user_language)"
|
|
kill_process "jamfHelper"
|
|
fi
|
|
|
|
|
|
if [[ $erase != "yes" && $reinstall != "yes" ]]; then
|
|
# Unmount the dmg
|
|
if [[ ! $ffi ]]; then
|
|
existing_installer=$(find /Volumes/*macOS* -maxdepth 2 -type d -name "Install*.app" -print -quit 2>/dev/null )
|
|
if [[ -d "$existing_installer" ]]; then
|
|
echo " [$script_name] Mounted installer will be unmounted: $existing_installer"
|
|
existing_installer_mount_point=$(echo "$existing_installer" | cut -d/ -f 1-3)
|
|
diskutil unmount force "$existing_installer_mount_point"
|
|
fi
|
|
fi
|
|
# Clear the working directory
|
|
echo " [$script_name] Cleaning working directory '$workdir/content'"
|
|
rm -rf "$workdir/content"
|
|
|
|
# kill caffeinate
|
|
kill_process "caffeinate"
|
|
echo
|
|
exit
|
|
fi
|
|
|
|
## Steps beyond here are to run startosinstall
|
|
|
|
echo
|
|
if [[ -f "$working_installer_pkg" ]]; then
|
|
# if we still have a packege we need to move it before we can install it
|
|
unpack_pkg_to_applications_folder
|
|
fi
|
|
|
|
if [[ ! -d "$working_macos_app" ]]; then
|
|
echo " [$script_name] ERROR: Can't find the installer! "
|
|
# kill caffeinate
|
|
kill_process "caffeinate"
|
|
exit 1
|
|
fi
|
|
[[ $erase == "yes" ]] && echo " [$script_name] WARNING! Running $working_macos_app with eraseinstall option"
|
|
[[ $reinstall == "yes" ]] && echo " [$script_name] WARNING! Running $working_macos_app with reinstall option"
|
|
echo
|
|
|
|
# If configured to do so, display a confirmation window to the user. Note: default button is cancel
|
|
if [[ $confirm == "yes" ]]; then
|
|
confirm
|
|
fi
|
|
|
|
# determine SIP status, as the volume is required if SIP is disabled
|
|
/usr/bin/csrutil status | sed -n 1p | grep -q 'disabled' && sip="disabled" || sip="enabled"
|
|
|
|
# set install argument for erase option
|
|
install_args=()
|
|
if [[ $erase == "yes" ]]; then
|
|
install_args+=("--eraseinstall")
|
|
elif [[ $reinstall == "yes" && $sip == "disabled" ]]; then
|
|
volname=$(diskutil info -plist / | grep -A1 "VolumeName" | tail -n 1 | awk -F '<string>|</string>' '{ print $2; exit; }')
|
|
install_args+=("--volume")
|
|
install_args+=("/Volumes/$volname")
|
|
fi
|
|
|
|
# check for packages then add install_package_list to end of command line (empty if no packages found)
|
|
find_extra_packages
|
|
|
|
# some cli options vary based on installer versions
|
|
installer_build=$( /usr/bin/defaults read "$working_macos_app/Contents/Info.plist" DTSDKBuild )
|
|
installer_darwin_version=${installer_build:0:2}
|
|
# add --preservecontainer to the install arguments if specified (for macOS 10.14 (Darwin 18) and above)
|
|
if [[ $installer_darwin_version -ge 18 && $preservecontainer == "yes" ]]; then
|
|
install_args+=("--preservecontainer")
|
|
fi
|
|
|
|
# OS X 10.12 (Darwin 16) requires the --applicationpath option
|
|
if [[ $installer_darwin_version -le 16 ]]; then
|
|
install_args+=("--applicationpath")
|
|
install_args+=("$working_macos_app")
|
|
fi
|
|
|
|
# macOS 11 (Darwin 20) and above requires the --allowremoval option
|
|
if [[ $installer_darwin_version -ge 20 ]]; then
|
|
install_args+=("--allowremoval")
|
|
fi
|
|
|
|
# macOS 10.15 (Darwin 19) and above requires the --forcequitapps options
|
|
if [[ $installer_darwin_version -ge 19 ]]; then
|
|
install_args+=("--forcequitapps")
|
|
fi
|
|
|
|
# icons for Jamf Helper erase and re-install windows
|
|
dialog_erase_icon="$working_macos_app/Contents/Resources/InstallAssistant.icns"
|
|
dialog_reinstall_icon="$working_macos_app/Contents/Resources/InstallAssistant.icns"
|
|
|
|
# if no_fs is set, show a utility window instead of the full screen display (for test purposes)
|
|
[[ $no_fs == "yes" ]] && window_type="utility" || window_type="fs"
|
|
|
|
# dialogs for reinstallation
|
|
if [[ $erase == "yes" ]]; then
|
|
if [[ $use_depnotify == "yes" ]]; then
|
|
echo " [$script_name] Opening DEPNotify full screen message (language=$user_language)"
|
|
dn_title="${!dialog_erase_title}"
|
|
dn_desc="${!dialog_erase_desc}"
|
|
dn_status="${!dialog_reinstall_status}"
|
|
dn_icon="$dialog_erase_icon"
|
|
dep_notify
|
|
dep_notify_progress startosinstall >/dev/null 2>&1 &
|
|
echo $! >> /tmp/depnotify_progress_pid
|
|
PID=$(pgrep -l "DEPNotify" | cut -d " " -f1)
|
|
elif [[ -f "$jamfHelper" ]]; then
|
|
echo " [$script_name] Opening jamfHelper full screen message (language=$user_language)"
|
|
"$jamfHelper" -windowType $window_type -title "${!dialog_erase_title}" -heading "${!dialog_erase_title}" -description "${!dialog_erase_desc}" -icon "$dialog_erase_icon" &
|
|
PID=$!
|
|
else
|
|
echo " [$script_name] Opening osascript dialog (language=$user_language)"
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "${!dialog_erase_desc}" "" "OK" stop &
|
|
PID=$!
|
|
fi
|
|
|
|
# dialogs for reinstallation
|
|
elif [[ $reinstall == "yes" ]]; then
|
|
if [[ $use_depnotify == "yes" ]]; then
|
|
echo " [$script_name] Opening DEPNotify full screen message (language=$user_language)"
|
|
dn_title="${!dialog_reinstall_title}"
|
|
dn_desc="${!dialog_reinstall_desc}"
|
|
dn_status="${!dialog_reinstall_status}"
|
|
dn_icon="$dialog_reinstall_icon"
|
|
dep_notify
|
|
dep_notify_progress startosinstall >/dev/null 2>&1 &
|
|
echo $! >> /tmp/depnotify_progress_pid
|
|
PID=$(pgrep -l "DEPNotify" | cut -d " " -f1)
|
|
elif [[ -f "$jamfHelper" ]]; then
|
|
echo " [$script_name] Opening jamfHelper full screen message (language=$user_language)"
|
|
"$jamfHelper" -windowType $window_type -title "${!dialog_reinstall_title}" -heading "${!dialog_reinstall_heading}" -description "${!dialog_reinstall_desc}" -icon "$dialog_reinstall_icon" &
|
|
PID=$!
|
|
else
|
|
echo " [$script_name] Opening osascript dialog (language=$user_language)"
|
|
# open_osascript_dialog syntax: title, message, button1, icon
|
|
open_osascript_dialog "${!dialog_reinstall_desc}" "" "OK" stop &
|
|
PID=$!
|
|
fi
|
|
fi
|
|
|
|
# set launchdaemon to remove $workdir if $cleanup_after_use is set
|
|
if [[ $cleanup_after_use != "" ]]; then
|
|
echo " [$script_name] Writing LaunchDaemon which will remove $workdir at next boot"
|
|
fi
|
|
|
|
# run an arbitrary command if preinstall_command is set
|
|
if [[ $preinstall_command != "" ]]; then
|
|
echo " [$script_name] Now running arbitrary command: $preinstall_command"
|
|
fi
|
|
|
|
# now actually run startosinstall
|
|
if [[ $test_run != "yes" ]]; then
|
|
if [[ $preinstall_command != "" ]]; then
|
|
# run an arbitrary command if preinstall_command is set
|
|
$preinstall_command
|
|
fi
|
|
if [[ $cleanup_after_use != "" ]]; then
|
|
# set launchdaemon to remove $workdir if $cleanup_after_use is set
|
|
create_launchdaemon_to_remove_workdir
|
|
fi
|
|
if [ "$arch" == "arm64" ]; then
|
|
# startosinstall --eraseinstall may fail if a user was converted to admin using the Privileges app
|
|
# this command supposedly fixes this problem (experimental!)
|
|
if [[ "$erase" == "yes" ]]; then
|
|
echo " [$script_name] updating preboot files (takes a few seconds)..."
|
|
/usr/sbin/diskutil apfs updatepreboot / > /dev/null
|
|
fi
|
|
# shellcheck disable=SC2086
|
|
"$working_macos_app/Contents/Resources/startosinstall" "${install_args[@]}" --pidtosignal $PID --agreetolicense --nointeraction --stdinpass --user "$account_shortname" "${install_package_list[@]}" <<< $account_password
|
|
else
|
|
"$working_macos_app/Contents/Resources/startosinstall" "${install_args[@]}" --pidtosignal $PID --agreetolicense --nointeraction "${install_package_list[@]}"
|
|
fi
|
|
# kill Self Service if running
|
|
kill_process "Self Service"
|
|
else
|
|
echo " [$script_name] Run without '--test-run' to run this command:"
|
|
if [ "$arch" == "arm64" ]; then
|
|
echo "$working_macos_app/Contents/Resources/startosinstall" "${install_args[@]}" "--pidtosignal $PID --pidtosignal $caffeinate_pid --agreetolicense --nointeraction --stdinpass --user" "$account_shortname" "${install_package_list[@]}" "<<< [PASSWORD REDACTED]"
|
|
else
|
|
echo "$working_macos_app/Contents/Resources/startosinstall" "${install_args[@]}" --pidtosignal $PID --pidtosignal $caffeinate_pid --agreetolicense --nointeraction "${install_package_list[@]}"
|
|
fi
|
|
sleep 120
|
|
fi |