#!/bin/bash -
# ------------------------------------------------------------------------------
# deploy-macos.sh
# ===============
#
# Scope macOS
# Copyright (C) 2024 by RaySoft, Zurich, Switzerland
# License GNU General Public License (GPL) 2.0
# https://www.gnu.org/licenses/gpl2.txt
#
# ------------------------------------------------------------------------------
# Sources
# -------
#
# https://github.com/mathiasbynens/dotfiles/blob/master/.macos
# https://github.com/pirate/OS-X-Security-and-Privacy-Guide/blob/master/README.md
#
# ------------------------------------------------------------------------------
set -o 'errexit' -o 'noglob' -o 'nounset' -o 'pipefail' # -o 'xtrace'
# ------------------------------------------------------------------------------
FQHN='aluminium.raysoft.loc'
USERS=('alex')
# ------------------------------------------------------------------------------
BASH=('/bin/bash' '-c')
CAT=('/usr/local/bin/gcat')
CHFLAGS=('/usr/bin/chflags')
CHSH=('/usr/bin/chsh')
DEFAULTS=('/usr/bin/defaults')
HOSTNAME=('/bin/hostname')
KILL=('/usr/local/bin/gkill')
KILLALL=('/usr/bin/killall')
LAUNCHCTL=('/bin/launchctl')
OPEN=('/usr/bin/open')
SCUTIL=('/usr/sbin/scutil')
SLEEP=('/usr/local/bin/gsleep')
SUDO=('/usr/bin/sudo')
SYSTEMSETUP=('/usr/sbin/systemsetup')
TMUTIL=('/usr/bin/tmutil')
# ------------------------------------------------------------------------------
# Ask for the administrator password upfront
"${SUDO[@]}" -v
# Keep-alive: update existing `sudo` time stamp until this script
# has finished
while true; do
"${SUDO[@]}" -n true
"${SLEEP[@]}" 60
"${KILL[@]}" -0 "$$" || exit
done 2>'/dev/null' &
# ------------------------------------------------------------------------------
# Set shell
# ---------
"${SUDO[@]}" "${BASH[@]}" "echo '/bin/bash' >>'/etc/shells'"
"${SUDO[@]}" "${CHSH[@]}" -s '/bin/bash' "root"
for user in "${USERS[@]}"; do
"${SUDO[@]}" "${CHSH[@]}" -s '/bin/bash' "${user}"
done
# ------------------------------------------------------------------------------
# General
# -------
# Set the hostname
if [[ "$("${HOSTNAME[@]}")" != "${FQHN}" ]]; then
"${SUDO[@]}" "${SCUTIL[@]}" --set 'HostName' "${FQHN}"
"${SUDO[@]}" "${SCUTIL[@]}" --set 'ComputerName' "${FQHN}"
"${SUDO[@]}" "${SCUTIL[@]}" --set 'LocalHostName' "${FQHN%%.*}"
fi
# Set the users' umask
"${SUDO[@]}" "${LAUNCHCTL[@]}" config user umask 0077
# Show the ~/Library folders
for user in "${USERS[@]}"; do
"${SUDO[@]}" "${CHFLAGS[@]}" nohidden "/Users/${user}/Library"
done
# Show the /Volumes folder
"${SUDO[@]}" "${CHFLAGS[@]}" nohidden "/Volumes"
# Set Sudoers list
"${SUDO[@]}" "${BASH[@]}" "${CAT[*]} <<-EOF >'/private/etc/sudoers.d/rays_sudoers'
Cmnd_Alias CHMOD = /usr/local/opt/coreutils/libexec/gnubin/chmod, /usr/local/bin/gchmod, /bin/chmod
Cmnd_Alias CHOWN = /usr/local/opt/coreutils/libexec/gnubin/chown, /usr/local/bin/gchown, /usr/sbin/chown
Cmnd_Alias DISKUTIL = /usr/sbin/diskutil
Cmnd_Alias DSCACHEUTIL = /usr/bin/dscacheutil
Cmnd_Alias FIND = /usr/local/opt/coreutils/libexec/gnubin/find, /usr/local/bin/gfind, /usr/bin/find
Cmnd_Alias KILLALL = /usr/bin/killall
Cmnd_Alias MDUTIL = /usr/bin/mdutil
Cmnd_Alias RM = /usr/local/opt/coreutils/libexec/gnubin/rm, /usr/local/bin/grm, /bin/rm
alex ALL=(ALL) NOPASSWD: CHMOD, CHOWN, DISKUTIL, DSCACHEUTIL, FIND, KILLALL, MDUTIL, RM
alex ALL=(ALL) NOPASSWD:SETENV: CHMOD, CHOWN, RM
EOF"
# Expand save panel by default
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'NSNavPanelExpandedStateForSaveMode' \
-boolean true
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'NSNavPanelExpandedStateForSaveMode2' \
-boolean true
# Expand print panel by default
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'PMPrintingExpandedStateForPrint' \
-boolean true
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'PMPrintingExpandedStateForPrint2' \
-boolean true
# Save to disk (not to iCloud) by default
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'NSDocumentSaveNewDocumentsToCloud' \
-boolean false
# Automatically quit printer app once the print jobs complete
"${DEFAULTS[@]}" write 'com.apple.print.PrintingPrefs' 'Quit When Finished' \
-boolean true
# Disable Resume system-wide
"${DEFAULTS[@]}" write 'com.apple.systempreferences' 'NSQuitAlwaysKeepsWindows' \
-boolean false
# Restart automatically if the computer freezes
"${SUDO[@]}" "${SYSTEMSETUP[@]}" -setrestartfreeze 'on'
# Disable automatic capitalization
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'NSAutomaticCapitalizationEnabled' \
-boolean false
# Disable smart dashes
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'NSAutomaticDashSubstitutionEnabled' \
-boolean false
# Disable automatic period substitution
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'NSAutomaticPeriodSubstitutionEnabled' \
-boolean false
# Disable smart quotes
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'NSAutomaticQuoteSubstitutionEnabled' \
-boolean false
# Disable auto-correct
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'NSAutomaticSpellingCorrectionEnabled' \
-boolean false
# ------------------------------------------------------------------------------
# Firewall
# --------
# Enable firewall
"${SUDO[@]}" "${DEFAULTS[@]}" write '/Library/Preferences/com.apple.alf' 'globalstate' \
-boolean true
# Enable firewall logging
"${SUDO[@]}" "${DEFAULTS[@]}" write '/Library/Preferences/com.apple.alf' 'loggingenabled' \
-boolean false
# Eneable firewall stealth mode to help protecting from scanning
"${SUDO[@]}" "${DEFAULTS[@]}" write '/Library/Preferences/com.apple.alf' 'stealthenabled' \
-boolean false
# Disable incoming connections for signed software
"${SUDO[@]}" "${DEFAULTS[@]}" write '/Library/Preferences/com.apple.alf' 'allowsignedenabled' \
-boolean false
# Disable captive portals
"${SUDO[@]}" "${DEFAULTS[@]}" write '/Library/Preferences/SystemConfiguration/com.apple.captive.control' 'Active' \
-boolean false
# ------------------------------------------------------------------------------
# Trackpad, keyboard and Bluetooth accessories
# --------------------------------------------
# Disable 'natural' (Lion-style) scrolling
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'com.apple.swipescrolldirection' \
-boolean false
# Increase sound quality for Bluetooth headphones/headsets
"${DEFAULTS[@]}" write 'com.apple.BluetoothAudioAgent' 'Apple Bitpool Min (editable)' \
-integer 40
# Enable keyboard access everywhere
# (e.g. enable Tab in modal dialogs)
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'AppleKeyboardUIMode' \
-integer 3
# ------------------------------------------------------------------------------
# Screen
# ------
# Require password immediately after sleep or screen saver begins
"${DEFAULTS[@]}" write 'com.apple.screensaver' 'askForPassword' \
-integer 1
"${DEFAULTS[@]}" write 'com.apple.screensaver' 'askForPasswordDelay' \
-integer 0
# Save screenshots to the desktop
"${DEFAULTS[@]}" write 'com.apple.screencapture' 'location' \
-string "${HOME}/Desktop"
# Save screenshots in PNG format (other options: BMP, GIF, JPG, PDF, TIFF)
"${DEFAULTS[@]}" write 'com.apple.screencapture' 'type' \
-string 'png'
# Disable shadow in screenshots
"${DEFAULTS[@]}" write 'com.apple.screencapture' 'disable-shadow' \
-boolean true
# ------------------------------------------------------------------------------
# Finder
# ------
# Disable all Finder sounds
"${DEFAULTS[@]}" write 'com.apple.finder' 'FinderSounds' \
-boolean false
# Set $HOME as the default location for new Finder windows
"${DEFAULTS[@]}" write 'com.apple.finder' 'NewWindowTarget' \
-string "PfLo"
"${DEFAULTS[@]}" write 'com.apple.finder' 'NewWindowTargetPath' \
-string "file://${HOME}"
# Show icons for hard drives, servers and removable media on the desktop
"${DEFAULTS[@]}" write 'com.apple.finder' 'ShowExternalHardDrivesOnDesktop' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.finder' 'ShowHardDrivesOnDesktop' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.finder' 'ShowMountedServersOnDesktop' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.finder' 'ShowRemovableMediaOnDesktop' \
-boolean false
# Show all filename extensions
"${DEFAULTS[@]}" write 'NSGlobalDomain' 'AppleShowAllExtensions' \
-boolean true
# Show side bar
"${DEFAULTS[@]}" write 'com.apple.finder' 'ShowSidebar' \
-boolean true
# Show status bar
"${DEFAULTS[@]}" write 'com.apple.finder' 'ShowStatusBar' \
-boolean true
# Show path bar
"${DEFAULTS[@]}" write 'com.apple.finder' 'ShowPathBar' \
-boolean true
"${DEFAULTS[@]}" write 'com.apple.Finder' 'NSToolbarTitleViewRolloverDelay' \
-float 0
# Show preview pane
"${DEFAULTS[@]}" write 'com.apple.finder' 'ShowPreviewPane' \
-boolean false
# Display full POSIX path as Finder window title
"${DEFAULTS[@]}" write 'com.apple.finder' '_FXShowPosixPathInTitle' \
-boolean true
# Keep folders on top when sorting by name
"${DEFAULTS[@]}" write 'com.apple.finder' '_FXSortFoldersFirst' \
-boolean false
# When performing a search, search the current folder by default
"${DEFAULTS[@]}" write 'com.apple.finder' 'FXDefaultSearchScope' \
-string 'SCcf'
# Disable warning when changing file extensions in Finder
"${DEFAULTS[@]}" write 'com.apple.finder' 'FXEnableExtensionChangeWarning' \
-boolean true
# Avoid creating .DS_Store files on network volumes & USB storages
"${DEFAULTS[@]}" write 'com.apple.desktopservices' 'DSDontWriteNetworkStores' \
-boolean true
"${DEFAULTS[@]}" write 'com.apple.desktopservices' 'DSDontWriteUSBStores' \
-boolean true
# Use list view in all Finder windows by default
# Four-letter codes for the other view modes: icnv, clmv, glyv
"${DEFAULTS[@]}" write 'com.apple.finder' 'FXPreferredViewStyle' \
-string 'Nlsv'
# Disable the warning before emptying the Trash
"${DEFAULTS[@]}" write 'com.apple.finder' 'WarnOnEmptyTrash' \
-boolean false
# Expand the following File Info panes:
# "General", "Open with", and "Sharing & Permissions"
"${DEFAULTS[@]}" write 'com.apple.finder' 'FXInfoPanesExpanded' \
-dict 'General' -boolean true \
'MetaData' -boolean false \
'Name' -boolean false \
'Comments' -boolean false \
'OpenWith' -boolean true \
'Preview' -boolean false \
'Privileges' -boolean true
# ------------------------------------------------------------------------------
# Dock
# ----
# Don't automatically rearrange Spaces based on most recent use
"${DEFAULTS[@]}" write 'com.apple.dock' 'mru-spaces' \
-boolean false
# Show small and more icons in the Lauchpad
"${DEFAULTS[@]}" write 'com.apple.dock' 'springboard-columns' \
-integer 15
"${DEFAULTS[@]}" write 'com.apple.dock' 'springboard-rows' \
-integer 10
# WARNING: The following commands 'hide' the Dock
# Automatically hide and show the Dock
"${DEFAULTS[@]}" write 'com.apple.dock' 'autohide' \
-boolean true
"${DEFAULTS[@]}" write 'com.apple.dock' 'autohide-delay' \
-float 0
"${DEFAULTS[@]}" write 'com.apple.dock' 'autohide-time-modifier' \
-float 0
"${DEFAULTS[@]}" write 'com.apple.dock' 'orientation' \
-string 'right'
"${DEFAULTS[@]}" write 'com.apple.dock' 'show-recents' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.dock' 'tilesize' \
-integer 1
# ------------------------------------------------------------------------------
# Safari
# ------
# Privacy: don’t send search queries to Apple
"${DEFAULTS[@]}" write 'com.apple.Safari' 'UniversalSearchEnabled' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.Safari' 'SuppressSearchSuggestions' \
-boolean true
# Show the full URL in the address bar (note: this still hides the scheme)
"${DEFAULTS[@]}" write 'com.apple.Safari' 'ShowFullURLInSmartSearchField' \
-boolean true
# Set Safari’s home page to 'about:blank' for faster loading
"${DEFAULTS[@]}" write 'com.apple.Safari' 'HomePage' \
-string 'about:blank'
# Prevent Safari from opening 'safe' files automatically after downloading
"${DEFAULTS[@]}" write 'com.apple.Safari' 'AutoOpenSafeDownloads' \
-boolean false
# Allow hitting the Backspace key to go to the previous page in history
"${DEFAULTS[@]}" write 'com.apple.Safari' \
'com.apple.Safari.ContentPageGroupIdentifier.WebKit2BackspaceKeyNavigationEnabled' \
-boolean true
# Hide Safari’s bookmarks bar by default
"${DEFAULTS[@]}" write 'com.apple.Safari' 'ShowFavoritesBar' \
-boolean false
# Hide Safari’s sidebar in Top Sites
"${DEFAULTS[@]}" write 'com.apple.Safari' 'ShowSidebarInTopSites' \
-boolean false
# Disable Safari’s thumbnail cache for History and Top Sites
"${DEFAULTS[@]}" write 'com.apple.Safari' 'DebugSnapshotsUpdatePolicy' \
-integer 2
# Enable continuous spellchecking
"${DEFAULTS[@]}" write 'com.apple.Safari' 'WebContinuousSpellCheckingEnabled' \
-boolean true
# Disable auto-correct
"${DEFAULTS[@]}" write 'com.apple.Safari' 'WebAutomaticSpellingCorrectionEnabled' \
-boolean false
# Disable AutoFill
"${DEFAULTS[@]}" write 'com.apple.Safari' 'AutoFillFromAddressBook' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.Safari' 'AutoFillPasswords' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.Safari' 'AutoFillCreditCardData' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.Safari' 'AutoFillMiscellaneousForms' \
-boolean false
# Warn about fraudulent websites
"${DEFAULTS[@]}" write 'com.apple.Safari' 'WarnAboutFraudulentWebsites' \
-boolean true
# Disable Java
"${DEFAULTS[@]}" write 'com.apple.Safari' 'WebKitJavaEnabled' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.Safari' \
'com.apple.Safari.ContentPageGroupIdentifier.WebKit2JavaEnabled' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.Safari' \
'com.apple.Safari.ContentPageGroupIdentifier.WebKit2JavaEnabledForLocalFiles' \
-boolean false
# Block pop-up windows
"${DEFAULTS[@]}" write 'com.apple.Safari' 'WebKitJavaScriptCanOpenWindowsAutomatically' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.Safari' \
'com.apple.Safari.ContentPageGroupIdentifier.WebKit2JavaScriptCanOpenWindowsAutomatically' \
-boolean false
# Disable auto-playing video
"${DEFAULTS[@]}" write 'com.apple.Safari' 'WebKitMediaPlaybackAllowsInline' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.SafariTechnologyPreview' 'WebKitMediaPlaybackAllowsInline' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.Safari' \
'com.apple.Safari.ContentPageGroupIdentifier.WebKit2AllowsInlineMediaPlayback' \
-boolean false
"${DEFAULTS[@]}" write 'com.apple.SafariTechnologyPreview' \
'com.apple.Safari.ContentPageGroupIdentifier.WebKit2AllowsInlineMediaPlayback' \
-boolean false
# Enable “Do Not Track”
"${DEFAULTS[@]}" write 'com.apple.Safari' 'SendDoNotTrackHTTPHeader' \
-boolean true
# Update extensions automatically
"${DEFAULTS[@]}" write 'com.apple.Safari' 'InstallExtensionUpdatesAutomatically' \
-boolean true
# ------------------------------------------------------------------------------
# Mail
# ----
# Display emails in threaded mode, sorted by date (newest at the top)
"${DEFAULTS[@]}" write 'com.apple.mail' 'DraftsViewerAttributes' \
-dict-add 'DisplayInThreadedMode' -string 'yes'
"${DEFAULTS[@]}" write 'com.apple.mail' 'DraftsViewerAttributes' \
-dict-add 'SortedDescending' -string 'no'
"${DEFAULTS[@]}" write 'com.apple.mail' 'DraftsViewerAttributes' \
-dict-add 'SortOrder' -string 'received-date'
# Disable inline attachments (just show the icons)
"${DEFAULTS[@]}" write 'com.apple.mail' 'DisableInlineAttachmentViewing' \
-boolean true
# ------------------------------------------------------------------------------
# iTerm 2
# -------
# Don’t display the annoying prompt when quitting iTerm
"${DEFAULTS[@]}" write 'com.googlecode.iterm2' 'PromptOnQuit' \
-boolean false
# ------------------------------------------------------------------------------
# Time Machine
# ------------
# Prevent Time Machine from prompting to use new hard drives as backup volume
"${DEFAULTS[@]}" write 'com.apple.TimeMachine' 'DoNotOfferNewDisksForBackup' \
-boolean true
# Disable Time Machine
"${SUDO[@]}" "${TMUTIL[@]}" 'disable'
# ------------------------------------------------------------------------------
# Preview, TextEdit and Disk Utility
# ----------------------------------
# Stop Preview from Auto-Restoring what you left open
"${DEFAULTS[@]}" write 'com.apple.Preview' 'NSQuitAlwaysKeepsWindows' \
-boolean false
# Work plain text
"${DEFAULTS[@]}" write 'com.apple.TextEdit' 'RichText' \
-integer 0
# Open and save files as UTF-8 in TextEdit
"${DEFAULTS[@]}" write 'com.apple.TextEdit' 'PlainTextEncoding' \
-integer 4
"${DEFAULTS[@]}" write 'com.apple.TextEdit' 'PlainTextEncodingForWrite' \
-integer 4
# Enable the debug menu in Disk Utility
"${DEFAULTS[@]}" write 'com.apple.DiskUtility' 'DUDebugMenuEnabled' \
-boolean true
"${DEFAULTS[@]}" write 'com.apple.DiskUtility' 'advanced-image-options' \
-boolean true
# ------------------------------------------------------------------------------
# Mac App Store
# -------------
# Enable the automatic update check
"${DEFAULTS[@]}" write 'com.apple.SoftwareUpdate' 'AutomaticCheckEnabled' \
-boolean true
# Check for software updates daily, not just once per week
"${DEFAULTS[@]}" write 'com.apple.SoftwareUpdate' 'ScheduleFrequency' \
-integer 1
# Download newly available updates in background
"${DEFAULTS[@]}" write 'com.apple.SoftwareUpdate' 'AutomaticDownload' \
-integer 1
# ------------------------------------------------------------------------------
# Messages
# --------
# Disable smart quotes as it’s annoying for messages that contain code
"${DEFAULTS[@]}" write 'com.apple.messageshelper.MessageController' 'SOInputLineSettings' \
-dict-add 'automaticQuoteSubstitutionEnabled' -boolean false
# ------------------------------------------------------------------------------
# GnuCash
# -------
"${DEFAULTS[@]}" write -app 'Gnucash' 'AppleLanguages' '(en)'
"${DEFAULTS[@]}" write -app 'Gnucash' 'AppleLocale' 'de_CH'
# ------------------------------------------------------------------------------
for app in 'cfprefsd' \
'Dock' \
'Finder' \
'GnuCash' \
'Mail' \
'Messages' \
'Safari' \
'SystemUIServer'; do
"${KILLALL[@]}" "${app}"
done
# ------------------------------------------------------------------------------
exit 0