Compare commits

...

30 Commits

Author SHA1 Message Date
T. R. Bernstein
1e1ea39a90 Use HOME env variable in brew caller scripts 2025-10-20 14:02:31 +02:00
T. R. Bernstein
cd8f2b7c0d Fix missing function in setup-user script
Add missing getDefaultGitHomebrewURL.
2025-10-20 14:02:31 +02:00
T. R. Bernstein
48fe726f31 Make sudo-settings module executable 2025-10-20 14:02:31 +02:00
5d9b2d56e5 revert ea86be7b01
revert Use brew caller script without sudo
2025-10-18 23:27:06 +02:00
ea86be7b01 Use brew caller script without sudo 2025-10-18 23:22:59 +02:00
b7664ad8a7 Adapt UID of _homebrew depending on system version 2025-10-18 21:17:54 +02:00
37542773e3 Run homebrew as _homebrew user 2025-10-18 18:21:27 +02:00
7c8d81f080 Remove bun from installation 2025-10-18 18:08:42 +02:00
854c19f35f Use empty home variable for homebrew installation
Homebrew install script does not respect HOMEBREW_CACHE variable. Using an empty home variable ensures we get the result we want, but it is hacky.
2025-10-18 18:03:49 +02:00
87df4b6d7d Remove involuntary unicode char 2025-10-18 17:48:07 +02:00
8bca66a7bb Fix logical error regarding user id of _homebrew 2025-10-18 17:24:34 +02:00
T. R. Bernstein
8def65f683 Install f2 2025-10-18 15:40:16 +02:00
T. R. Bernstein
563a29aa58 Add Parallels to setup-host cmd 2025-10-18 15:30:36 +02:00
T. R. Bernstein
0fbcf46307 Allow proximitywake 2025-10-18 00:57:53 +02:00
T. R. Bernstein
59f627914d Symlink brew caller only on intel cpu 2025-10-18 00:54:21 +02:00
T. R. Bernstein
d4d03b0bb3 Add HOMEBREW_PREFIX and _CELLAR env vars to brew caller 2025-10-18 00:42:09 +02:00
T. R. Bernstein
ad7007a8ee Clean symlinks too for brew caller script 2025-10-18 00:41:34 +02:00
T. R. Bernstein
6cdd744093 Adapt path of brew caller script 2025-10-18 00:32:36 +02:00
T. R. Bernstein
604789d9a9 Always recreate brew caller script 2025-10-18 00:32:19 +02:00
T. R. Bernstein
53b8527e12 Allow passwordless sudo during installation 2025-10-18 00:18:15 +02:00
T. R. Bernstein
e44c48efbd Adapt to new repository 2025-10-17 23:41:52 +02:00
T. R. Bernstein
b008d4ebb0 Add module to adapt sudo settings 2025-10-17 23:41:39 +02:00
T. R. Bernstein
d4a39b5dd5 Use c app to call brew 2025-10-17 23:41:13 +02:00
T. R. Bernstein
1953b01148 Install node using volta 2025-10-17 08:04:35 +02:00
T. R. Bernstein
3f2f23a446 Create setup-user command 2025-10-16 23:25:54 +02:00
T. R. Bernstein
5c1a6998b7 Adapt homebrew installation for macOS on Intel 2025-10-16 23:21:29 +02:00
T. R. Bernstein
60fc6580d8 Make config of hostname a setup step 2025-10-16 23:20:56 +02:00
T. R. Bernstein
dd6875b79a Create setup-host command 2025-10-16 23:18:02 +02:00
T. R. Bernstein
aef49d322f Update brew install list 2025-10-16 23:17:06 +02:00
T. R. Bernstein
82e5747b67 Update MAS app list
Remove Affinity apps, as those are already installed via MDM.
2025-08-31 21:07:38 +02:00
10 changed files with 434 additions and 161 deletions

View File

@@ -1,16 +1,19 @@
# macOS System
Scripts to align a macOS system to Astzweig system configuration.
## Install
```zsh
/bin/zsh -c "$(curl -fsSL https://raw.githubusercontent.com/astzweig/macos-system/main/bootstrap.sh)"
/bin/zsh -c "$(curl -fsSL https://git.tabshift.dev/spacebar/macos-system/raw/branch/main/bootstrap.sh)"
```
## What it does
1. Run all setup from a temporary directory and delete it in the end
## Process
1. `install.sh` queries all modules for their required information
1. The modules print their required information to stdout using the format as described below.
1. `install.sh` parses those informations and tries to read them from a configuration file.
@@ -18,14 +21,17 @@ Scripts to align a macOS system to Astzweig system configuration.
1. `install.sh` then runs the modules with their required informations passed in as parameter values.
## License exclusion
The license does not cover the file [Astzweig.png](resources/user-pictures/Astzweig.png).
## Required Information Format
Modules must print their required information to stdout if they're called with
`show-questions` command. Required information are all information the module
might want to ask the user in order to configure some aspect of the system.
### Schema
The general schema is:
```zsh
@@ -33,14 +39,15 @@ The general schema is:
s: --highlight-color=What color shall your system highlight color be? # choose from: blue,red,light green;
p: --user-password=What color shall your system highlight color be?
```
The letter at the beginning is the question type:
| Question type | Description | Arguments |
| ------------- | ----------- | --------- |
| i (info) | A question where the user has no restrictions on input | `default`: a default answer. |
| p (password) | A question where the user input is not printed to standard output. | - |
| c (confirm) | A yes/no question where the user is allowed to answer yes or no. | - |
| s (select) | A list of choices where the user can select one using numbers. | `choose from`: a comma separated list of possible select values. |
| Question type | Description | Arguments |
| ------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- |
| i (info) | A question where the user has no restrictions on input | `default`: a default answer. |
| p (password) | A question where the user input is not printed to standard output. | - |
| c (confirm) | A yes/no question where the user is allowed to answer yes or no. | - |
| s (select) | A list of choices where the user can select one using numbers. | `choose from`: a comma separated list of possible select values. |
`<PARAMETER NAME>` is the the parameter name, that will receive the user answer
when the module is called. Single char parameter names will be prefixed with a
@@ -50,4 +57,5 @@ the parameter name `highlight-color` becomes `--highlight-color <user response>`
`<QUESTION>` must contain any punctuation you want to show.
[^zshlib-askUser]: Currently supported: info, password, confirm, choose. They map to [zshlib/askUser][zshlib-overview] commands.
[zshlib-overview]: https://github.com/astzweig/zshlib#whats-included

View File

@@ -1,45 +0,0 @@
#!/usr/bin/env zsh
# vi: set ft=zsh tw=80 ts=2
function _installMASApp() {
mas install ${id} &> /dev/null
}
function installMASApp() {
local appName="$1"
local id="$2"
indicateActivity -- "Install ${appName} app" _installMASApp
}
function main() {
lop -y h1 -- -i 'Install Mac AppStore Apps'
installMASApp 'Affinity Designer 2' 1616831348
installMASApp 'Affinity Photo 2' 1616822987
installMASApp 'Affinity Publisher 2' 1606941598
installMASApp 'AusweisApp' 948660805
installMASApp 'Compressor' 424390742
installMASApp 'Developer' 640199958
installMASApp 'Diagrams' 1276248849
installMASApp 'Final Cut Pro' 424389933
installMASApp 'Iconographer Mini' 1541509510
installMASApp 'iMazing Profile Editor' 1487860882
installMASApp 'Infuse' 1136220934
installMASApp 'Keka' 470158793
installMASApp 'Keynote' 409183694
installMASApp 'Mass Rename' 1175416599
installMASApp 'Microsoft Excel' 462058435
installMASApp 'Microsoft Word' 462054704
installMASApp 'Motion' 434290957
installMASApp 'Numbers' 409203825
installMASApp 'Outbank' 1094255754
installMASApp 'Pages' 409201541
installMASApp 'Pixelmator Pro' 1289583905
installMASApp 'SnippetsLab' 1006087419
installMASApp 'WorkingHours' 1495643653
installMASApp 'Xcode' 497799835
}
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
source autoload-zshlib
main "$@"
fi

View File

@@ -1,73 +0,0 @@
#!/usr/bin/env zsh
# vi: set ft=zsh tw=80 ts=2
function configureLogging() {
local output=tostdout level=info
[ -n "${logfile}" ] && output=${logfile}
[ "${verbose}" = true ] && level=debug
lop setoutput -l ${level} ${output}
}
function getUsage() {
local cmdName=$1 text=''
read -r -d '' text <<- USAGE
Usage:
$cmdName [-v] [-d FILE] --hostname NAME
Configure host specific settings.
Options:
--hostname NAME Set NAME as current host's host name.
-d FILE, --logfile FILE Print log message to logfile instead of stdout.
-v, --verbose Be more verbose.
----
$cmdName 0.1.0
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE
print -- ${text}
}
function quitSystemPreferences() {
ps -A -o pid,comm | grep "MacOS/System Settings" | awk '{print "kill -9 " $1}' | /bin/sh
}
function setComputerName() {
scutil --set ComputerName "${hostname}"
scutil --set HostName "${hostname}"
scutil --set LocalHostName "${hostname}"
systemsetup -setcomputername "${hostname}"
systemsetup -setlocalsubnetname "${hostname}"
}
function configureComputerHostname() {
local currentComputerName="`scutil --get ComputerName`"
if [[ "${currentComputerName}" != "${hostname}" ]]; then
lop -- -i 'Hostname of computer has not been set.' -i "Will set to ${hostname}."
indicateActivity -- 'Set computer name' setComputerName
else
lop -- -i 'Hostname of computer seems to have already been set. Skipping.' -i "Hostname: $currentComputerName"
fi
}
function requireRootPrivileges() {
[[ `id -u` -eq 0 ]] || { lop -- -e 'Need root access to change hostname. Aborting.'; return 10 }
}
function main() {
local cmdPath=${1} cmdName=${1:t}
shift
eval "`getUsage $cmdName | docopts -f -V - -h - : "$@"`"
configureLogging
requireRootPrivileges || return $?
lop -y h1 -- -i 'Configure System Settings'
indicateActivity -- 'Quitting System Preferences' quitSystemPreferences
configureComputerHostname
}
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel || "${ZSH_EVAL_CONTEXT}" == cmdarg ]]; then
_DIR="${0:A:h}"
source autoload-zshlib
main $0 "$@"
fi

45
bin/azw-setup-host Executable file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env zsh
# vi: set ft=zsh tw=80 ts=2
function _installMASApp() {
mas install ${id} &> /dev/null
}
function installMASApp() {
local appName="$1"
local id="$2"
indicateActivity -- "Install ${appName} app" _installMASApp
}
function main() {
lop -y h1 -- -i 'Install Mac AppStore Apps'
installMASApp 'AusweisApp' 948660805
installMASApp 'Compressor' 424390742
installMASApp 'Developer' 640199958
installMASApp 'Diagrams' 1276248849
installMASApp 'Final Cut Pro' 424389933
installMASApp 'Iconographer Mini' 1541509510
installMASApp 'iMazing Profile Editor' 1487860882
installMASApp 'Infuse' 1136220934
installMASApp 'Keka' 470158793
installMASApp 'Keynote' 409183694
installMASApp 'Mass Rename' 1175416599
installMASApp 'Microsoft Excel' 462058435
installMASApp 'Microsoft Word' 462054704
installMASApp 'Motion' 434290957
installMASApp 'Numbers' 409203825
installMASApp 'Outbank' 1094255754
installMASApp 'Pages' 409201541
installMASApp 'Parallels Desktop' 1085114709
installMASApp 'Pixelmator Pro' 1289583905
installMASApp 'SnippetsLab' 1006087419
installMASApp 'Tim' 1449619230
installMASApp 'WorkingHours' 1495643653
installMASApp 'Xcode' 497799835
}
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel || "${ZSH_EVAL_CONTEXT}" == cmdarg ]]; then
_DIR="${0:A:h}"
source autoload-zshlib
main "$@"
fi

185
bin/azw-setup-user Executable file
View File

@@ -0,0 +1,185 @@
#!/usr/bin/env zsh
# vi: set ft=zsh tw=80 ts=2
function getExecPrerequisites() {
cmds+=(
[launchctl]=''
[git]=''
[mkdir]=''
[chmod]=''
[]=''
)
}
function hideFolders() {
chflags hidden ${HOME}/bin
}
function checkExecPrerequisites() {
local -A cmds
getExecPrerequisites || return
checkCommands -m 'This script needs %1$s to work. Please install and retry.' ${(k)cmds} || return
}
function configureInstallPrefix() {
local dirPath= desc=
for dirPath desc in ${homebrew_prefix} 'Creating install prefix' ${homebrew_directory} 'Creating Homebrew directory'; do
if [[ ! -d ${dirPath} ]]; then
indicateActivity -- ${desc} createDirAndLogOnFailure ${dirPath}
fi
done
}
function createDirAndLogOnFailure() {
local dirPath=$1
mkdir -p ${dirPath} 2> /dev/null || {
loptty -- -e 'Could not create directory' -e $dirPath
return 10
}
chmod 744 ${dirPath}
}
function downloadHomebrew() {
local git_homebrew_remote=`getDefaultGitHomebrewURL`
pushd -q ${homebrew_directory} || return 10
test -d ".git" && return
git init -q
git config remote.origin.url "${git_homebrew_remote}"
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
git config core.autocrlf false
git config --replace-all homebrew.analyticsmessage false
git config --replace-all homebrew.caskanalyticsmessage false
git fetch --quiet --force origin > /dev/null
git fetch --quiet --force --tags origin > /dev/null
git reset --hard origin/master
popd -q
}
function getCanonicalBrewPath() {
local brewPath=${homebrew_directory}/bin/brew
if [[ $brewPath == $HOME* ]]; then
brewPath="\${HOME}${brewPath#$HOME}"
fi
echo $brewPath
}
function createBrewCallerScript() {
local cmdPath=${HOME}/bin/brew
local brewPath=`getCanonicalBrewPath`
[[ -f ${cmdPath} ]] && rm ${cmdPath}
mkdir -p ${cmdPath:h}
cat <<- BREWCALLER > ${cmdPath}
#!/usr/bin/env zsh
export HOMEBREW_CASK_OPTS="--no-quarantine \${HOMEBREW_CASK_OPTS}"
export HOMEBREW_NO_AUTO_UPDATE=1
export HOMEBREW_NO_ANALYTICS=1
export HOMEBREW_NO_ANALYTICS_THIS_RUN=1
export HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT=1
"${brewPath}" "\$@"
BREWCALLER
chown $(id -un):$(id -gn) ${cmdPath}
chmod u=rwx,go=r ${cmdPath}
}
function createBrewPeriodicScript() {
local brewPath=`getCanonicalBrewPath`
local cmdPath=${HOME}/bin/brew-periodic
[[ -f ${cmdPath} ]] && rm ${cmdPath}
mkdir -p ${cmdPath:h}
cat <<- BREWCALLER > ${cmdPath}
#!/usr/bin/env zsh
local brew='${brewPath}'
\$brew update
\$brew upgrade --greedy
\$brew cleanup
BREWCALLER
chown $(id -un):$(id -gn) ${cmdPath}
chmod u=rwx,go=r ${cmdPath}
}
function updateBrew() {
local brewPath=${homebrew_directory}/bin/brew
${brewPath} update --force --quiet
chmod go-w "${homebrew_directory}/share/zsh"
}
function createLaunchDaemonsPlist() {
local uid=`id -u`
local serviceName="de.astzweig.macos.launchagents.$1"
local launchAgentPath=${HOME}/Library/LaunchAgents
local launcherPath="${launchAgentPath}/${serviceName}.plist"
[[ -d ${launchAgentPath} ]] || mkdir ${launchAgentPath} || return
[[ -f ${launcherPath} ]] && {
launchctl disable gui/${uid}/${serviceName}
rm ${launcherPath}
}
cat <<- LAUNCHDPLIST > ${launcherPath}
<?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">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${serviceName}</string>
<key>ProgramArguments</key>
<array>
<string>${HOME}/bin/brew-periodic</string>
</array>
<key>StartInterval</key>
<integer>1800</integer>
</dict>
</plist>
LAUNCHDPLIST
chmod u=rw,go=r ${launcherPath}
launchctl enable gui/${uid}/${serviceName} &> /dev/null
launchctl bootstrap gui/${uid}/ ${launcherPath} &> /dev/null || true
}
function installHomebrewUpdater() {
createLaunchDaemonsPlist brew-updater
}
function getDefaultHomebrewPrefix() {
print -- ${HOMEBREW_PREFIX:-${HOME}/Library/Application Support}
}
function getHomebrewDirectoryPath() {
print -- ${homebrew_prefix:-$(getDefaultHomebrewPrefix)}/Homebrew
}
function getDefaultGitHomebrewURL() {
print -- ${HOMEBREW_BREW_GIT_REMOTE:-https://github.com/Homebrew/brew.git}
}
function configureFolders() {
local dirPath
for dirPath in ${HOME}/bin ${HOME}/.vim/{backups,swaps}; do
[[ ! -d ${dirPath} ]] && indicateActivity -- "Creating ${dirPath}" mkdir ${dirPath}
done
indicateActivity -- 'Hide folders' hideFolders
}
function installNode() {
volta install node
}
function main() {
lop -y h1 -- -i 'Setup User'
[[ $(id -u) -eq 0 ]] && { loptty -- -e 'Command must not be run as root.' -e $dirPath; return 16 }
configureFolders
indicateActivity -- 'Install node' installNode
local homebrew_prefix=`getDefaultHomebrewPrefix`
local homebrew_directory=`getHomebrewDirectoryPath`
checkExecPrerequisites || return 17
configureInstallPrefix || return 10
indicateActivity -- 'Downloading Homebrew' downloadHomebrew || return 11
indicateActivity -- 'Create brew caller script' createBrewCallerScript || return 12
indicateActivity -- 'Create brew periodic script' createBrewPeriodicScript || return 13
indicateActivity -- 'Update brew' updateBrew || return 14
indicateActivity -- 'Install Homebrew updater' installHomebrewUpdater || return 15
}
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel || "${ZSH_EVAL_CONTEXT}" == cmdarg ]]; then
_DIR="${0:A:h}"
source autoload-zshlib
main "$@"
fi

View File

@@ -107,7 +107,7 @@ function ensureDocopts() {
}
function cloneMacOSSystemRepo() {
local repoUrl="${MACOS_SYSTEM_REPO_URL:-https://github.com/astzweig/macos-system.git}"
local repoUrl="${MACOS_SYSTEM_REPO_URL:-https://git.tabshift.dev/spacebar/macos-system.git}"
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
}

54
modules/00a-sudo-settings.sh Executable file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/env zsh
# vi: set ft=zsh tw=80 ts=2
function ensureTRBernsteinCanUpdateSystem() {
local username=$1
local sudoersFile='/etc/sudoers.d/allow-softwareupdate-for-all'
[[ -f ${sudoersFile} ]] && return
cat <<- SUDOERS > "${sudoersFile}"
trbernstein ALL=(root) NOPASSWD: /usr/sbin/softwareupdate -irR --user admin
SUDOERS
chown root:wheel "${sudoersFile}" || return 10
chmod u=rw,g=r,o= "${sudoersFile}" || return 20
}
function configure_system() {
lop -y h1 -- -i 'Configure sudoers files'
ensurePathOrLogError ${dstDir} 'Could not install binaries.' || return 10
indicateActivity -- "Allow trbernstein user to install updates using softwareupdate" ensureTRBernsteinCanUpdateSystem
}
function getExecPrerequisites() {
cmds=(
[cat]=''
[chown]=''
[chmod]=''
)
}
function getUsage() {
read -r -d '' text <<- USAGE
Usage:
$cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE]
Add sudo rules to:
1. allow trbernstein user to install updates using softwareupdate
Options:
-d FILE, --logfile FILE Print log message to logfile instead of stdout.
-v, --verbose Be more verbose.
----
$cmdName 0.1.0
Copyright (C) 2022 Rezart Qelibari, Astzweig GmbH & Co. KG
License EUPL-1.2. There is NO WARRANTY, to the extent permitted by law.
USAGE
print -- ${text}
}
if [[ "${ZSH_EVAL_CONTEXT}" == toplevel ]]; then
_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 }
source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@"
fi

View File

@@ -39,7 +39,7 @@ function configurePowerManagement() {
${cmd} sleep 0
${cmd} womp 0
${cmd} acwake 0
${cmd} proximitywake 0
${cmd} proximitywake 1
${cmd} destroyfvkeyonstandby 1
pmset -b acwake 1
${cmd} lidwake 1
@@ -65,6 +65,24 @@ function configureMacOSFirewall() {
${cmd} --setallowsignedapp on
}
function setComputerName() {
scutil --set ComputerName "${hostname}"
scutil --set HostName "${hostname}"
scutil --set LocalHostName "${hostname}"
systemsetup -setcomputername "${hostname}"
systemsetup -setlocalsubnetname "${hostname}"
}
function configureComputerHostname() {
local currentComputerName="`scutil --get ComputerName`"
if [[ "${currentComputerName}" != "${hostname}" ]]; then
lop -- -i 'Hostname of computer has not been set.' -i "Will set to ${hostname}."
indicateActivity -- 'Set computer name' setComputerName
else
lop -- -i 'Hostname of computer seems to have already been set. Skipping.' -i "Hostname: $currentComputerName"
fi
}
function configure_system() {
lop -y h1 -- -i 'Configure System Settings'
indicateActivity -- 'Quitting System Preferences' quitSystemPreferences
@@ -73,6 +91,13 @@ function configure_system() {
indicateActivity -- 'Configuring login window' configureLoginWindow
indicateActivity -- 'Configure global umask' launchctl config user umask 027
indicateActivity -- 'Configure macOS firewall' configureMacOSFirewall
configureComputerHostname
}
function getQuestions() {
questions=(
'i: hostname=What shall the systems hostname be?'
)
}
function getUsage() {
@@ -80,11 +105,12 @@ function getUsage() {
read -r -d '' text <<- USAGE
Usage:
$cmdName show-questions [<modkey> <modans>]...
$cmdName [-v] [-d FILE]
$cmdName [-v] [-d FILE] --hostname NAME
Set energy, network and basic preferences.
Options:
--hostname NAME Set NAME as current host's host name.
-d FILE, --logfile FILE Print log message to logfile instead of stdout.
-v, --verbose Be more verbose.
----

View File

@@ -35,13 +35,18 @@ function ensureUserCanRunPasswordlessSudo() {
chmod u=rw,g=r,o= "${sudoersFile}" || return 20
}
function ensureUserCanNoLongerRunPasswordlessSudo() {
local username=$1
local sudoersFile="/etc/sudoers.d/no-auth-sudo-for-${username}"
[[ ! -f ${sudoersFile} ]] || rm ${sudoersFile}
}
function getFirstFreeRoleAccountID() {
local minUserID=450
local maxUserID=499
local uname_machine=$(/usr/bin/uname -m)
if [[ ${uname_machine} == "arm64" ]]; then
minUserID=200
maxUserID=400
local minUserID=200
local maxUserID=400
if is-at-least 13.0 $(sw_vers -productVersion); then
minUserID=450
maxUserID=499
fi
dscl . -list '/Users' UniqueID | grep '_.*' | sort -n -k2 | awk -v i=${minUserID} '$2>='${minUserID}' && $2<'${maxUserID}' {if(i < $2) { print i; nextfile} else i=$2+1;} END {if(i <= '${maxUserID}' && ($2 < '${minUserID}' || $2 > '${maxUserID}')) print i;}'
}
@@ -96,28 +101,93 @@ function getHomebrewRepositoryPath() {
function createBrewCallerScript() {
ensureLocalBinFolder
local uname_machine=$(/usr/bin/uname -m)
local username=${homebrew_username}
local brewCallerPath="/usr/local/bin/brew"
[ -f "${brewCallerPath}" ] && rm "${brewCallerPath}"
cat <<- BREWCALLER > ${brewCallerPath}
#!/usr/bin/env zsh
if [ "\$(id -un)" != "${username}" ]; then
echo 'brew will be run as ${username} user.' >&2
sudo -E -u "${username}" "\$0" "\$@"
exit \$?
fi
export HOMEBREW_CACHE="${homebrew_cache}"
export HOMEBREW_LOGS="${homebrew_log}"
export HOMEBREW_CASK_OPTS="--no-quarantine \${HOMEBREW_CASK_OPTS}"
export HOMEBREW_NO_AUTO_UPDATE=1
export HOMEBREW_NO_ANALYTICS=1
export HOMEBREW_NO_ANALYTICS_THIS_RUN=1
export HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT=1
umask 002
"$(getHomebrewRepositoryPath)/bin/brew" "\$@"
local homebrewRepositoryPath="$(getHomebrewRepositoryPath)"
local brewCallerPath="${homebrewRepositoryPath}/bin/brew-caller"
local brewCallerSymlink="/usr/local/bin/brew"
[[ -f "${brewCallerPath}" ]] && rm "${brewCallerPath}"
[[ -f "${brewCallerSymlink}" || -h "${brewCallerSymlink}" ]] && rm "${brewCallerSymlink}"
[[ ${uname_machine} == "arm64" ]] && brewCallerPath=${brewCallerSymlink}
cat <<- BREWCALLER | clang -x c -O2 -Wall -o "${brewCallerPath}" -
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
extern char **environ;
char *format_env(const char *env_name, const char *format) {
if (!env_name || !format) return NULL;
const char *value = getenv(env_name);
if (!value) return NULL;
int formatted_len = snprintf(NULL, 0, format, value);
if (formatted_len < 0) return NULL;
char *formatted_value = malloc(formatted_len + 1);
if (!formatted_value) return NULL;
snprintf(formatted_value, formatted_len + 1, format, value);
size_t result_len = strlen(env_name) + 1 + formatted_len;
char *result = malloc(result_len + 1);
if (!result) { free(formatted_value); return NULL; }
sprintf(result, "%s=%s", env_name, formatted_value);
free(formatted_value);
return result;
}
#define HOMEBREW_USER "${username}"
#define HOMEBREW_DIR "${homebrewRepositoryPath}/bin"
#define BREW_PATH HOMEBREW_DIR "/brew"
int main(int argc, char *argv[]) {
struct passwd *pw;
uid_t target_uid;
gid_t target_gid;
pw = getpwnam(HOMEBREW_USER);
if (!pw) { fprintf(stderr, "Error: user %s not found\n", HOMEBREW_USER); return 1; }
target_uid = pw->pw_uid;
target_gid = pw->pw_gid;
char *new_environ[] = {
"PATH=" HOMEBREW_DIR ":/usr/bin:/bin:/usr/sbin:/sbin",
"HOMEBREW_CACHE=${homebrew_cache}",
"HOMEBREW_LOGS=${homebrew_log}",
format_env("HOME", "%s"),
format_env("HOMEBREW_CASK_OPTS", "--no-quarantine %s"),
"HOMEBREW_PREFIX=${homebrewRepositoryPath}",
"HOMEBREW_CELLAR=${homebrewRepositoryPath}/Cellar",
"HOMEBREW_NO_AUTO_UPDATE=1",
"HOMEBREW_NO_ANALYTICS=1",
"HOMEBREW_NO_ANALYTICS_THIS_RUN=1",
"HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT=1",
NULL
};
if (setegid(target_gid) != 0) { fprintf(stderr, "setegid(%d): %s\n", target_gid, strerror(errno)); return 1; }
if (seteuid(target_uid) != 0) { fprintf(stderr, "seteuid(%d): %s\n", target_uid, strerror(errno)); return 1; }
char **newargv = malloc((argc + 1) * sizeof(char *));
if (!newargv) { fprintf(stderr, "malloc failed for new argv\n"); return 1; }
newargv[0] = (char *)BREW_PATH;
for (int i = 1; i < argc; ++i) newargv[i] = argv[i];
newargv[argc] = NULL;
umask(0002);
execve(BREW_PATH, newargv, new_environ);
fprintf(stderr, "execv(%s) failed: %s\n", BREW_PATH, strerror(errno));
return 1;
}
BREWCALLER
chown root:admin ${brewCallerPath}
chmod ug+x,o-x ${brewCallerPath}
chmod 4550 ${brewCallerPath}
[[ ${uname_machine} == "arm64" ]] || ln -s "${brewCallerPath}" "${brewCallerSymlink}"
}
function createBrewPeriodicScript() {
@@ -136,9 +206,8 @@ function createBrewPeriodicScript() {
}
function installHomebrewCore() {
export NONINTERACTIVE=1
[ ! -d $(getHomebrewRepositoryPath) ] || return
sudo --preserve-env=NONINTERACTIVE -u "${homebrew_username}" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
[ ! -d $(getHomebrewRepositoryPath) ] || return
NONINTERACTIVE=1 HOME= sudo --preserve-env=NONINTERACTIVE,HOME -u "${homebrew_username}" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
[ -d $(getHomebrewRepositoryPath) ]
}
@@ -213,6 +282,7 @@ function configure_system() {
ensureHomebrewCacheDirectory || return 13
ensureHomebrewLogDirectory || return 14
indicateActivity 'Install Homebrew core' installHomebrewCore || return 15
indicateActivity 'Ensure Homebrew user can nolonger run passwordless sudo' ensureUserCanNoLongerRunPasswordlessSudo ${homebrew_username} || return 20
indicateActivity 'Create brew caller script' createBrewCallerScript || return 16
indicateActivity 'Create brew periodic script' createBrewPeriodicScript || return 17
indicateActivity 'Install Homebrew updater' installHomebrewUpdater || return 18
@@ -285,6 +355,7 @@ function getUsage() {
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 }
autoload is-at-least
source "${ASTZWEIG_MACOS_SYSTEM_LIB}"
module_main $0 "$@"
fi

View File

@@ -4,7 +4,7 @@
function brewInstall() {
local identifier="$1"
local cask="${2:+--cask}"
indicateActivity -- "Installing ${identifier}${cask:+ (Cask)}" ${homebrew_path} install -q ${cask} ${identifier}
HOME=/Users/Shared indicateActivity -- "Installing ${identifier}${cask:+ (Cask)}" sudo -u _homebrew ${homebrew_path} install -q ${cask} ${identifier}
}
function installCask() {
@@ -26,7 +26,9 @@ function installCasks() {
installCask devonthink
installCask firefox
installCask hazel
installCask hopper-disassembler
installCask istat-menus
installCask jdownloader
installCask launchcontrol
installCask makemkv
installCask network-radar
@@ -34,6 +36,7 @@ function installCasks() {
installCask orbstack
installCask rapidapi
installCask rectangle
installCask serverbuddy
installCask sf-symbols
installCask sketch
installCask suspicious-package
@@ -56,13 +59,14 @@ function installBrews() {
lop -y body:h1 -- -i 'Installing Homebrew formulas'
installBrew mas
if ! isDebug; then
installBrew bun
installBrew chezmoi
installBrew cocogitto
installBrew composer
installBrew curl
installBrew docker
installBrew docker-buildx
installBrew exiftool
installBrew f2
installBrew ffmpeg
installBrew gnupg
installBrew go
@@ -71,18 +75,16 @@ function installBrews() {
installBrew imap-backup
installBrew imapsync
installBrew nmap
installBrew node
installBrew pdfgrep
installBrew php
installBrew python
installBrew qpdf
installBrew rcm
installBrew ruby
installBrew 'hay-kot/scaffold-tap/scaffold'
installBrew 'lemoony/tap/snipkit'
installBrew sqlite
installBrew tree
installBrew tidy-html5
installBrew volta
installBrew yq
fi
}