Convert indention from spaces to tabs

This commit is contained in:
T. R. Bernstein
2024-06-28 17:17:47 +02:00
committed by T. R. Bernstein
parent 4096da8d4d
commit 545d95bd9c
12 changed files with 912 additions and 913 deletions

View File

@@ -2,158 +2,158 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
function versionGT() { function versionGT() {
[[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -gt "${2#*.}" ]] [[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -gt "${2#*.}" ]]
} }
function majorMinor() { function majorMinor() {
echo "${1%%.*}.$(x="${1#*.}" echo "${x%%.*}")" echo "${1%%.*}.$(x="${1#*.}" echo "${x%%.*}")"
} }
function shouldInstallCommandLineTools() { function shouldInstallCommandLineTools() {
local macosVersion=$(majorMinor $(/usr/bin/sw_vers -productVersion)) local macosVersion=$(majorMinor $(/usr/bin/sw_vers -productVersion))
if version_gt "${macosVersion}" "10.13" if version_gt "${macosVersion}" "10.13"
then then
! [[ -e "/Library/Developer/CommandLineTools/usr/bin/git" ]] ! [[ -e "/Library/Developer/CommandLineTools/usr/bin/git" ]]
else else
! [[ -e "/Library/Developer/CommandLineTools/usr/bin/git" ]] || ! [[ -e "/Library/Developer/CommandLineTools/usr/bin/git" ]] ||
! [[ -e "/usr/include/iconv.h" ]] ! [[ -e "/usr/include/iconv.h" ]]
fi fi
} }
function removeNewlines() { function removeNewlines() {
printf "%s" "${1/"$'\n'"/}" printf "%s" "${1/"$'\n'"/}"
} }
function acceptXcodeLicense() { function acceptXcodeLicense() {
xcodebuild -license accept xcodebuild -license accept
} }
function installCommandLineTools() { function installCommandLineTools() {
shouldInstallCommandLineTools || return shouldInstallCommandLineTools || return
cltPlaceholder="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress" cltPlaceholder="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress"
touch ${cltPlaceholder} touch ${cltPlaceholder}
cltLabelCommand="/usr/sbin/softwareupdate -l | cltLabelCommand="/usr/sbin/softwareupdate -l |
grep -B 1 -E 'Command Line Tools' | grep -B 1 -E 'Command Line Tools' |
awk -F'*' '/^ *\\*/ {print \$2}' | awk -F'*' '/^ *\\*/ {print \$2}' |
sed -e 's/^ *Label: //' -e 's/^ *//' | sed -e 's/^ *Label: //' -e 's/^ *//' |
sort -V | sort -V |
tail -n1" tail -n1"
cltLabel="$(removeNewlines "$(/bin/bash -c "${cltLabelCommand}")")" cltLabel="$(removeNewlines "$(/bin/bash -c "${cltLabelCommand}")")"
if [[ -n "${cltLabel}" ]] if [[ -n "${cltLabel}" ]]
then then
/usr/sbin/softwareupdate -i ${cltLabel} /usr/sbin/softwareupdate -i ${cltLabel}
/usr/bin/xcode-select --switch /Library/Developer/CommandLineTools /usr/bin/xcode-select --switch /Library/Developer/CommandLineTools
fi fi
rm -f ${cltPlaceholder} rm -f ${cltPlaceholder}
} }
function ensureCommandLineTools() { function ensureCommandLineTools() {
installCommandLineTools installCommandLineTools
acceptXcodeLicense acceptXcodeLicense
} }
function ensureDocopts() { function ensureDocopts() {
which docopts > /dev/null && return which docopts > /dev/null && return
local fileURL="${DOCOPTS_URL:-https://github.com/astzweig/docopts/releases/download/v.0.7.0/docopts_darwin_amd64}" local fileURL="${DOCOPTS_URL:-https://github.com/astzweig/docopts/releases/download/v.0.7.0/docopts_darwin_amd64}"
curl --output ./docopts -fsSL "${fileURL}" || return curl --output ./docopts -fsSL "${fileURL}" || return
chmod u+x ./docopts chmod u+x ./docopts
PATH="${PATH}:`pwd`" PATH="${PATH}:`pwd`"
} }
function cloneMacOSSystemRepo() { function cloneMacOSSystemRepo() {
local repoUrl="${MACOS_SYSTEM_REPO_URL:-https://github.com/astzweig/macos-system.git}" local repoUrl="${MACOS_SYSTEM_REPO_URL:-https://github.com/astzweig/macos-system.git}"
git clone --depth 1 -q "${repoUrl}" . 2> /dev/null || return 10 git clone --depth 1 -q "${repoUrl}" . 2> /dev/null || return 10
[ -n "${MACOS_SYSTEM_REPO_BRANCH}" ] && git checkout -q ${MACOS_SYSTEM_REPO_BRANCH} 2> /dev/null || true [ -n "${MACOS_SYSTEM_REPO_BRANCH}" ] && git checkout -q ${MACOS_SYSTEM_REPO_BRANCH} 2> /dev/null || true
} }
function cloneZSHLibRepo() { function cloneZSHLibRepo() {
local zshlibRepoUrl="${ZSHLIB_REPO_URL:-https://github.com/astzweig/zshlib.git}" local zshlibRepoUrl="${ZSHLIB_REPO_URL:-https://github.com/astzweig/zshlib.git}"
git config --file=.gitmodules submodule.zshlib.url "${zshlibRepoUrl}" git config --file=.gitmodules submodule.zshlib.url "${zshlibRepoUrl}"
git submodule -q sync git submodule -q sync
[ -n "${ZSHLIB_REPO_BRANCH}" ] && git submodule set-branch -b ${ZSHLIB_REPO_BRANCH} `git config --file=.gitmodules submodule.zshlib.path` 2> /dev/null || true [ -n "${ZSHLIB_REPO_BRANCH}" ] && git submodule set-branch -b ${ZSHLIB_REPO_BRANCH} `git config --file=.gitmodules submodule.zshlib.path` 2> /dev/null || true
git submodule -q update --depth 1 --init --recursive --remote 2> /dev/null || return 10 git submodule -q update --depth 1 --init --recursive --remote 2> /dev/null || return 10
} }
function isDebug() { function isDebug() {
test "${DEBUG}" = true -o "${DEBUG}" = 1 test "${DEBUG}" = true -o "${DEBUG}" = 1
} }
function printSuccess() { function printSuccess() {
print "${colors[green]}${*}${colors[reset]}" print "${colors[green]}${*}${colors[reset]}"
} }
function printError() { function printError() {
print "${errColors[red]}${*}${errColors[reset]}" >&2 print "${errColors[red]}${*}${errColors[reset]}" >&2
} }
function printFailedWithError() { function printFailedWithError() {
print "${colors[red]}failed.${colors[reset]}" print "${colors[red]}failed.${colors[reset]}"
print "$*" >&2 print "$*" >&2
} }
function defineColors() { function defineColors() {
local -A colorCodes=(red "`tput setaf 9`" green "`tput setaf 10`" reset "`tput sgr0`") local -A colorCodes=(red "`tput setaf 9`" green "`tput setaf 10`" reset "`tput sgr0`")
[ -t 1 ] && colors=("${(kv)colorCodes[@]}") [ -t 1 ] && colors=("${(kv)colorCodes[@]}")
[ -t 2 ] && errColors=("${(kv)colorCodes[@]}") [ -t 2 ] && errColors=("${(kv)colorCodes[@]}")
} }
function ensureRepo() { function ensureRepo() {
local repoName="$1" cmdName="${2}" local repoName="$1" cmdName="${2}"
print -n "Installing ${1}..." print -n "Installing ${1}..."
$cmdName || { printFailedWithError "This script requires $repoName but was not able to clone it. Please ensure access to the $repoName repository."; return 10} $cmdName || { printFailedWithError "This script requires $repoName but was not able to clone it. Please ensure access to the $repoName repository."; return 10}
printSuccess 'done' printSuccess 'done'
} }
function ensureBinary() { function ensureBinary() {
local binaryName="$1" cmdName="${2}" local binaryName="$1" cmdName="${2}"
print -n "Ensure ${1} is installed..." print -n "Ensure ${1} is installed..."
$cmdName || { printFailedWithError "This script requires $binaryName but was neither able to locate and install it. Please install $binaryName and add it to one of the PATH directories."; return 10} $cmdName || { printFailedWithError "This script requires $binaryName but was neither able to locate and install it. Please install $binaryName and add it to one of the PATH directories."; return 10}
printSuccess 'done' printSuccess 'done'
} }
function configureTerminal() { function configureTerminal() {
if [ -t 0 ]; then if [ -t 0 ]; then
traps+=("stty $(stty -g)") traps+=("stty $(stty -g)")
stty -echo stty -echo
fi fi
if [ -t 1 ]; then if [ -t 1 ]; then
traps+=('tput cnorm') traps+=('tput cnorm')
tput civis tput civis
export TERMINAL_CURSOR_HIDDEN=true export TERMINAL_CURSOR_HIDDEN=true
fi fi
} }
function main() { function main() {
local traps=() local traps=()
local -A colors=() errColors=() local -A colors=() errColors=()
defineColors defineColors
configureTerminal configureTerminal
local tmpdir="`mktemp -d -t 'macos-system'`" local tmpdir="`mktemp -d -t 'macos-system'`"
isDebug || traps+=("rm -fr -- '${tmpdir}'") isDebug || traps+=("rm -fr -- '${tmpdir}'")
trap ${(j.;.)traps} INT TERM EXIT trap ${(j.;.)traps} INT TERM EXIT
pushd -q "${tmpdir}" pushd -q "${tmpdir}"
print -l "Working directory is: ${tmpdir}" print -l "Working directory is: ${tmpdir}"
print 'Ensure command line tools are available.' print 'Ensure command line tools are available.'
ensureCommandLineTools ensureCommandLineTools
ensureRepo 'macos-system' cloneMacOSSystemRepo || return ensureRepo 'macos-system' cloneMacOSSystemRepo || return
ensureRepo 'zshlib' cloneZSHLibRepo || return ensureRepo 'zshlib' cloneZSHLibRepo || return
ensureBinary 'docopts' ensureDocopts || return ensureBinary 'docopts' ensureDocopts || return
print 'Will now run the installer.' print 'Will now run the installer.'
local -A colors=() errColors=() local -A colors=() errColors=()
[ -t 1 ] && tput cnorm [ -t 1 ] && tput cnorm
isDebug && export MACOS_SYSTEM_DEBUG=true isDebug && export MACOS_SYSTEM_DEBUG=true
"${tmpdir}/install.sh" "$@" "${tmpdir}/install.sh" "$@"
[ -t 1 ] && tput civis [ -t 1 ] && tput civis
popd -q popd -q
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel || "${ZSH_EVAL_CONTEXT}" == cmdarg ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel || "${ZSH_EVAL_CONTEXT}" == cmdarg ]]; then
_DIR="${0:A:h}" _DIR="${0:A:h}"
main "$@" main "$@"
fi fi

View File

@@ -2,106 +2,105 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
runModule() { runModule() {
"$@" "$@"
} }
function askNecessaryQuestions() { function askNecessaryQuestions() {
local mod= configArgs=() local mod= configArgs=()
config setappname "de.astzweig.macos.system-setup" config setappname "de.astzweig.macos.system-setup"
if [ -n "${config_only}" ]; then if [ -n "${config_only}" ]; then
lop -- -d "Config only option given with value:" -d "${config_only}" lop -- -d "Config only option given with value:" -d "${config_only}"
config setconfigfile "${config_only}" config setconfigfile "${config_only}"
elif [ -n "${config}" ]; then elif [ -n "${config}" ]; then
config setconfigfile "${config}" config setconfigfile "${config}"
configArgs=(-x) configArgs=(-x)
fi fi
askUserModuleQuestions ${configArgs} -c config -v moduleAnswers ${modulesToInstall} askUserModuleQuestions ${configArgs} -c config -v moduleAnswers ${modulesToInstall}
} }
function printModulesToInstall() { function printModulesToInstall() {
lop -- -d 'Modules that will install are:' -d "${modulesToInstall}" lop -- -d 'Modules that will install are:' -d "${modulesToInstall}"
for mod in "${modulesToInstall[@]}"; do for mod in "${modulesToInstall[@]}"; do
print "${mod}" print "${mod}"
done | abbreviatePaths done | abbreviatePaths
exit 0 exit 0
} }
function generateModuleOptions() { function generateModuleOptions() {
local value answerKey optionKey argName local value answerKey optionKey argName
for answerKey in ${(k)moduleAnswers}; do for answerKey in ${(k)moduleAnswers}; do
[[ ${answerKey} = ${mod}_* ]] || continue [[ ${answerKey} = ${mod}_* ]] || continue
optionKey="${answerKey#${mod}_}" optionKey="${answerKey#${mod}_}"
argName=${optionKey//_/-}; argName=${optionKey//_/-};
value="${moduleAnswers[${answerKey}]}" value="${moduleAnswers[${answerKey}]}"
if [[ "${optionKey}" =~ ^[[:alpha:]]$ ]]; then if [[ "${optionKey}" =~ ^[[:alpha:]]$ ]]; then
moduleOptions+=("-${argName}" "${value}") moduleOptions+=("-${argName}" "${value}")
elif [[ "${optionKey}" =~ ^[[:alpha:]][-[:alpha:]]+$ ]]; then elif [[ "${optionKey}" =~ ^[[:alpha:]][-[:alpha:]]+$ ]]; then
moduleOptions+=("--${argName}" "${value}") moduleOptions+=("--${argName}" "${value}")
else else
moduleOptions+=("${argName}" "${value}") moduleOptions+=("${argName}" "${value}")
fi fi
done done
} }
function filterPasswordOptions() { function filterPasswordOptions() {
local opt= hide=false local opt= hide=false
for opt in ${moduleOptions}; do for opt in ${moduleOptions}; do
[[ ${hide} = true ]] && { opt='******'; hide=false } [[ ${hide} = true ]] && { opt='******'; hide=false }
[[ $opt =~ ^--?.*password ]] && hide=true [[ $opt =~ ^--?.*password ]] && hide=true
filteredOptions+=($opt) filteredOptions+=($opt)
done done
} }
function installModules() { function installModules() {
local mod moduleOptions filteredOptions local mod moduleOptions filteredOptions
for mod in ${modulesToInstall}; do for mod in ${modulesToInstall}; do
moduleOptions=() moduleOptions=()
filteredOptions=() filteredOptions=()
generateModuleOptions generateModuleOptions
filterPasswordOptions filterPasswordOptions
[[ "${verbose}" == true ]] && moduleOptions+=(-v) [[ "${verbose}" == true ]] && moduleOptions+=(-v)
[[ -n ${logfile} ]] && moduleOptions+=(-d ${logfile}) [[ -n ${logfile} ]] && moduleOptions+=(-d ${logfile})
[[ -n ${noninteractive} ]] && moduleOptions+=(--noninteractive) lop -- -d "Running ${mod}" -d "with ${#moduleOptions} args:" -d "${filteredOptions}"
lop -- -d "Running ${mod}" -d "with ${#moduleOptions} args:" -d "${filteredOptions}" runModule ${mod} ${moduleOptions}
runModule ${mod} ${moduleOptions} done
done
} }
function isMacOS() { function isMacOS() {
autoload is-at-least autoload is-at-least
[ "`uname -s`" = Darwin ] || return [ "`uname -s`" = Darwin ] || return
is-at-least "10.13" "`sw_vers -productVersion 2> /dev/null`" is-at-least "10.13" "`sw_vers -productVersion 2> /dev/null`"
} }
function isPlistBuddyInstalled() { function isPlistBuddyInstalled() {
test -x /usr/libexec/PlistBuddy && return test -x /usr/libexec/PlistBuddy && return
which PlistBuddy >&! /dev/null && return which PlistBuddy >&! /dev/null && return
} }
function checkPrerequisites() { function checkPrerequisites() {
isMacOS || { lop -- -e 'This setup is only for macOS 10.13 and up.'; return 10 } isMacOS || { lop -- -e 'This setup is only for macOS 10.13 and up.'; return 10 }
isPlistBuddyInstalled || { lop -- -e 'This setup requires PlistBuddy to be either at /usr/libexec or in any of the PATH directories.'; return 11 } isPlistBuddyInstalled || { lop -- -e 'This setup requires PlistBuddy to be either at /usr/libexec or in any of the PATH directories.'; return 11 }
} }
function configureTerminal() { function configureTerminal() {
if [ -t 0 ]; then if [ -t 0 ]; then
traps+=("stty $(stty -g)") traps+=("stty $(stty -g)")
stty -echo stty -echo
fi fi
if [ -t 1 ]; then if [ -t 1 ]; then
traps+=('tput cnorm') traps+=('tput cnorm')
tput civis tput civis
export TERMINAL_CURSOR_HIDDEN=true export TERMINAL_CURSOR_HIDDEN=true
fi fi
} }
function main() { function main() {
local traps=() local traps=()
configureTerminal configureTerminal
trap ${(j.;.)traps} INT TERM EXIT trap ${(j.;.)traps} INT TERM EXIT
autoloadZShLib || return autoloadZShLib || return
checkPrerequisites || return checkPrerequisites || return
eval "`docopts -f -V - -h - : "$@" <<- USAGE eval "`docopts -f -V - -h - : "$@" <<- USAGE
Usage: $0 [options] [-m PATH]... [<module>...] Usage: $0 [options] [-m PATH]... [<module>...]
Install all modules in module search path. If any <module> arg is given, Install all modules in module search path. If any <module> arg is given,
@@ -127,28 +126,28 @@ function main() {
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE`" USAGE`"
local allModules=() modulesToInstall=() local allModules=() modulesToInstall=()
local -A moduleAnswers local -A moduleAnswers
configureLogging configureLogging
lop -- -d "Current working dir is: `pwd`" lop -- -d "Current working dir is: `pwd`"
lop -- -d "Called main with $# args: $*" lop -- -d "Called main with $# args: $*"
[[ -n ${noninteractive} && -z ${config} ]] && { lop -- -e 'A config file must be provided in noninteractive mode.'; return 10 } [[ -n ${noninteractive} && -z ${config} ]] && { lop -- -e 'A config file must be provided in noninteractive mode.'; return 10 }
modpath+=("${_DIR}/modules") modpath+=("${_DIR}/modules")
loadModules -v modulesToInstall ${$(echo -m):^^modpath} "${module[@]}" loadModules -v modulesToInstall ${$(echo -m):^^modpath} "${module[@]}"
[ "${list}" = true ] && printModulesToInstall [ "${list}" = true ] && printModulesToInstall
askNecessaryQuestions askNecessaryQuestions
[ -z "${config_only}" ] || return 0 [ -z "${config_only}" ] || return 0
requireRootPrivileges requireRootPrivileges
installModules installModules
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
_DIR="${0:A:h}" _DIR="${0:A:h}"
export ASTZWEIG_MACOS_SYSTEM_LIB=${_DIR}/modules/lib.sh export ASTZWEIG_MACOS_SYSTEM_LIB=${_DIR}/modules/lib.sh
export ASTZWEIG_ZSHLIB=${_DIR}/zshlib export ASTZWEIG_ZSHLIB=${_DIR}/zshlib
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
main "$@" main "$@"
fi fi

View File

@@ -2,32 +2,32 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
function getQuestionsPrerequisites() { function getQuestionsPrerequisites() {
cmds=( cmds=(
[systemsetup]='' [systemsetup]=''
) )
requireRootPrivileges requireRootPrivileges
} }
function getExecPrerequisites() { function getExecPrerequisites() {
cmds=( cmds=(
[osascript]='' [osascript]=''
[scutil]='' [scutil]=''
[systemsetup]='' [systemsetup]=''
[nvram]='' [nvram]=''
[pmset]='' [pmset]=''
[defaults]='' [defaults]=''
[/usr/libexec/ApplicationFirewall/socketfilterfw]='' [/usr/libexec/ApplicationFirewall/socketfilterfw]=''
[launchctl]='' [launchctl]=''
) )
} }
function getQuestions() { function getQuestions() {
local timezones local timezones
timezones="`systemsetup -listtimezones | tail -n +2 | awk '{print $1}' | paste -sd, -`" timezones="`systemsetup -listtimezones | tail -n +2 | awk '{print $1}' | paste -sd, -`"
questions=( questions=(
'i: hostname=What shall the hostname of this host be?' 'i: hostname=What shall the hostname of this host be?'
's: timezone=What shall the timezone of this host be? # choose from:'"${timezones};" 's: timezone=What shall the timezone of this host be? # choose from:'"${timezones};"
) )
} }
function quitSystemPreferences() { function quitSystemPreferences() {
@@ -35,84 +35,84 @@ function quitSystemPreferences() {
} }
function setComputerName() { function setComputerName() {
scutil --set ComputerName "${hostname}" scutil --set ComputerName "${hostname}"
scutil --set HostName "${hostname}" scutil --set HostName "${hostname}"
scutil --set LocalHostName "${hostname}" scutil --set LocalHostName "${hostname}"
systemsetup -setcomputername "${hostname}" systemsetup -setcomputername "${hostname}"
systemsetup -setlocalsubnetname "${hostname}" systemsetup -setlocalsubnetname "${hostname}"
} }
function configureComputerHostname() { function configureComputerHostname() {
local currentComputerName="`scutil --get ComputerName`" local currentComputerName="`scutil --get ComputerName`"
if [[ "${currentComputerName}" != "${hostname}" ]]; then if [[ "${currentComputerName}" != "${hostname}" ]]; then
lop -- -i 'Hostname of computer has not been set.' -i "Will set to ${hostname}." lop -- -i 'Hostname of computer has not been set.' -i "Will set to ${hostname}."
indicateActivity -- 'Set computer name' setComputerName indicateActivity -- 'Set computer name' setComputerName
else else
lop -- -i 'Hostname of computer seems to have already been set. Skipping.' -i "Hostname: $currentComputerName" lop -- -i 'Hostname of computer seems to have already been set. Skipping.' -i "Hostname: $currentComputerName"
fi fi
} }
function configureBasicSystem(){ function configureBasicSystem(){
# Disable the sound effects on boot # Disable the sound effects on boot
nvram SystemAudioVolume=" " nvram SystemAudioVolume=" "
systemsetup -settimezone "${timezone}" >&! /dev/null systemsetup -settimezone "${timezone}" >&! /dev/null
systemsetup -setusingnetworktime on >&! /dev/null systemsetup -setusingnetworktime on >&! /dev/null
systemsetup -setnetworktimeserver 'time.apple.com' >&! /dev/null systemsetup -setnetworktimeserver 'time.apple.com' >&! /dev/null
systemsetup -setsleep never >&! /dev/null systemsetup -setsleep never >&! /dev/null
systemsetup -setwakeonnetworkaccess off >&! /dev/null systemsetup -setwakeonnetworkaccess off >&! /dev/null
systemsetup -setrestartfreeze on >&! /dev/null systemsetup -setrestartfreeze on >&! /dev/null
systemsetup -f -setremotelogin off >&! /dev/null systemsetup -f -setremotelogin off >&! /dev/null
systemsetup -setremoteappleevents off >&! /dev/null systemsetup -setremoteappleevents off >&! /dev/null
} }
function configurePowerManagement() { function configurePowerManagement() {
cmd=(pmset -a) cmd=(pmset -a)
${cmd} displaysleep 0 ${cmd} displaysleep 0
${cmd} disksleep 0 ${cmd} disksleep 0
${cmd} sleep 0 ${cmd} sleep 0
${cmd} womp 0 ${cmd} womp 0
${cmd} acwake 0 ${cmd} acwake 0
${cmd} proximitywake 0 ${cmd} proximitywake 0
${cmd} destroyfvkeyonstandby 1 ${cmd} destroyfvkeyonstandby 1
pmset -b acwake 1 pmset -b acwake 1
${cmd} lidwake 1 ${cmd} lidwake 1
${cmd} halfdim 1 ${cmd} halfdim 1
${cmd} powernap 1 ${cmd} powernap 1
${cmd} hibernatemode 0 ${cmd} hibernatemode 0
} }
function configureLoginWindow() { function configureLoginWindow() {
cmd=(defaults write '/Library/Preferences/com.apple.loginwindow') cmd=(defaults write '/Library/Preferences/com.apple.loginwindow')
${cmd} DisableFDEAutoLogin -bool true ${cmd} DisableFDEAutoLogin -bool true
${cmd} SHOWFULLNAME -bool true ${cmd} SHOWFULLNAME -bool true
${cmd} AdminHostInfo -string HostName ${cmd} AdminHostInfo -string HostName
${cmd} GuestEnabled -bool false ${cmd} GuestEnabled -bool false
} }
function configureMacOSFirewall() { function configureMacOSFirewall() {
cmd=(/usr/libexec/ApplicationFirewall/socketfilterfw) cmd=(/usr/libexec/ApplicationFirewall/socketfilterfw)
${cmd} --setglobalstate on ${cmd} --setglobalstate on
${cmd} --setblockall off ${cmd} --setblockall off
${cmd} --setstealthmode on ${cmd} --setstealthmode on
${cmd} --setallowsigned on ${cmd} --setallowsigned on
${cmd} --setallowsignedapp on ${cmd} --setallowsignedapp on
} }
function configure_system() { function configure_system() {
lop -y h1 -- -i 'Configure System Settings' lop -y h1 -- -i 'Configure System Settings'
quitSystemPreferences quitSystemPreferences
configureComputerHostname configureComputerHostname
indicateActivity -- 'Configuring systemsetup and nvram' configureBasicSystem indicateActivity -- 'Configuring systemsetup and nvram' configureBasicSystem
indicateActivity -- 'Configuring power management' configurePowerManagement indicateActivity -- 'Configuring power management' configurePowerManagement
indicateActivity -- 'Configuring login window' configureLoginWindow indicateActivity -- 'Configuring login window' configureLoginWindow
indicateActivity -- 'Configure global umask' launchctl config user umask 027 indicateActivity -- 'Configure global umask' launchctl config user umask 027
indicateActivity -- 'Configure macOS firewall' configureMacOSFirewall indicateActivity -- 'Configure macOS firewall' configureMacOSFirewall
} }
function getUsage() { function getUsage() {
local cmdName=$1 text='' local cmdName=$1 text=''
read -r -d '' text <<- USAGE read -r -d '' text <<- USAGE
Usage: Usage:
$cmdName show-questions [<modkey> <modans>]... $cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE] --hostname NAME --timezone ZONE $cmdName [-v] [-d FILE] --hostname NAME --timezone ZONE
@@ -120,8 +120,8 @@ function getUsage() {
Set energy, basic network and host preferences. Set energy, basic network and host preferences.
Options: Options:
--hostname NAME Set NAME as current host's host name. --hostname NAME Set NAME as current host's host name.
--timezone ZONE Set ZONE as current host's timezone [default: Europe/Berlin]. --timezone ZONE Set ZONE as current host's timezone [default: Europe/Berlin].
-d FILE, --logfile FILE Print log message to logfile instead of stdout. -d FILE, --logfile FILE Print log message to logfile instead of stdout.
-v, --verbose Be more verbose. -v, --verbose Be more verbose.
---- ----
@@ -129,11 +129,11 @@ function getUsage() {
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE USAGE
print -- ${text} print -- ${text}
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 } test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@" module_main $0 "$@"
fi fi

View File

@@ -2,138 +2,138 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
function getComputerName() { function getComputerName() {
local moduleAnswer local moduleAnswer
local computerName="`scutil --get ComputerName 2> /dev/null`" local computerName="`scutil --get ComputerName 2> /dev/null`"
getModuleAnswerByKeyRegEx '_hostname$' && computerName=$moduleAnswer getModuleAnswerByKeyRegEx '_hostname$' && computerName=$moduleAnswer
print -- $computerName print -- $computerName
} }
function getDefaultFullname() { function getDefaultFullname() {
local computerName="`getComputerName`" local computerName="`getComputerName`"
lop -- -d 'Default full name based on current computer name is:' -d "$computerName" lop -- -d 'Default full name based on current computer name is:' -d "$computerName"
print "${computerName}" print "${computerName}"
} }
function getDefaultUsername() { function getDefaultUsername() {
local username="`getDefaultFullname | tr '[:upper:]' '[:lower:]' | tr -C '[:alnum:]\n' '-'`" local username="`getDefaultFullname | tr '[:upper:]' '[:lower:]' | tr -C '[:alnum:]\n' '-'`"
lop -- -d 'Default username based on current computer name is:' -d "$username" lop -- -d 'Default username based on current computer name is:' -d "$username"
print "${username}" print "${username}"
} }
function isAPFSFilesystem() { function isAPFSFilesystem() {
[[ $(diskutil info / | awk 'sub(/File System Personality: /,""){print $0}') = *APFS* ]] [[ $(diskutil info / | awk 'sub(/File System Personality: /,""){print $0}') = *APFS* ]]
} }
function getUsersWithSecureToken() { function getUsersWithSecureToken() {
local username uuid local username uuid
for uuid in ${$(diskutil apfs listUsers / | awk '/\+\-\-/ {print $2}')}; do for uuid in ${$(diskutil apfs listUsers / | awk '/\+\-\-/ {print $2}')}; do
username="$(dscl . -search /Users GeneratedUID ${uuid} | awk 'NR==1{print $1}')" username="$(dscl . -search /Users GeneratedUID ${uuid} | awk 'NR==1{print $1}')"
checkSecureTokenForUser ${username} && secureTokenUsers+=("${username}") checkSecureTokenForUser ${username} && secureTokenUsers+=("${username}")
done done
} }
function getDefaultUserPictures() { function getDefaultUserPictures() {
pushd -q '/Library/User Pictures' pushd -q '/Library/User Pictures'
defaultUserPictures=("${(@f)$(find -E . -type f -iregex '.*\.(tif|png|jpeg|jpg)' | abbreviatePaths)}") defaultUserPictures=("${(@f)$(find -E . -type f -iregex '.*\.(tif|png|jpeg|jpg)' | abbreviatePaths)}")
popd -q popd -q
} }
function convertPathToDefaultPicture() { function convertPathToDefaultPicture() {
local resolved='' local resolved=''
lop -- -d 'Converting path' -d "${filevault_picture}" -d 'to default picture path if necessary.' lop -- -d 'Converting path' -d "${filevault_picture}" -d 'to default picture path if necessary.'
if [ -r "${filevault_picture}" ]; then if [ -r "${filevault_picture}" ]; then
lop -- -d 'Path seems to be a valid path already. Skipping conversion.' lop -- -d 'Path seems to be a valid path already. Skipping conversion.'
return return
fi fi
pushd -q '/Library/User Pictures' pushd -q '/Library/User Pictures'
resolved="`find "$_" -type f -path "*${filevault_picture}" 2> /dev/null`" resolved="`find "$_" -type f -path "*${filevault_picture}" 2> /dev/null`"
lop -- -d 'Resolved path is' -d "${resolved}" lop -- -d 'Resolved path is' -d "${resolved}"
popd -q popd -q
[ -n "${resolved}" -a -r "${resolved}" ] && filevault_picture="${resolved}" [ -n "${resolved}" -a -r "${resolved}" ] && filevault_picture="${resolved}"
} }
function _isPathToPicture() { function _isPathToPicture() {
local filevault_picture=$1 local filevault_picture=$1
convertPathToDefaultPicture convertPathToDefaultPicture
[ -r "${filevault_picture}" ] || { lop -- -d 'Resolved path is not a valid path. Returning.'; return 10 } [ -r "${filevault_picture}" ] || { lop -- -d 'Resolved path is not a valid path. Returning.'; return 10 }
[[ "${filevault_picture:e:l}" =~ (tif|png|jpeg|jpg) ]] || return 11 [[ "${filevault_picture:e:l}" =~ (tif|png|jpeg|jpg) ]] || return 11
} }
function isPathToPicture() { function isPathToPicture() {
indicateActivity -- "Verifying $1 as picture path" _isPathToPicture $1 indicateActivity -- "Verifying $1 as picture path" _isPathToPicture $1
} }
function _checkSecureTokenForUser() { function _checkSecureTokenForUser() {
local u=$1 local u=$1
sysadminctl -secureTokenStatus "${u}" 2>&1 | grep ENABLED >&! /dev/null sysadminctl -secureTokenStatus "${u}" 2>&1 | grep ENABLED >&! /dev/null
} }
function checkSecureTokenForUser() { function checkSecureTokenForUser() {
local u=$1 local u=$1
indicateActivity -- "Checking if user $u has a secure token set" _checkSecureTokenForUser $u indicateActivity -- "Checking if user $u has a secure token set" _checkSecureTokenForUser $u
} }
function _checkUserPassword() { function _checkUserPassword() {
local username=$1 password=$2 local username=$1 password=$2
dscl . -authonly ${username} ${password} >&! /dev/null dscl . -authonly ${username} ${password} >&! /dev/null
} }
function checkSecureTokenUserPassword() { function checkSecureTokenUserPassword() {
indicateActivity -- "Checking password for user ${secure_token_user_username}" _checkUserPassword ${secure_token_user_username} ${secure_token_user_password} indicateActivity -- "Checking password for user ${secure_token_user_username}" _checkUserPassword ${secure_token_user_username} ${secure_token_user_password}
} }
function checkFileVaultUserPassword() { function checkFileVaultUserPassword() {
indicateActivity -- "Checking password for user ${filevault_username}" _checkUserPassword ${filevault_username} ${filevault_password} indicateActivity -- "Checking password for user ${filevault_username}" _checkUserPassword ${filevault_username} ${filevault_password}
} }
function _doesFileVaultUserExist() { function _doesFileVaultUserExist() {
dscl . -list /Users | grep "${filevault_username}" >&! /dev/null dscl . -list /Users | grep "${filevault_username}" >&! /dev/null
} }
function doesFileVaultUserExist() { function doesFileVaultUserExist() {
indicateActivity -- "Checking if ${filevault_username} already exists" _doesFileVaultUserExist indicateActivity -- "Checking if ${filevault_username} already exists" _doesFileVaultUserExist
} }
function _createFileVaultUser() { function _createFileVaultUser() {
local un=${filevault_username} fn=${filevault_fullname} pw=${filevault_password} result= local un=${filevault_username} fn=${filevault_fullname} pw=${filevault_password} result=
lop -- -d 'Creating FileVault user' -d "${un}" lop -- -d 'Creating FileVault user' -d "${un}"
sysadminctl -addUser ${un} -fullName ${fn} -shell /usr/bin/false -home /var/empty -password ${pw} -picture ${filevault_picture} sysadminctl -addUser ${un} -fullName ${fn} -shell /usr/bin/false -home /var/empty -password ${pw} -picture ${filevault_picture}
result=$? result=$?
lop -- -d 'Return value of sysadminctl is ' -d "$?" lop -- -d 'Return value of sysadminctl is ' -d "$?"
return $result return $result
} }
function createFileVaultUser() { function createFileVaultUser() {
indicateActivity -- "Creating FileVault user ${filevault_username}" _createFileVaultUser indicateActivity -- "Creating FileVault user ${filevault_username}" _createFileVaultUser
} }
function _configureFileVaultUser() { function _configureFileVaultUser() {
local un=${filevault_username} local un=${filevault_username}
dscl . -create "/Users/${un}" IsHidden 1 dscl . -create "/Users/${un}" IsHidden 1
chsh -s /usr/bin/false "${un}" >&! /dev/null chsh -s /usr/bin/false "${un}" >&! /dev/null
} }
function configureFileVaultUser() { function configureFileVaultUser() {
indicateActivity -- "Configuring FileVault user ${filevault_username}" _configureFileVaultUser indicateActivity -- "Configuring FileVault user ${filevault_username}" _configureFileVaultUser
} }
function configureSecureToken() { function configureSecureToken() {
local un=${filevault_username} up=${filevault_password} local un=${filevault_username} up=${filevault_password}
local stun=${secure_token_user_username} stup=${secure_token_user_password} local stun=${secure_token_user_username} stup=${secure_token_user_password}
indicateActivity -- "Enable secure token for ${un}" sysadminctl -secureTokenOn "${un}" -password "${up}" -adminUser "${stun}" -adminPassword "${stup}" indicateActivity -- "Enable secure token for ${un}" sysadminctl -secureTokenOn "${un}" -password "${up}" -adminUser "${stun}" -adminPassword "${stup}"
} }
function canUserUnlockDisk() { function canUserUnlockDisk() {
local username=$1 local username=$1
for fdeuser in ${(f)"$(fdesetup list | cut -d',' -f1)"}; do for fdeuser in ${(f)"$(fdesetup list | cut -d',' -f1)"}; do
[[ ${fdeuser} = ${username} ]] && return [[ ${fdeuser} = ${username} ]] && return
done done
return 1 return 1
} }
function getFDESetupXMLForUser() { function getFDESetupXMLForUser() {
local username="${1}" password="${2}" local username="${1}" password="${2}"
cat <<- XML cat <<- XML
<?xml version="1.0" encoding=\"UTF-8\"?> <?xml version="1.0" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\"> <plist version=\"1.0\">
@@ -148,120 +148,120 @@ function getFDESetupXMLForUser() {
} }
function _enableFileVaultForSecureTokenUser() { function _enableFileVaultForSecureTokenUser() {
local username="${1}" password="${2}" local username="${1}" password="${2}"
getFDESetupXMLForUser "${username}" "${password}" | fdesetup enable -inputplist getFDESetupXMLForUser "${username}" "${password}" | fdesetup enable -inputplist
} }
function enableFileVaultForSecureTokenUser() { function enableFileVaultForSecureTokenUser() {
fdesetup isactive >&! /dev/null && return fdesetup isactive >&! /dev/null && return
indicateActivity -- "Enable FileVault for secure token" _enableFileVaultForSecureTokenUser ${secure_token_user_username} ${secure_token_user_password} indicateActivity -- "Enable FileVault for secure token" _enableFileVaultForSecureTokenUser ${secure_token_user_username} ${secure_token_user_password}
} }
function _allowUserToUnlockDisk() { function _allowUserToUnlockDisk() {
local username="${1}" password="${2}" local username="${1}" password="${2}"
getFDESetupXMLForUser ${username} ${password} | fdesetup add -inputplist getFDESetupXMLForUser ${username} ${password} | fdesetup add -inputplist
} }
function allowFileVaultUserToUnlockDisk() { function allowFileVaultUserToUnlockDisk() {
indicateActivity -- "Allow FileVault user to unlock disk" _allowUserToUnlockDisk ${filevault_username} ${filevault_password} indicateActivity -- "Allow FileVault user to unlock disk" _allowUserToUnlockDisk ${filevault_username} ${filevault_password}
} }
function _allowOnlyFileVaultUserToUnlock() { function _allowOnlyFileVaultUserToUnlock() {
local fdeuser local fdeuser
for fdeuser in ${(f)"$(fdesetup list | cut -d',' -f1)"}; do for fdeuser in ${(f)"$(fdesetup list | cut -d',' -f1)"}; do
[[ ${fdeuser} != ${filevault_username} ]] && fdesetup remove -user "${fdeuser}" [[ ${fdeuser} != ${filevault_username} ]] && fdesetup remove -user "${fdeuser}"
done done
return 0 return 0
} }
function allowOnlyFileVaultUserToUnlock() { function allowOnlyFileVaultUserToUnlock() {
indicateActivity -- "Disallow everyone else from unlocking disk" _allowOnlyFileVaultUserToUnlock indicateActivity -- "Disallow everyone else from unlocking disk" _allowOnlyFileVaultUserToUnlock
} }
function configure_system() { function configure_system() {
lop -y h1 -- -i 'Setup FileVault System' lop -y h1 -- -i 'Setup FileVault System'
checkSecureTokenForUser "${secure_token_user_username}" || { lop -- -e 'The provided secure token user has no secure token.'; return 10 } checkSecureTokenForUser "${secure_token_user_username}" || { lop -- -e 'The provided secure token user has no secure token.'; return 10 }
checkSecureTokenUserPassword || { lop -- -e 'The secure token user password is incorrect.'; return 11 } checkSecureTokenUserPassword || { lop -- -e 'The secure token user password is incorrect.'; return 11 }
indicateActivity -- "Resolving path of picture ${filevault_picture}" convertPathToDefaultPicture indicateActivity -- "Resolving path of picture ${filevault_picture}" convertPathToDefaultPicture
isPathToPicture "${filevault_picture}" || { lop -- -e 'The provided FileVault user picture is not a valid path to a TIF, PNG or JPEG file.'; return 12 } isPathToPicture "${filevault_picture}" || { lop -- -e 'The provided FileVault user picture is not a valid path to a TIF, PNG or JPEG file.'; return 12 }
if doesFileVaultUserExist; then if doesFileVaultUserExist; then
checkFileVaultUserPassword || { lop -- -e 'The FileVault user password is incorrect.'; return 13 } checkFileVaultUserPassword || { lop -- -e 'The FileVault user password is incorrect.'; return 13 }
else else
createFileVaultUser || { lop -- -e 'Was not able to create FileVault user.'; return 14 } createFileVaultUser || { lop -- -e 'Was not able to create FileVault user.'; return 14 }
fi fi
configureFileVaultUser || { lop -- -e 'Could not configure FileVault user.'; return 15 } configureFileVaultUser || { lop -- -e 'Could not configure FileVault user.'; return 15 }
enableFileVaultForSecureTokenUser || { lop -- -e 'Could not enable FileVault for secure token user.'; return 16 } enableFileVaultForSecureTokenUser || { lop -- -e 'Could not enable FileVault for secure token user.'; return 16 }
checkSecureTokenForUser "${filevault_username}" || configureSecureToken || { lop -- -e 'Could not configure secure token for FileVault user.'; return 17 } checkSecureTokenForUser "${filevault_username}" || configureSecureToken || { lop -- -e 'Could not configure secure token for FileVault user.'; return 17 }
canUserUnlockDisk ${filevault_username} || allowFileVaultUserToUnlockDisk || { lop -- -e 'Was not able to allow FileVault user to unlock disk.'; return 18 } canUserUnlockDisk ${filevault_username} || allowFileVaultUserToUnlockDisk || { lop -- -e 'Was not able to allow FileVault user to unlock disk.'; return 18 }
allowOnlyFileVaultUserToUnlock "${filevault_username}" || { lop -- -e 'Was not able to deactivate all other user from unlocking disk.'; return 19 } allowOnlyFileVaultUserToUnlock "${filevault_username}" || { lop -- -e 'Was not able to deactivate all other user from unlocking disk.'; return 19 }
indicateActivity -- 'Update APFS preboot volume' diskutil apfs updatePreboot / || { lop -- -e 'Was not able to update APFS preboot volume.'; return 20 } indicateActivity -- 'Update APFS preboot volume' diskutil apfs updatePreboot / || { lop -- -e 'Was not able to update APFS preboot volume.'; return 20 }
} }
function getHelpPrerequisites() { function getHelpPrerequisites() {
cmds=( cmds=(
[tr]='' [tr]=''
[scutil]='' [scutil]=''
) )
addDocoptsToCmds addDocoptsToCmds
} }
function getQuestionsPrerequisites() { function getQuestionsPrerequisites() {
cmds=( cmds=(
[find]='' [find]=''
[dscl]='' [dscl]=''
[dseditgroup]='' [dseditgroup]=''
[awk]='' [awk]=''
[diskutil]='' [diskutil]=''
[sysadminctl]='' [sysadminctl]=''
) )
isAPFSFilesystem || { lop -- -e 'This module requires an APFS filesystem.'; return 10 } isAPFSFilesystem || { lop -- -e 'This module requires an APFS filesystem.'; return 10 }
} }
function getExecPrerequisites() { function getExecPrerequisites() {
cmds=( cmds=(
[cut]='' [cut]=''
[cat]='' [cat]=''
[fdesetup]='' [fdesetup]=''
[base64]='' [base64]=''
[dsimport]='' [dsimport]=''
) )
requireRootPrivileges requireRootPrivileges
} }
function getQuestions() { function getQuestions() {
local secureTokenUsers=() defaultUserPictures=() local secureTokenUsers=() defaultUserPictures=()
local defaultUsername="`getDefaultUsername`" defaultFullname="`getDefaultFullname`" local defaultUsername="`getDefaultUsername`" defaultFullname="`getDefaultFullname`"
getUsersWithSecureToken getUsersWithSecureToken
getDefaultUserPictures getDefaultUserPictures
local defaultUsernameHint= defaultFullnameHint= local defaultUsernameHint= defaultFullnameHint=
[ -n "${defaultUsername}" ] && defaultUsernameHint="default:${defaultUsername};" [ -n "${defaultUsername}" ] && defaultUsernameHint="default:${defaultUsername};"
[ -n "${defaultFullname}" ] && defaultFullnameHint="default:${defaultFullname};" [ -n "${defaultFullname}" ] && defaultFullnameHint="default:${defaultFullname};"
questions=( questions=(
'i: filevault-fullname=What shall the FileVault user'\''s full name be? # '"${defaultFullnameHint}" 'i: filevault-fullname=What shall the FileVault user'\''s full name be? # '"${defaultFullnameHint}"
'i: filevault-username=What shall the FileVault user'\''s username be? # '"${defaultUsernameHint}" 'i: filevault-username=What shall the FileVault user'\''s username be? # '"${defaultUsernameHint}"
'p: filevault-password=What shall the FileVault user'\''s password be?' 'p: filevault-password=What shall the FileVault user'\''s password be?'
's: filevault-picture=Select a picture for FileVault user or enter the path to your own picture # validator:'"${cmdPath}"',is-picture;choose from:'"${(j.,.)defaultUserPictures};" 's: filevault-picture=Select a picture for FileVault user or enter the path to your own picture # validator:'"${cmdPath}"',is-picture;choose from:'"${(j.,.)defaultUserPictures};"
's: secure-token-user-username=Which user with a secure token shall be used? # choose from:'"${(j.,.)secureTokenUsers};" 's: secure-token-user-username=Which user with a secure token shall be used? # choose from:'"${(j.,.)secureTokenUsers};"
'p: secure-token-user-password=What is the secure token user'\''s password?' 'p: secure-token-user-password=What is the secure token user'\''s password?'
) )
} }
function preQuestionHook() { function preQuestionHook() {
if [[ "${is_picture}" = true ]]; then if [[ "${is_picture}" = true ]]; then
isPathToPicture ${pathstr} isPathToPicture ${pathstr}
exit $? exit $?
fi fi
} }
function getUsage() { function getUsage() {
local cmdName=$1 text='' varname= local cmdName=$1 text='' varname=
local defaultUsername="`getDefaultUsername`" defaultFullname="`getDefaultFullname`" local defaultUsername="`getDefaultUsername`" defaultFullname="`getDefaultFullname`"
for varname in defaultUsername defaultFullname; do for varname in defaultUsername defaultFullname; do
local ${varname}Str= local ${varname}Str=
[ -n "${(P)varname}" ] && local ${varname}Str=" [default: ${(P)varname}]" [ -n "${(P)varname}" ] && local ${varname}Str=" [default: ${(P)varname}]"
done done
read -r -d '' text <<- USAGE read -r -d '' text <<- USAGE
Usage: Usage:
$cmdName show-questions [<modkey> <modans>]... $cmdName show-questions [<modkey> <modans>]...
$cmdName is-picture <pathstr> $cmdName is-picture <pathstr>
@@ -274,31 +274,31 @@ function getUsage() {
length and randomness). length and randomness).
Options: Options:
--filevault-fullname NAME Full name of the designated FileVault user. An --filevault-fullname NAME Full name of the designated FileVault user. An
existing FileVault user will be renamed to that existing FileVault user will be renamed to that
name${defaultFullnameStr}. name ${defaultFullnameStr}.
--filevault-username NAME Username of the designated FileVault user. An --filevault-username NAME Username of the designated FileVault user. An
existing FileVault user will be renamed to that existing FileVault user will be renamed to that
name${defaultUsernameStr}. name ${defaultUsernameStr}.
--filevault-password PASSWORD Password of the designated FileVault user. The password --filevault-password PASSWORD Password of the designated FileVault user. The password
an existing FileVault user will not be changed. an existing FileVault user will not be changed.
--filevault-picture PATH_TO_PIC The path to the picture that shall be made the FileVault --filevault-picture PATH_TO_PIC The path to the picture that shall be made the FileVault
user picture. The picture of an existing FileVault user user picture. The picture of an existing FileVault user
will be updated. will be updated.
--secure-token-user-username NAME The username of an user with a secure token. --secure-token-user-username NAME The username of an user with a secure token.
--secure-token-user-password PASSWORD The password of the secure token user. --secure-token-user-password PASSWORD The password of the secure token user.
-d FILE, --logfile FILE Print log message to logfile instead of stdout. -d FILE, --logfile FILE Print log message to logfile instead of stdout.
-v, --verbose Be more verbose. -v, --verbose Be more verbose.
---- ----
$cmdName 0.1.0 $cmdName 0.1.0
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE USAGE
print -- ${text} print -- ${text}
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 } test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@" module_main $0 "$@"
fi fi

View File

@@ -5,14 +5,14 @@ export HOMEBREW_NO_ANALYTICS_THIS_RUN=1
export HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT=1 export HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT=1
function doesUserExist() { function doesUserExist() {
local username=$1 local username=$1
dscl . -list /Users | grep "^${username}$" 2> /dev/null >&2 dscl . -list /Users | grep "^${username}$" 2> /dev/null >&2
} }
function runAsUser() { function runAsUser() {
local username=$1 local username=$1
shift shift
sudo -Hu "${username}" "${@}" sudo -Hu "${username}" "${@}"
} }
function runAsHomebrewUser() { function runAsHomebrewUser() {
@@ -20,95 +20,95 @@ function runAsHomebrewUser() {
} }
function ensureUserIsInAdminGroup() { function ensureUserIsInAdminGroup() {
local username=$1 local username=$1
dseditgroup -o edit -a "${username}" -t user admin dseditgroup -o edit -a "${username}" -t user admin
} }
function ensureUserCanRunPasswordlessSudo() { function ensureUserCanRunPasswordlessSudo() {
local username=$1 local username=$1
local sudoersFile="/etc/sudoers.d/no-auth-sudo-for-${username}" local sudoersFile="/etc/sudoers.d/no-auth-sudo-for-${username}"
[[ -f ${sudoersFile} ]] && return [[ -f ${sudoersFile} ]] && return
cat <<- SUDOERS > "${sudoersFile}" cat <<- SUDOERS > "${sudoersFile}"
Defaults:${username} !authenticate Defaults:${username} !authenticate
SUDOERS SUDOERS
chown root:wheel "${sudoersFile}" || return 10 chown root:wheel "${sudoersFile}" || return 10
chmod u=rw,g=r,o= "${sudoersFile}" || return 20 chmod u=rw,g=r,o= "${sudoersFile}" || return 20
} }
function getFirstFreeRoleAccountID() { function getFirstFreeRoleAccountID() {
dscl . -list '/Users' UniqueID | grep '_.*' | sort -n -k2 | awk -v i=401 '$2>200 && $2<401 {if(i < $2) { print i; nextfile} else i=$2+1;}' dscl . -list '/Users' UniqueID | grep '_.*' | sort -n -k2 | awk -v i=401 '$2>200 && $2<401 {if(i < $2) { print i; nextfile} else i=$2+1;}'
} }
function createHomebrewUser() { function createHomebrewUser() {
local username=$1 local username=$1
local userID=`getFirstFreeRoleAccountID` local userID=`getFirstFreeRoleAccountID`
sysadminctl -addUser "${username}" -fullName "Homebrew User" -shell /usr/bin/false -home '/var/empty' -roleAccount -UID "${userID}" > /dev/null 2>&1 sysadminctl -addUser "${username}" -fullName "Homebrew User" -shell /usr/bin/false -home '/var/empty' -roleAccount -UID "${userID}" > /dev/null 2>&1
} }
function createHomebrewUserIfNeccessary() { function createHomebrewUserIfNeccessary() {
if ! doesUserExist ${homebrew_username}; then if ! doesUserExist ${homebrew_username}; then
lop -y body:warn -y body -- -i "No Homebrew user named ${homebrew_username} found." -i 'Will create user.' lop -y body:warn -y body -- -i "No Homebrew user named ${homebrew_username} found." -i 'Will create user.'
indicateActivity 'Creating Homebrew user' createHomebrewUser ${homebrew_username} || return 10 indicateActivity 'Creating Homebrew user' createHomebrewUser ${homebrew_username} || return 10
else else
lop -y body:note -y body -- -i "Homebrew user named ${homebrew_username} already exists." -i 'Skipping.' lop -y body:note -y body -- -i "Homebrew user named ${homebrew_username} already exists." -i 'Skipping.'
fi fi
} }
function ensureDirectoryWithDefaultMod() { function ensureDirectoryWithDefaultMod() {
local itemPath=${1} local itemPath=${1}
mkdir -p ${itemPath} mkdir -p ${itemPath}
ensureHomebrewOwnershipAndPermission ${itemPath} ensureHomebrewOwnershipAndPermission ${itemPath}
} }
function ensureHomebrewOwnershipAndPermission() { function ensureHomebrewOwnershipAndPermission() {
local itemPath=${1} local itemPath=${1}
local username=${homebrew_username} local username=${homebrew_username}
[[ -f ${itemPath} || -d ${itemPath} ]] || return 1 [[ -f ${itemPath} || -d ${itemPath} ]] || return 1
chown -R "${username}:admin" ${itemPath} chown -R "${username}:admin" ${itemPath}
chmod u=rwx,go=rx ${itemPath} chmod u=rwx,go=rx ${itemPath}
} }
function ensureHomebrewCacheDirectory() { function ensureHomebrewCacheDirectory() {
ensureDirectoryWithDefaultMod "${homebrew_cache}" ensureDirectoryWithDefaultMod "${homebrew_cache}"
runAsHomebrewUser touch "${homebrew_cache}/.cleaned" runAsHomebrewUser touch "${homebrew_cache}/.cleaned"
} }
function ensureHomebrewLogDirectory() { function ensureHomebrewLogDirectory() {
ensureDirectoryWithDefaultMod ${homebrew_log} ensureDirectoryWithDefaultMod ${homebrew_log}
} }
function ensureLocalBinFolder() { function ensureLocalBinFolder() {
local folder="/usr/local/bin" local folder="/usr/local/bin"
if [ ! -d "${folder}" ]; then if [ ! -d "${folder}" ]; then
mkdir -p "${folder}" 2> /dev/null || { mkdir -p "${folder}" 2> /dev/null || {
lop -- -e 'Could not create directory' -e $folder lop -- -e 'Could not create directory' -e $folder
return 10 return 10
} }
chown root:admin "${folder}" chown root:admin "${folder}"
chmod ug=rwx,o=rx "${folder}" chmod ug=rwx,o=rx "${folder}"
fi fi
} }
function getHomebrewRepositoryPath() { function getHomebrewRepositoryPath() {
local uname_machine=$(/usr/bin/uname -m) local uname_machine=$(/usr/bin/uname -m)
if [[ ${uname_machine} == "arm64" ]]; then if [[ ${uname_machine} == "arm64" ]]; then
print -- "/opt/homebrew" print -- "/opt/homebrew"
else else
print "/usr/local/Homebrew" print "/usr/local/Homebrew"
fi fi
} }
function createBrewCallerScript() { function createBrewCallerScript() {
ensureLocalBinFolder ensureLocalBinFolder
local username=${homebrew_username} local username=${homebrew_username}
local brewCallerPath="/usr/local/bin/brew" local brewCallerPath="/usr/local/bin/brew"
[ -f "${brewCallerPath}" ] && rm "${brewCallerPath}" [ -f "${brewCallerPath}" ] && rm "${brewCallerPath}"
cat <<- BREWCALLER > ${brewCallerPath} cat <<- BREWCALLER > ${brewCallerPath}
#!/usr/bin/env zsh #!/usr/bin/env zsh
if [ "\$(id -un)" != "${username}" ]; then if [ "\$(id -un)" != "${username}" ]; then
echo 'brew will be run as ${username} user.' >&2 echo 'brew will be run as ${username} user.' >&2
sudo -E -u "${username}" "\$0" "\$@" sudo -E -u "${username}" "\$0" "\$@"
exit \$? exit \$?
fi fi
export HOMEBREW_CACHE="${homebrew_cache}" export HOMEBREW_CACHE="${homebrew_cache}"
export HOMEBREW_LOGS="${homebrew_log}" export HOMEBREW_LOGS="${homebrew_log}"
@@ -120,109 +120,109 @@ function createBrewCallerScript() {
umask 002 umask 002
"$(getHomebrewRepositoryPath)/bin/brew" "\$@" "$(getHomebrewRepositoryPath)/bin/brew" "\$@"
BREWCALLER BREWCALLER
chown ${username}:admin ${brewCallerPath} chown ${username}:admin ${brewCallerPath}
chmod ug+x,o-x ${brewCallerPath} chmod ug+x,o-x ${brewCallerPath}
} }
function installHomebrewCore() { function installHomebrewCore() {
export NONINTERACTIVE=1 export NONINTERACTIVE=1
[ ! -d $(getHomebrewRepositoryPath) ] || return [ ! -d $(getHomebrewRepositoryPath) ] || return
sudo --preserve-env=NONINTERACTIVE -u "${homebrew_username}" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" sudo --preserve-env=NONINTERACTIVE -u "${homebrew_username}" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
[ -d $(getHomebrewRepositoryPath) ] [ -d $(getHomebrewRepositoryPath) ]
} }
function createLaunchDaemonsPlist() { function createLaunchDaemonsPlist() {
local username=${homebrew_username} local username=${homebrew_username}
local launcherName="de.astzweig.macos.launchdaemons.$1" local launcherName="de.astzweig.macos.launchdaemons.$1"
local launcherPath="/Library/LaunchDaemons/${launcherName}.plist" local launcherPath="/Library/LaunchDaemons/${launcherName}.plist"
[[ -f $launcherPath ]] && return [[ -f $launcherPath ]] && return
local brewCommand="$2" local brewCommand="$2"
cat <<- LAUNCHDPLIST > ${launcherPath} cat <<- LAUNCHDPLIST > ${launcherPath}
<?xml version=\"1.0\" encoding=\"UTF-8\"?> <?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\"> <plist version=\"1.0\">
<dict> <dict>
<key>Label</key> <key>Label</key>
<string>${launcherName}</string> <string>${launcherName}</string>
<key>Program</key> <key>Program</key>
<string>/usr/local/bin/brew</string> <string>/usr/local/bin/brew</string>
<key>ProgramArguments</key> <key>ProgramArguments</key>
<array> <array>
<string>${brewCommand}</string> <string>${brewCommand}</string>
</array> </array>
<key>StartInterval</key> <key>StartInterval</key>
<integer>1800</integer> <integer>1800</integer>
<key>UserName</key> <key>UserName</key>
<string>${username}</string> <string>${username}</string>
<key>GroupName</key> <key>GroupName</key>
<string>admin</string> <string>admin</string>
<key>Umask</key> <key>Umask</key>
<integer>2</integer> <integer>2</integer>
</dict> </dict>
</plist>" </plist>"
LAUNCHDPLIST LAUNCHDPLIST
chown root:wheel ${launcherPath} chown root:wheel ${launcherPath}
chmod u=rw,go=r ${launcherPath} chmod u=rw,go=r ${launcherPath}
launchctl bootstrap system ${launcherPath} launchctl bootstrap system ${launcherPath}
} }
function installHomebrewUpdater() { function installHomebrewUpdater() {
createLaunchDaemonsPlist brew-updater update createLaunchDaemonsPlist brew-updater update
createLaunchDaemonsPlist brew-upgrader upgrade createLaunchDaemonsPlist brew-upgrader upgrade
return return
} }
function configure_system() { function configure_system() {
lop -y h1 -- -i 'Install System Homebrew' lop -y h1 -- -i 'Install System Homebrew'
createHomebrewUserIfNeccessary || return 10 createHomebrewUserIfNeccessary || return 10
indicateActivity 'Ensure Homebrew user is in admin group' ensureUserIsInAdminGroup ${homebrew_username} || return 11 indicateActivity 'Ensure Homebrew user is in admin group' ensureUserIsInAdminGroup ${homebrew_username} || return 11
indicateActivity 'Ensure Homebrew user can run passwordless sudo' ensureUserCanRunPasswordlessSudo ${homebrew_username} || return 12 indicateActivity 'Ensure Homebrew user can run passwordless sudo' ensureUserCanRunPasswordlessSudo ${homebrew_username} || return 12
ensureHomebrewCacheDirectory || return 13 ensureHomebrewCacheDirectory || return 13
ensureHomebrewLogDirectory || return 14 ensureHomebrewLogDirectory || return 14
indicateActivity 'Install Homebrew core' installHomebrewCore || return 15 indicateActivity 'Install Homebrew core' installHomebrewCore || return 15
indicateActivity 'Create brew caller script' createBrewCallerScript || return 16 indicateActivity 'Create brew caller script' createBrewCallerScript || return 16
indicateActivity 'Install Homebrew updater' installHomebrewUpdater || return 17 indicateActivity 'Install Homebrew updater' installHomebrewUpdater || return 17
} }
function getExecPrerequisites() { function getExecPrerequisites() {
cmds=( cmds=(
[dscl]='' [dscl]=''
[dseditgroup]='' [dseditgroup]=''
[chown]='' [chown]=''
[chmod]='' [chmod]=''
[sudo]='' [sudo]=''
[grep]='' [grep]=''
[git]='' [git]=''
[sort]='' [sort]=''
[awk]='' [awk]=''
[launchctl]='' [launchctl]=''
[sysadminctl]='' [sysadminctl]=''
) )
requireRootPrivileges requireRootPrivileges
} }
function getDefaultHomebrewUsername() { function getDefaultHomebrewUsername() {
print -- _homebrew print -- _homebrew
} }
function getDefaultHomebrewCachePath() { function getDefaultHomebrewCachePath() {
print -- /Library/Caches/Homebrew print -- /Library/Caches/Homebrew
} }
function getDefaultHomebrewLogPath() { function getDefaultHomebrewLogPath() {
print -- /var/log/Homebrew print -- /var/log/Homebrew
} }
function getQuestions() { function getQuestions() {
questions=( questions=(
'i: homebrew-username=What shall the Homebrew user'\''s username be? # default:'"$(getDefaultHomebrewUsername)" 'i: homebrew-username=What shall the Homebrew user'\''s username be? # default:'"$(getDefaultHomebrewUsername)"
'i: homebrew-cache=What shall the Homebrew cache directory be? # default:'"$(getDefaultHomebrewCachePath)" 'i: homebrew-cache=What shall the Homebrew cache directory be? # default:'"$(getDefaultHomebrewCachePath)"
'i: homebrew-log=What shall the Homebrew log directory be? # default:'"$(getDefaultHomebrewLogPath)" 'i: homebrew-log=What shall the Homebrew log directory be? # default:'"$(getDefaultHomebrewLogPath)"
) )
} }
function getUsage() { function getUsage() {
read -r -d '' text <<- USAGE read -r -d '' text <<- USAGE
Usage: Usage:
$cmdName show-questions [<modkey> <modans>]... $cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE] --homebrew-username NAME --homebrew-cache PATH --homebrew-log PATH $cmdName [-v] [-d FILE] --homebrew-username NAME --homebrew-cache PATH --homebrew-log PATH
@@ -232,24 +232,24 @@ function getUsage() {
given PREFIX and make the new Homebrew user the owner of that. given PREFIX and make the new Homebrew user the owner of that.
Options: Options:
--homebrew-cache PATH Path to folder that shall be used as the --homebrew-cache PATH Path to folder that shall be used as the
cache for Homebrew [default: $(getDefaultHomebrewCachePath)]. cache for Homebrew [default: $(getDefaultHomebrewCachePath)].
--homebrew-log PATH Path to folder that shall be used as the log --homebrew-log PATH Path to folder that shall be used as the log
directory for Homebrew [default: $(getDefaultHomebrewLogPath)]. directory for Homebrew [default: $(getDefaultHomebrewLogPath)].
--homebrew-username NAME Username of the designated Homebrew user. --homebrew-username NAME Username of the designated Homebrew user.
[default: $(getDefaultHomebrewUsername)]. [default: $(getDefaultHomebrewUsername)].
-d FILE, --logfile FILE Print log message to logfile instead of stdout. -d FILE, --logfile FILE Print log message to logfile instead of stdout.
-v, --verbose Be more verbose. -v, --verbose Be more verbose.
---- ----
$cmdName 0.1.0 $cmdName 0.1.0
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE USAGE
print -- ${text} print -- ${text}
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 } test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@" module_main $0 "$@"
fi fi

View File

@@ -2,89 +2,89 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
function brewInstall() { function brewInstall() {
local identifier="$1" local identifier="$1"
local cask="${2:+--cask}" local cask="${2:+--cask}"
indicateActivity -- "Installing ${identifier}${cask:+ (Cask)}" ${homebrew_path} install -q ${cask} ${identifier} indicateActivity -- "Installing ${identifier}${cask:+ (Cask)}" ${homebrew_path} install -q ${cask} ${identifier}
} }
function installCask() { function installCask() {
brewInstall $1 cask brewInstall $1 cask
} }
function installBrew() { function installBrew() {
brewInstall $1 brewInstall $1
} }
function installCasks() { function installCasks() {
lop -y body:h1 -- -i 'Installing Homebrew casks' lop -y body:h1 -- -i 'Installing Homebrew casks'
if ! isDebug; then if ! isDebug; then
installCask sketch installCask sketch
installCask nova installCask nova
installCask transmit installCask transmit
installCask automattic-texts installCask automattic-texts
installCask synology-drive installCask synology-drive
installCask sf-symbols installCask sf-symbols
installCask prizmo installCask prizmo
fi fi
} }
function installFonts() { function installFonts() {
} }
function installBrews() { function installBrews() {
lop -y body:h1 -- -i 'Installing Homebrew formulas' lop -y body:h1 -- -i 'Installing Homebrew formulas'
installBrew mas installBrew mas
if ! isDebug; then if ! isDebug; then
installBrew python installBrew python
installBrew rcm installBrew rcm
installBrew php installBrew php
installBrew composer installBrew composer
installBrew curl installBrew curl
installBrew exiftool installBrew exiftool
installBrew ffmpeg installBrew ffmpeg
installBrew gnupg installBrew gnupg
installBrew node installBrew node
installBrew nmap installBrew nmap
installBrew tree installBrew tree
fi fi
} }
function configure_system() { function configure_system() {
lop -y h1 -- -i 'Install Homebrew Applications' lop -y h1 -- -i 'Install Homebrew Applications'
pushd -q / pushd -q /
installBrews installBrews
installCasks installCasks
installFonts installFonts
popd -q popd -q
} }
function getExecPrerequisites() { function getExecPrerequisites() {
cmds=( cmds=(
[brew]='' [brew]=''
[find]='' [find]=''
[head]='' [head]=''
[installer]='' [installer]=''
[hdiutil]='' [hdiutil]=''
) )
id -nG | grep admin >&! /dev/null || { lop -- -e 'This module requires the user to be in admin group. Please run again as either root or an admin user.'; return 11 } id -nG | grep admin >&! /dev/null || { lop -- -e 'This module requires the user to be in admin group. Please run again as either root or an admin user.'; return 11 }
checkCommands checkCommands
} }
function getDefaultHomebrewPath() { function getDefaultHomebrewPath() {
local moduleAnswer local moduleAnswer
local hbpath=`whence -p brew` local hbpath=`whence -p brew`
getModuleAnswerByKeyRegEx '_homebrew-prefix$' && hbpath=$moduleAnswer/bin/brew getModuleAnswerByKeyRegEx '_homebrew-prefix$' && hbpath=$moduleAnswer/bin/brew
print -- ${hbpath} print -- ${hbpath}
} }
function getQuestions() { function getQuestions() {
questions=( questions=(
'i: homebrew-path=Which Homebrew binary shall be used? # default:'"$(getDefaultHomebrewPath)" 'i: homebrew-path=Which Homebrew binary shall be used? # default:'"$(getDefaultHomebrewPath)"
) )
} }
function getUsage() { function getUsage() {
read -r -d '' text <<- USAGE read -r -d '' text <<- USAGE
Usage: Usage:
$cmdName show-questions [<modkey> <modans>]... $cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE] --homebrew-path PATH $cmdName [-v] [-d FILE] --homebrew-path PATH
@@ -92,19 +92,19 @@ function getUsage() {
Install cli tools, macOS apps and fonts via Homebrew. Install cli tools, macOS apps and fonts via Homebrew.
Options: Options:
--homebrew-path PATH Path to Homebrew binary [default: $(getDefaultHomebrewPath)]. --homebrew-path PATH Path to Homebrew binary [default: $(getDefaultHomebrewPath)].
-d FILE, --logfile FILE Print log message to logfile instead of stdout. -d FILE, --logfile FILE Print log message to logfile instead of stdout.
-v, --verbose Be more verbose. -v, --verbose Be more verbose.
---- ----
$cmdName 0.1.0 $cmdName 0.1.0
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE USAGE
print -- ${text} print -- ${text}
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 } test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@" module_main $0 "$@"
fi fi

View File

@@ -2,166 +2,166 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
function ensureRightAccess() { function ensureRightAccess() {
local filesystemItem="$1" local filesystemItem="$1"
chown root:wheel ${filesystemItem} chown root:wheel ${filesystemItem}
chmod ugo=rx ${filesystemItem} chmod ugo=rx ${filesystemItem}
} }
function getDataForMicrosoftKeyboard() { function getDataForMicrosoftKeyboard() {
local name="$1" local name="$1"
[ "$name" = "ProductID" ] && echo '0x7a5' [ "$name" = "ProductID" ] && echo '0x7a5'
[ "$name" = "VendorID" ] && echo '0x45e' [ "$name" = "VendorID" ] && echo '0x45e'
[ "$name" = "LaunchdServiceName" ] && echo 'de.astzweig.macos.launchdaemons.microsoft-keymapper' [ "$name" = "LaunchdServiceName" ] && echo 'de.astzweig.macos.launchdaemons.microsoft-keymapper'
[ "$name" = "BinaryName" ] && echo 'remap-keys-microsoft' [ "$name" = "BinaryName" ] && echo 'remap-keys-microsoft'
[ "$name" = "KeyMappings" ] && cat <<- KEYMAPPINGS [ "$name" = "KeyMappings" ] && cat <<- KEYMAPPINGS
{"HIDKeyboardModifierMappingSrc": 0x700000065, "HIDKeyboardModifierMappingDst": 0x7000000e7}, {"HIDKeyboardModifierMappingSrc": 0x700000065, "HIDKeyboardModifierMappingDst": 0x7000000e7},
{"HIDKeyboardModifierMappingSrc": 0x7000000e3, "HIDKeyboardModifierMappingDst": 0x7000000e2}, {"HIDKeyboardModifierMappingSrc": 0x7000000e3, "HIDKeyboardModifierMappingDst": 0x7000000e2},
{"HIDKeyboardModifierMappingSrc": 0x7000000e2, "HIDKeyboardModifierMappingDst": 0x7000000e3} {"HIDKeyboardModifierMappingSrc": 0x7000000e2, "HIDKeyboardModifierMappingDst": 0x7000000e3}
KEYMAPPINGS KEYMAPPINGS
} }
function getDataForLogitechKeyboard() { function getDataForLogitechKeyboard() {
local name="$1" local name="$1"
[ "$name" = "ProductID" ] && echo '0xc52b' [ "$name" = "ProductID" ] && echo '0xc52b'
[ "$name" = "VendorID" ] && echo '0x46d' [ "$name" = "VendorID" ] && echo '0x46d'
[ "$name" = "LaunchdServiceName" ] && echo 'de.astzweig.macos.launchdaemons.logitech-keymapper' [ "$name" = "LaunchdServiceName" ] && echo 'de.astzweig.macos.launchdaemons.logitech-keymapper'
[ "$name" = "BinaryName" ] && echo 'remap-keys-logitech' [ "$name" = "BinaryName" ] && echo 'remap-keys-logitech'
[ "$name" = "KeyMappings" ] && cat <<- KEYMAPPINGS [ "$name" = "KeyMappings" ] && cat <<- KEYMAPPINGS
{"HIDKeyboardModifierMappingSrc": 0x7000000e6, "HIDKeyboardModifierMappingDst": 0x7000000e7}, {"HIDKeyboardModifierMappingSrc": 0x7000000e6, "HIDKeyboardModifierMappingDst": 0x7000000e7},
{"HIDKeyboardModifierMappingSrc": 0x7000000e7, "HIDKeyboardModifierMappingDst": 0x7000000e6} {"HIDKeyboardModifierMappingSrc": 0x7000000e7, "HIDKeyboardModifierMappingDst": 0x7000000e6}
KEYMAPPINGS KEYMAPPINGS
} }
function createXPCConsumer() { function createXPCConsumer() {
[[ -x ${xpcConsumerPath} ]] && return [[ -x ${xpcConsumerPath} ]] && return
clang -framework Foundation -x objective-c -o ${xpcConsumerPath} - <<- BINARY clang -framework Foundation -x objective-c -o ${xpcConsumerPath} - <<- BINARY
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include <xpc/xpc.h> #include <xpc/xpc.h>
int main(int argc, const char * argv[]) { int main(int argc, const char * argv[]) {
@autoreleasepool { @autoreleasepool {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
xpc_set_event_stream_handler("com.apple.iokit.matching", NULL, ^(xpc_object_t _Nonnull object) { xpc_set_event_stream_handler("com.apple.iokit.matching", NULL, ^(xpc_object_t _Nonnull object) {
const char *event = xpc_dictionary_get_string(object, XPC_EVENT_KEY_NAME); const char *event = xpc_dictionary_get_string(object, XPC_EVENT_KEY_NAME);
NSLog(@"%s", event); NSLog(@"%s", event);
dispatch_semaphore_signal(semaphore); dispatch_semaphore_signal(semaphore);
}); });
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if(argc >= 2) { if(argc >= 2) {
execv(argv[1], (char **)argv+1); execv(argv[1], (char **)argv+1);
} }
} }
} }
BINARY BINARY
ensureRightAccess ${xpcConsumerPath} ensureRightAccess ${xpcConsumerPath}
} }
function getProductPlistDict() { function getProductPlistDict() {
cat <<- PLISTDICT cat <<- PLISTDICT
<dict> <dict>
<key>idProduct</key> <key>idProduct</key>
<integer>$(($($dataProvider ProductID)))</integer> <integer>$(($($dataProvider ProductID)))</integer>
<key>idVendor</key> <key>idVendor</key>
<integer>$(($($dataProvider VendorID)))</integer> <integer>$(($($dataProvider VendorID)))</integer>
<key>IOProviderClass</key> <key>IOProviderClass</key>
<string>IOUSBDevice</string> <string>IOUSBDevice</string>
<key>IOMatchLaunchStream</key> <key>IOMatchLaunchStream</key>
<true/> <true/>
</dict> </dict>
PLISTDICT PLISTDICT
} }
function createRemapKeysBinary() { function createRemapKeysBinary() {
cat > ${binaryPath} <<- BINARY cat > ${binaryPath} <<- BINARY
#!/bin/zsh #!/bin/zsh
PRODUCT_MATCHER='{"ProductID":$($dataProvider ProductID),"VendorID":$($dataProvider VendorID)}' PRODUCT_MATCHER='{"ProductID":$($dataProvider ProductID),"VendorID":$($dataProvider VendorID)}'
hasMappingBeenAlreadyActivated() { hasMappingBeenAlreadyActivated() {
local currentModifierKeyMappings="\`hidutil property --get UserKeyMapping -m "\${PRODUCT_MATCHER}" | grep HIDKeyboardModifierMappingDst | wc -l\`" local currentModifierKeyMappings="\`hidutil property --get UserKeyMapping -m "\${PRODUCT_MATCHER}" | grep HIDKeyboardModifierMappingDst | wc -l\`"
test "\${currentModifierKeyMappings}" -gt 1 test "\${currentModifierKeyMappings}" -gt 1
} }
hasMappingBeenAlreadyActivated || \ hasMappingBeenAlreadyActivated || \
hidutil property --matching "\${PRODUCT_MATCHER}" --set '{"UserKeyMapping": [ hidutil property --matching "\${PRODUCT_MATCHER}" --set '{"UserKeyMapping": [
$($dataProvider KeyMappings) $($dataProvider KeyMappings)
]}' > /dev/null 2>&1 ]}' > /dev/null 2>&1
BINARY BINARY
ensureRightAccess ${binaryPath} ensureRightAccess ${binaryPath}
} }
function createLaunchDaemon() { function createLaunchDaemon() {
cat > ${launchDaemonPath} <<- LDAEMON cat > ${launchDaemonPath} <<- LDAEMON
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>Label</key> <key>Label</key>
<string>$($dataProvider LaunchdServiceName)</string> <string>$($dataProvider LaunchdServiceName)</string>
<key>ProgramArguments</key> <key>ProgramArguments</key>
<array> <array>
<string>${xpcConsumerPath}</string> <string>${xpcConsumerPath}</string>
<string>${remapKeysPath}</string> <string>${remapKeysPath}</string>
</array> </array>
<key>LaunchEvents</key> <key>LaunchEvents</key>
<dict> <dict>
<key>com.apple.iokit.matching</key> <key>com.apple.iokit.matching</key>
<dict> <dict>
<key>com.apple.device-attach</key> <key>com.apple.device-attach</key>
$(getProductPlistDict) $(getProductPlistDict)
</dict> </dict>
</dict> </dict>
</dict> </dict>
</plist> </plist>
LDAEMON LDAEMON
ensureRightAccess ${launchDaemonPath} ensureRightAccess ${launchDaemonPath}
} }
function enableLaunchDaemon() { function enableLaunchDaemon() {
launchctl enable system/${launchDaemonPath%.*} launchctl enable system/${launchDaemonPath%.*}
launchctl bootstrap system ${launchDaemonPath} launchctl bootstrap system ${launchDaemonPath}
} }
function createLaunchdService() { function createLaunchdService() {
local launchDaemonPath="/Library/LaunchDaemons/$($dataProvider LaunchdServiceName).plist" local launchDaemonPath="/Library/LaunchDaemons/$($dataProvider LaunchdServiceName).plist"
[[ -f ${launchDaemonPath} ]] || indicateActivity -- 'Create Launch Daemon' createLaunchDaemon [[ -f ${launchDaemonPath} ]] || indicateActivity -- 'Create Launch Daemon' createLaunchDaemon
indicateActivity -- 'Enable Launch Daemon' enableLaunchDaemon indicateActivity -- 'Enable Launch Daemon' enableLaunchDaemon
} }
function configureKeymappers() { function configureKeymappers() {
local mapper= dataProvider= binaryPath= local mapper= dataProvider= binaryPath=
for mapper dataProvider in ${(kv)mappers}; do for mapper dataProvider in ${(kv)mappers}; do
lop -y h1 -- -i "Configure ${mapper} Keymapper" lop -y h1 -- -i "Configure ${mapper} Keymapper"
binaryPath="${dstDir}/$($dataProvider BinaryName)" binaryPath="${dstDir}/$($dataProvider BinaryName)"
createRemapKeysBinary createRemapKeysBinary
createLaunchdService createLaunchdService
done done
} }
function configure_system() { function configure_system() {
typeset -A mappers=( typeset -A mappers=(
[Microsoft]=getDataForMicrosoftKeyboard [Microsoft]=getDataForMicrosoftKeyboard
[Logitech]=getDataForLogitechKeyboard [Logitech]=getDataForLogitechKeyboard
) )
local dstDir='/usr/local/bin' local dstDir='/usr/local/bin'
local xpcConsumerPath="${dstDir}/astzweig-xpc-consumer" local xpcConsumerPath="${dstDir}/astzweig-xpc-consumer"
ensurePathOrLogError ${dstDir} 'Could not create destination dir for remap-keys binary.' || return 10 ensurePathOrLogError ${dstDir} 'Could not create destination dir for remap-keys binary.' || return 10
indicateActivity -- 'Create XPC event consumer' createXPCConsumer indicateActivity -- 'Create XPC event consumer' createXPCConsumer
configureKeymappers configureKeymappers
} }
function getExecPrerequisites() { function getExecPrerequisites() {
cmds=( cmds=(
[clang]='' [clang]=''
[launchctl]='' [launchctl]=''
[cp]='' [cp]=''
[chown]='' [chown]=''
[chmod]='' [chmod]=''
) )
} }
function getUsage() { function getUsage() {
read -r -d '' text <<- USAGE read -r -d '' text <<- USAGE
Usage: Usage:
$cmdName show-questions [<modkey> <modans>]... $cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE] $cmdName [-v] [-d FILE]
@@ -170,18 +170,18 @@ function getUsage() {
macOS nativ hidutil, in order to swap command and option key. macOS nativ hidutil, in order to swap command and option key.
Options: Options:
-d FILE, --logfile FILE Print log message to logfile instead of stdout. -d FILE, --logfile FILE Print log message to logfile instead of stdout.
-v, --verbose Be more verbose. -v, --verbose Be more verbose.
---- ----
$cmdName 0.1.0 $cmdName 0.1.0
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE USAGE
print -- ${text} print -- ${text}
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 } test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@" module_main $0 "$@"
fi fi

View File

@@ -2,47 +2,47 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
function installMASApp() { function installMASApp() {
local currentUser="`who am i | cut -d' ' -f1`" local currentUser="`who am i | cut -d' ' -f1`"
local appName="$1" local appName="$1"
local id="$2" local id="$2"
indicateActivity -- "Install ${appName} app" sudo -u ${currentUser} mas install ${id} indicateActivity -- "Install ${appName} app" sudo -u ${currentUser} mas install ${id}
} }
function configure_system() { function configure_system() {
lop -y h1 -- -i 'Install Mac AppStore Apps' lop -y h1 -- -i 'Install Mac AppStore Apps'
installMASApp Keka 470158793 installMASApp Keka 470158793
if ! isDebug; then if ! isDebug; then
installMASApp Pages 409201541 installMASApp Pages 409201541
installMASApp Numbers 409203825 installMASApp Numbers 409203825
installMASApp Outbank 1094255754 installMASApp Outbank 1094255754
installMASApp 'Final Cut Pro' 424389933 installMASApp 'Final Cut Pro' 424389933
installMASApp GarageBand 682658836 installMASApp GarageBand 682658836
installMASApp Motion 434290957 installMASApp Motion 434290957
installMASApp Compressor 424390742 installMASApp Compressor 424390742
installMASApp 'Logic Pro' 634148309 installMASApp 'Logic Pro' 634148309
fi fi
} }
function getExecPrerequisites() { function getExecPrerequisites() {
cmds=( cmds=(
[mas]='' [mas]=''
[sudo]='' [sudo]=''
[who]='' [who]=''
[cut]='' [cut]=''
) )
} }
function getQuestions { function getQuestions {
questions=( questions=(
'c: logged-in=Have you ensured a user is logged in to the macOS App Store?' 'c: logged-in=Have you ensured a user is logged in to the macOS App Store?'
) )
} }
function getUsage() { function getUsage() {
read -r -d '' text <<- USAGE read -r -d '' text <<- USAGE
Usage: Usage:
$cmdName show-questions [<modkey> <modans>]... $cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE] --logged-in ANS $cmdName [-v] [-d FILE] --logged-in ANS
@@ -59,11 +59,11 @@ function getUsage() {
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE USAGE
print -- ${text} print -- ${text}
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 } test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@" module_main $0 "$@"
fi fi

View File

@@ -2,57 +2,57 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
function ensureRightAccess() { function ensureRightAccess() {
local filesystemItem="$1" local filesystemItem="$1"
chown root:admin "${filesystemItem}" chown root:admin "${filesystemItem}"
chmod ugo=rx "${filesystemItem}" chmod ugo=rx "${filesystemItem}"
} }
function copyUtilityBinaries() { function copyUtilityBinaries() {
for file in ${_DIR}/../bin/*; do for file in ${_DIR}/../bin/*; do
indicateActivity -- "Copying ${file##*/}" cp ${file} ${dstDir} indicateActivity -- "Copying ${file##*/}" cp ${file} ${dstDir}
ensureRightAccess ${file} ensureRightAccess ${file}
done done
} }
function installDocopts() { function installDocopts() {
local destPath='/usr/local/bin/docopts' local destPath='/usr/local/bin/docopts'
[[ -x ${destPath} ]] && return [[ -x ${destPath} ]] && return
indicateActivity -- 'Downloading docpts' curl --output ${destPath} -fsSL ${docopts_url} || return indicateActivity -- 'Downloading docpts' curl --output ${destPath} -fsSL ${docopts_url} || return
ensureRightAccess ${destPath} ensureRightAccess ${destPath}
} }
function configure_system() { function configure_system() {
lop -y h1 -- -i 'Install Utility Binaries' lop -y h1 -- -i 'Install Utility Binaries'
local dstDir='/usr/local/bin' local dstDir='/usr/local/bin'
ensurePathOrLogError ${dstDir} 'Could not install binaries.' || return 10 ensurePathOrLogError ${dstDir} 'Could not install binaries.' || return 10
indicateActivity -- "Set sticky bit to ${dstDir} folder" chmod +t ${dstDir} indicateActivity -- "Set sticky bit to ${dstDir} folder" chmod +t ${dstDir}
installDocopts installDocopts
copyUtilityBinaries copyUtilityBinaries
} }
function getExecPrerequisites() { function getExecPrerequisites() {
cmds=( cmds=(
[cp]='' [cp]=''
[chown]='' [chown]=''
[chmod]='' [chmod]=''
[curl]='' [curl]=''
[install]='' [install]=''
) )
} }
function getDefaultDocoptsURL() { function getDefaultDocoptsURL() {
local fileURL="${DOCOPTS_URL:-https://github.com/astzweig/docopts/releases/download/v.0.7.0/docopts_darwin_amd64}" local fileURL="${DOCOPTS_URL:-https://github.com/astzweig/docopts/releases/download/v.0.7.0/docopts_darwin_amd64}"
print -- ${fileURL} print -- ${fileURL}
} }
function getQuestions() { function getQuestions() {
questions=( questions=(
'i: docopts-url=From which URL shall the docopts binary be downloaded? # default:'"$(getDefaultDocoptsURL)" 'i: docopts-url=From which URL shall the docopts binary be downloaded? # default:'"$(getDefaultDocoptsURL)"
) )
} }
function getUsage() { function getUsage() {
read -r -d '' text <<- USAGE read -r -d '' text <<- USAGE
Usage: Usage:
$cmdName show-questions [<modkey> <modans>]... $cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE] --docopts-url URL $cmdName [-v] [-d FILE] --docopts-url URL
@@ -69,12 +69,12 @@ function getUsage() {
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE USAGE
print -- ${text} print -- ${text}
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
_DIR="${0:A:h}" _DIR="${0:A:h}"
test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 } test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@" module_main $0 "$@"
fi fi

View File

@@ -5,48 +5,48 @@ function addLibToStartupFile() {
} }
function installZshlib() { function installZshlib() {
local zshlibPath=${libDir}/astzweig_zshlib local zshlibPath=${libDir}/astzweig_zshlib
if [[ -d ${ASTZWEIG_ZSHLIB} ]]; then if [[ -d ${ASTZWEIG_ZSHLIB} ]]; then
pushd -q ${ASTZWEIG_ZSHLIB} pushd -q ${ASTZWEIG_ZSHLIB}
zcompile -z -U ${zshlibPath} $(find . -type f -perm +u=x -maxdepth 1) zcompile -z -U ${zshlibPath} $(find . -type f -perm +u=x -maxdepth 1)
libs+=(${zshlibPath}.zwc) libs+=(${zshlibPath}.zwc)
popd -q popd -q
elif [[ -f ${ASTZWEIG_ZSHLIB} ]]; then elif [[ -f ${ASTZWEIG_ZSHLIB} ]]; then
cp ${ASTZWEIG_ZSHLIB} ${zshlibPath}.zwc; cp ${ASTZWEIG_ZSHLIB} ${zshlibPath}.zwc;
fi fi
chmod ugo=r ${zshlibPath}.zwc chmod ugo=r ${zshlibPath}.zwc
} }
function modifyGlobalFpath() { function modifyGlobalFpath() {
local startupFile=/etc/zshenv local startupFile=/etc/zshenv
cat ${startupFile} | grep "${(q)libs}" >&! /dev/null && return cat ${startupFile} | grep "${(q)libs}" >&! /dev/null && return
print -- "fpath+=(${(q)libs})" >> ${startupFile} print -- "fpath+=(${(q)libs})" >> ${startupFile}
chown root:wheel ${startupFile} chown root:wheel ${startupFile}
chmod u=rw,go=r ${startupFile} chmod u=rw,go=r ${startupFile}
} }
function configure_system() { function configure_system() {
lop -y h1 -- -i 'Install ZSh Libraries' lop -y h1 -- -i 'Install ZSh Libraries'
local libDir=/usr/local/share/zsh/site-functions local libDir=/usr/local/share/zsh/site-functions
local libs=() local libs=()
ensurePathOrLogError ${libDir} 'Could not install zsh libraries.' || return 10 ensurePathOrLogError ${libDir} 'Could not install zsh libraries.' || return 10
lop -- -d "ASTZWEIG_ZSHLIB is ${ASTZWEIG_ZSHLIB}" lop -- -d "ASTZWEIG_ZSHLIB is ${ASTZWEIG_ZSHLIB}"
indicateActivity 'Install zshlib' installZshlib indicateActivity 'Install zshlib' installZshlib
indicateActivity 'Modify global fpath' modifyGlobalFpath indicateActivity 'Modify global fpath' modifyGlobalFpath
} }
function getExecPrerequisites() { function getExecPrerequisites() {
cmds=( cmds=(
[cat]='' [cat]=''
[grep]='' [grep]=''
[chown]='' [chown]=''
[chmod]='' [chmod]=''
[install]='' [install]=''
) )
} }
function getUsage() { function getUsage() {
read -r -d '' text <<- USAGE read -r -d '' text <<- USAGE
Usage: Usage:
$cmdName show-questions [<modkey> <modans>]... $cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE] $cmdName [-v] [-d FILE]
@@ -61,12 +61,12 @@ function getUsage() {
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE USAGE
print -- ${text} print -- ${text}
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
_DIR="${0:A:h}" _DIR="${0:A:h}"
test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 } test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@" module_main $0 "$@"
fi fi

View File

@@ -2,35 +2,35 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
function installGoogleFonts() { function installGoogleFonts() {
local fontsDir=/Library/Fonts/Google-Fonts local fontsDir=/Library/Fonts/Google-Fonts
[[ -d ${fontsDir} ]] && return [[ -d ${fontsDir} ]] && return
indicateActivity 'Download Google Fonts' git clone "${git_google_fonts}" "${fontsDir}" indicateActivity 'Download Google Fonts' git clone "${git_google_fonts}" "${fontsDir}"
indicateActivity 'Fix Directory Permissions' find ${fontsDir} -type d -mindepth 1 -exec chmod g+rwx,o+rx {} \; indicateActivity 'Fix Directory Permissions' find ${fontsDir} -type d -mindepth 1 -exec chmod g+rwx,o+rx {} \;
indicateActivity 'Fix File Permissions' find ${fontsDir} -type f -mindepth 1 -exec chmod g+rw,o+r {} \; indicateActivity 'Fix File Permissions' find ${fontsDir} -type f -mindepth 1 -exec chmod g+rw,o+r {} \;
} }
function configure_system() { function configure_system() {
lop -y h1 -- -i 'Install Fonts' lop -y h1 -- -i 'Install Fonts'
} }
function getExecPrerequisites() { function getExecPrerequisites() {
cmds=( cmds=(
[git]='' [git]=''
) )
} }
function getDefaultGitGoogleFontsURL() { function getDefaultGitGoogleFontsURL() {
print -- ${GOOGLE_FONTS_GIT_REMOTE:-https://github.com/google/fonts.git} print -- ${GOOGLE_FONTS_GIT_REMOTE:-https://github.com/google/fonts.git}
} }
function getQuestions() { function getQuestions() {
questions=( questions=(
'i: git-google-fonts=Which Git repository shall be used to install Google Fonts from? # default:'"$(getDefaultGitGoogleFontsURL)" 'i: git-google-fonts=Which Git repository shall be used to install Google Fonts from? # default:'"$(getDefaultGitGoogleFontsURL)"
) )
} }
function getUsage() { function getUsage() {
read -r -d '' text <<- USAGE read -r -d '' text <<- USAGE
Usage: Usage:
$cmdName show-questions [<modkey> <modans>]... $cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE] --git-google-fonts URL $cmdName [-v] [-d FILE] --git-google-fonts URL
@@ -46,12 +46,12 @@ function getUsage() {
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law. License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE USAGE
print -- ${text} print -- ${text}
} }
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
_DIR="${0:A:h}" _DIR="${0:A:h}"
test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 } test -f "${ASTZWEIG_MACOS_SYSTEM_LIB}" || { echo 'This module requires macos-system library. Please run again with macos-system library provieded as a path in ASTZWEIG_MACOS_SYSTEM_LIB env variable.'; return 10 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}" source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@" module_main $0 "$@"
fi fi

View File

@@ -2,107 +2,107 @@
# vi: set ft=zsh tw=80 ts=2 # vi: set ft=zsh tw=80 ts=2
function autoloadZShLib() { function autoloadZShLib() {
test -d "${ASTZWEIG_ZSHLIB}" || { echo "This module needs astzweig/zshlib to work." >&2; return 99 } test -d "${ASTZWEIG_ZSHLIB}" || { echo "This module needs astzweig/zshlib to work." >&2; return 99 }
FPATH="${ASTZWEIG_ZSHLIB}:${FPATH}" FPATH="${ASTZWEIG_ZSHLIB}:${FPATH}"
fpath+=(${ASTZWEIG_ZSHLIB}) fpath+=(${ASTZWEIG_ZSHLIB})
if [[ -d ${ASTZWEIG_ZSHLIB} ]]; then if [[ -d ${ASTZWEIG_ZSHLIB} ]]; then
local funcNames=($(find "${ASTZWEIG_ZSHLIB}" -type f -perm +u=x -maxdepth 1 | awk -F/ '{ print $NF }')) local funcNames=($(find "${ASTZWEIG_ZSHLIB}" -type f -perm +u=x -maxdepth 1 | awk -F/ '{ print $NF }'))
autoload -Uz ${funcNames} autoload -Uz ${funcNames}
elif [[ -f ${ASTZWEIG_ZSHLIB} ]]; then elif [[ -f ${ASTZWEIG_ZSHLIB} ]]; then
autoload -Uzw ${ASTZWEIG_ZSHLIB} autoload -Uzw ${ASTZWEIG_ZSHLIB}
fi fi
} }
function isDebug() { function isDebug() {
test "${MACOS_SYSTEM_DEBUG}" = true -o "${MACOS_SYSTEM_DEBUG}" = 1 test "${MACOS_SYSTEM_DEBUG}" = true -o "${MACOS_SYSTEM_DEBUG}" = 1
} }
function configureLogging() { function configureLogging() {
local output=tostdout level=info local output=tostdout level=info
[ -n "${logfile}" ] && output=${logfile} [ -n "${logfile}" ] && output=${logfile}
[ "${verbose}" = true ] && level=debug [ "${verbose}" = true ] && level=debug
lop setoutput -l ${level} ${output} lop setoutput -l ${level} ${output}
} }
function getModuleAnswerByKeyRegEx() { function getModuleAnswerByKeyRegEx() {
local key value local key value
local searchRegEx=$1 local searchRegEx=$1
for key moduleAnswer in ${modkey:^modans}; do for key moduleAnswer in ${modkey:^modans}; do
[[ $key =~ $searchRegEx ]] && return 0 [[ $key =~ $searchRegEx ]] && return 0
done done
return 1 return 1
} }
function ensurePathOrLogError() { function ensurePathOrLogError() {
local dir=$1 msg=$2 local dir=$1 msg=$2
[[ -d ${dir} ]] || install -m $(umask -S) -d $(getMissingPaths ${dir}) || { [[ -d ${dir} ]] || install -m $(umask -S) -d $(getMissingPaths ${dir}) || {
lop -- -e "$msg" -e "Directory ${dir} does not exist and could not be created." lop -- -e "$msg" -e "Directory ${dir} does not exist and could not be created."
return 10 return 10
} }
} }
function checkHelpPrerequisites() { function checkHelpPrerequisites() {
local -A cmds local -A cmds
getHelpPrerequisites || return getHelpPrerequisites || return
checkCommands ${(k)cmds} checkCommands ${(k)cmds}
} }
function addDocoptsToCmds() { function addDocoptsToCmds() {
cmds+=(docopts '(with -f option supported)') cmds+=(docopts '(with -f option supported)')
} }
function requireRootPrivileges() { function requireRootPrivileges() {
test "`id -u`" -eq 0 || { lop -- -e 'This module requires root access. Please run as root.'; return 11 } test "`id -u`" -eq 0 || { lop -- -e 'This module requires root access. Please run as root.'; return 11 }
} }
whence getHelpPrerequisites >&! /dev/null || function $_() { whence getHelpPrerequisites >&! /dev/null || function $_() {
addDocoptsToCmds addDocoptsToCmds
} }
function checkQuestionsPrerequisites() { function checkQuestionsPrerequisites() {
local -A cmds local -A cmds
getQuestionsPrerequisites || return getQuestionsPrerequisites || return
checkCommands ${(k)cmds} checkCommands ${(k)cmds}
} }
function checkExecPrerequisites() { function checkExecPrerequisites() {
local -A cmds local -A cmds
getExecPrerequisites || return getExecPrerequisites || return
checkCommands ${(k)cmds} checkCommands ${(k)cmds}
} }
function showQuestions() { function showQuestions() {
local questions=() local questions=()
getQuestions getQuestions
for question in ${questions}; do for question in ${questions}; do
hio -- body "${question}" hio -- body "${question}"
done done
} }
function module_main() { function module_main() {
local cmdPath=${1} cmdName=${1:t} hookBag=() local cmdPath=${1} cmdName=${1:t} hookBag=()
local -A traps=() local -A traps=()
preCommandNameHook "$@" || return preCommandNameHook "$@" || return
shift shift
autoloadZShLib || return autoloadZShLib || return
preHelpHook "$@" || return preHelpHook "$@" || return
checkHelpPrerequisites || return checkHelpPrerequisites || return
configureLogging configureLogging
trap 'traps call int; return 70' INT trap 'traps call int; return 70' INT
trap 'traps call term; return 80' TERM trap 'traps call term; return 80' TERM
trap 'traps call exit' EXIT trap 'traps call exit' EXIT
eval "`getUsage $cmdName | docopts -f -V - -h - : "$@"`" eval "`getUsage $cmdName | docopts -f -V - -h - : "$@"`"
preQuestionHook "$@" || return preQuestionHook "$@" || return
checkQuestionsPrerequisites || return checkQuestionsPrerequisites || return
[ "${show_questions}" = true ] && { showQuestions; return } [ "${show_questions}" = true ] && { showQuestions; return }
preExecHook "$@" || return preExecHook "$@" || return
checkExecPrerequisites || return checkExecPrerequisites || return
configure_system configure_system
} }
function { function {
local name local name
for name in preCommandNameHook preHelpHook preQuestionHook preExecHook getQuestionsPrerequisites getExecPrerequisites getQuestions getUsage; do for name in preCommandNameHook preHelpHook preQuestionHook preExecHook getQuestionsPrerequisites getExecPrerequisites getQuestions getUsage; do
whence ${name} >&! /dev/null || function $_() {} whence ${name} >&! /dev/null || function $_() {}
done done
} }