/home/alex/dev/deploy-macos.sh (1)

From RaySoft
#!/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

Usage

~/dev/deploy-macos.sh