-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.zsh
More file actions
446 lines (361 loc) · 18.2 KB
/
install.zsh
File metadata and controls
446 lines (361 loc) · 18.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
#!/usr/bin/env zsh
###################################################################################################
# Created by Nicholas McDonald | support@kandji.io | Kandji, Inc.
###################################################################################################
#
# Created on 08/10/2020
# Updated on 03/09/2022 - Matt Wilson
#
###################################################################################################
# Tested macOS Versions
###################################################################################################
#
# - 12.2.1
# - 11.6.2
# - 10.15.6
#
###################################################################################################
# Software Information
###################################################################################################
#
# Inspiration for portions of this script taken from homebrew-3.3.sh.
# Original credit to Tony Williams (Honestpuck)
# https://github.com/Honestpuck/homebrew.sh/blob/master/homebrew-3.3.sh
#
# This script silently installs homebrew as the most common local user.
# This script can be set to "every 15 minutes" or "daily" to ensure homebrew remains installed
#
# For the latest on brew Apple Silicon compatibility see: https://github.com/Homebrew/brew/issues/7857
#
###################################################################################################
# License Information
###################################################################################################
# Copyright 2022 Kandji, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this
# software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
# to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or
# substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
###################################################################################################
# CHANGELOG
###################################################################################################
#
# 1.2.0
# - Added check for Apple Silicon homebrew binary location
#
# 1.3.0
# - Code refactor
# - Moved Rosetta2 check to a function
# - Added additional comments for clarification
# - Changed how permissions and ownership are set for brew and dependencies
# - Change how brew doctor is interpreted and let admin know to check logs for more
# info. Apple Silicon has a warning showing that brew is not installed at /usr/local
# and might have unexpected behavior some app installs that have not updated for Apple
# Silicon.
# - Added separate permissions and ownership logic for brew installed on Apple Silicon
# - Added local logging to /Library/Logs/homebrew_install.log
# - Added check for xcode cli tools and will install them if not present
# - Added support for homebrew install as a standard user.
#
# 1.3.1
# - Added logic to determine most common user if a logged in user is not found.
# - Added additional logic to validate OS versions for Xcode CLI tools compatibility
#
# 1.4.0
# - Refactored brew install process so that the curl command is only downloading the latest
# brew tarball file to the correct location
# - Added function that creates the brew enviroment
# - General code refactor
# - Added additional logging output
#
# 1.4.1
# - Minor refactor and bug squashing
#
###################################################################################################
# Script version
VERSION="1.4.1"
###################################################################################################
###################################### VARIABLES ##################################################
###################################################################################################
# Logging config
LOG_NAME="homebrew_install.log"
LOG_DIR="/Library/Logs/com.purplecomputing.mdm"
LOG_PATH="$LOG_DIR/$LOG_NAME"
###################################################################################################
############################ FUNCTIONS - DO NOT MODIFY BELOW ######################################
###################################################################################################
logging() {
# Logging function
#
# Takes in a log level and log string and logs to /Library/Logs/$script_name if a LOG_PATH
# constant variable is not found. Will set the log level to INFO if the first built-in $1 is
# passed as an empty string.
#
# Args:
# $1: Log level. Examples "info", "warning", "debug", "error"
# $2: Log statement in string format
#
# Examples:
# logging "" "Your info log statement here ..."
# logging "warning" "Your warning log statement here ..."
log_level=$(printf "$1" | /usr/bin/tr '[:lower:]' '[:upper:]')
log_statement="$2"
script_name="$(/usr/bin/basename $0)"
prefix=$(/bin/date +"[%b %d, %Y %Z %T $log_level]:")
# see if a LOG_PATH has been set
if [[ -z "${LOG_PATH}" ]]; then
LOG_PATH="/Library/Logs/${script_name}"
fi
if [[ -z $log_level ]]; then
# If the first builtin is an empty string set it to log level INFO
log_level="INFO"
fi
if [[ -z $log_statement ]]; then
# The statement was piped to the log function from another command.
log_statement=""
fi
# echo the same log statement to stdout
/bin/echo "$prefix $log_statement"
# send log statement to log file
printf "%s %s\n" "$prefix" "$log_statement" >>"$LOG_PATH"
}
check_brew_install_status() {
# Check brew insall status.
brew_path="$(/usr/bin/find /usr/local/bin /opt -maxdepth 3 -name brew 2>/dev/null)"
if [[ -n $brew_path ]]; then
# If the brew binary is found just run brew update and exit
logging "info" "Homebrew already installed at $brew_path ..."
echo Status: "Homebrew already installed at $brew_path .." >> /var/tmp/depnotify.log
logging "info" "Updating homebrew ..."
echo Status: "Updating homebrew ..." >> /var/tmp/depnotify.log
/usr/bin/su - "$current_user" -c "$brew_path update --force" | /usr/bin/tee "$LOG_PATH"
logging "info" "Done ..."
exit 0
else
logging "info" "Homebrew is not installed ..."
echo Status: "Homebrew is not installed, installing..." >> /var/tmp/depnotify.log
fi
}
rosetta2_check() {
# Check for and install Rosetta2 if needed.
# $1: processor_brand
# Determine the processor brand
if [[ "$1" == *"Apple"* ]]; then
logging "info" "Apple Processor is present..."
# Check if the Rosetta service is running
check_rosetta_status=$(/usr/bin/pgrep oahd)
# Rosetta Folder location
# Condition to check to see if the Rosetta folder exists. This check was added because
# the Rosetta2 service is already running in macOS versions 11.5 and greater without
# Rosseta2 actually being installed.
rosetta_folder="/Library/Apple/usr/share/rosetta"
if [[ -n $check_rosetta_status ]] && [[ -e $rosetta_folder ]]; then
logging "info" "Rosetta2 is installed... no action needed"
else
logging "info" "Rosetta is not installed... installing now"
# Installs Rosetta
/usr/sbin/softwareupdate --install-rosetta --agree-to-license
# Checks the outcome of the Rosetta install
if [[ $? -ne 0 ]]; then
logging "error" "Rosetta2 install failed..."
exit 1
fi
fi
else
logging "info" "Apple Processor is not present... Rosetta2 not needed"
fi
}
xcode_cli_tools() {
# Check for and install Xcode CLI tools
# Run command to check for an Xcode cli tools path
/usr/bin/xcrun --version >/dev/null 2>&1
# check to see if there is a valid CLI tools path
if [[ $? -eq 0 ]]; then
logging "info" "Valid Xcode path found. No need to install Xcode CLI tools ..."
else
logging "info" "Valid Xcode CLI tools path was not found ..."
# find out when the OS was built
build_year=$(/usr/bin/sw_vers -buildVersion | cut -c 1,2)
# Trick softwareupdate into giving us everything it knows about xcode cli tools
xclt_tmp="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress"
# create the file above
logging "info" "Creating $xclt_tmp ..."
/usr/bin/touch "${xclt_tmp}"
if [[ ${build_year} -ge 19 ]]; then
# for Catalina or newer
logging "info" "Getting the latest Xcode CLI tools available ..."
cmd_line_tools=$(/usr/sbin/softwareupdate -l | awk '/\*\ Label: Command Line Tools/ { $1=$1;print }' | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 9-)
else
# For Mojave or older
logging "info" "Getting the latest Xcode CLI tools available ..."
cmd_line_tools=$(/usr/sbin/softwareupdate -l | /usr/bin/awk '/\*\ Command Line Tools/ { $1=$1;print }' | /usr/bin/grep -i "macOS" | /ussr/bin/sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | /usr/bin/cut -c 2-)
fi
if [[ "${cmd_line_tools}" == "" ]]; then
logging "warning" "Unable to determine available XCode CLI tool updates ..."
logging "warning" "This may require manual installation ..."
else
logging "info" "XCode CLI tool updates found: ${cmd_line_tools}"
fi
if (($(/usr/bin/grep -c . <<<"${cmd_line_tools}") > 1)); then
cmd_line_tools_output="${cmd_line_tools}"
cmd_line_tools=$(printf "${cmd_line_tools_output}" | /usr/bin/tail -1)
logging "info" "Latest Xcode CLI tools found: $cmd_line_tools"
fi
# run softwareupdate to install xcode cli tools
logging "info" "Installing the latest Xcode CLI tools ..."
# Sending this output to the local homebrew_install.log as well as stdout
/usr/sbin/softwareupdate -i "${cmd_line_tools}" --verbose | /usr/bin/tee "/Library/Logs/homebrew_install.log"
# cleanup the temp file
logging "info" "Cleaning up $xclt_tmp ..."
/bin/rm "${xclt_tmp}"
fi
}
set_brew_prefix() {
# Set the homebrew prefix.
# Set the brew prefix to either the Apple Silicon location or the Intel location based on the
# processor_brand information
#
# $1: proccessor brand information
local brew_prefix
if [[ $1 == *"Apple"* ]]; then
# set brew prefix for apple silicon
brew_prefix="/opt/homebrew"
else
# set brew prefix for Intel
brew_prefix="/usr/local"
fi
# return the brew_prefix
/bin/echo "$brew_prefix"
}
create_brew_environment() {
# Create the brew environment.
#
# Create of the directories needed by brew, set the ownership, and set permissions.
#
# $1: brew_prefix
# $2: current_user
logging "info" "Creating directories required by brew ..."
/bin/mkdir -p "${1}/Caskroom" "${1}/Cellar" "${1}/Frameworks" "${1}/Homebrew" "${1}/bin" "${1}/etc" "${1}/include" "${1}/lib" "${1}/opt" "${1}/sbin" "${1}/man/man1" "${1}/share/doc" "${1}/share/man/man1" "${1}/share/zsh/site-functions" "${1}/var" "${1}/var/homebrew/linked"
logging "info" "Creating symlink to ${1}/bin/brew ..."
/bin/ln -s "${1}/Homebrew/bin/brew" "${1}/bin/brew"
logging "info" "Setting homebrew ownership to $2 ..."
/usr/sbin/chown -R "$2" "${1}/Cellar" "${1}/Caskroom" "${1}/Frameworks" "${1}/Homebrew" "${1}/bin" "${1}/bin/brew" "${1}/etc" "${1}/include" "${1}/lib" "${1}/man" "${1}/opt" "${1}/sbin" "${1}/share" "${1}/var"
logging "info" "Setting permissions for brew directories and files ..."
/bin/chmod -R 755 "${1}/Homebrew" "${1}/Cellar" "${1}/Caskroom" "${1}/Frameworks" "${1}/bin" "${1}/bin/brew" "${1}/etc" "${1}/include" "${1}/lib" "${1}/man" "${1}/opt" "${1}/sbin" "${1}/share" "${1}/var"
}
reset_source() {
# Reset the shell source so that brew doctor will find brew in the user's PATH
if [[ "/Users/$current_user/.zshrc" ]]; then
/usr/bin/su - "$current_user" -c source "/Users/$current_user/.zshrc"
fi
if [[ "/Users/$current_user/.bashrc" ]]; then
/usr/bin/su - "$current_user" -c source "/Users/$current_user/.bashrc"
fi
}
brew_doctor() {
# Check Homebrew install status
#
# if on Apple Silicon you may see the following output from brew doctor
#
# Please note that these warnings are just used to help the Homebrew maintainers
# with debugging if you file an issue. If everything you use Homebrew for is
# working fine: please don't worry or file an issue; just ignore this. Thanks!
#
# Warning: Your Homebrew's prefix is not /usr/local.
# Some of Homebrew's bottles (binary packages) can only be used with the default
# prefix (/usr/local).
# You will encounter build failures with some formulae.
# Please create pull requests instead of asking for help on Homebrew's GitHub,
# Twitter or any other official channels. You are responsible for resolving
# any issues you experience while you are running this
# unsupported configuration.
#
# $1: brew_prefix
# $2: current_user
/usr/bin/su - "$2" -c "$1/bin/brew doctor" 2>&1 | /usr/bin/tee "$LOG_PATH"
if [[ $? -ne 0 ]]; then
logging "error" "brew doctor has errors. Review logs to see if action needs to be taken ..."
echo Status: "Brew had some errors..." >> /var/tmp/depnotify.log
else
logging "info" "Homebrew installation complete! Your system is ready to brew."
echo Status: "Homebrew installation complete!" >> /var/tmp/depnotify.log
fi
}
###################################################################################################
############################ MAIN LOGIC - DO NOT MODIFY BELOW #####################################
###################################################################################################
# Do not modify the below, there be dragons. Modify at your own risk.
logging "info" "--- Start homebrew install log ---"
/bin/echo "Log file at /Library/Logs/homebrew_install.log"
# Get the processor brand information
processor_brand="$(/usr/sbin/sysctl -n machdep.cpu.brand_string)"
# Get the current logged in user excluding loginwindow, _mbsetupuser, and root
current_user=$(/usr/sbin/scutil <<<"show State:/Users/ConsoleUser" | /usr/bin/awk '/Name :/ && ! /loginwindow/ && ! /root/ && ! /_mbsetupuser/ { print $3 }' | /usr/bin/awk -F '@' '{print $1}')
# Make sure that we can find the most recent logged in user
if [[ $current_user == "" ]]; then
logging "info" "Current user not logged in ..."
logging "info" "Attempting to determine the most common user..."
# Because someone other than the current user was returned we are going to look at who uses
# the this Mac the most and then set current user to that user.
current_user=$(/usr/sbin/ac -p | /usr/bin/sort -nk 2 | /usr/bin/grep -E -v "total|admin|root|mbsetup|adobe" | /usr/bin/tail -1 | /usr/bin/xargs | /usr/bin/cut -d " " -f1)
fi
logging "info" "Most common user: $current_user"
# Verify the current_user is valid
if /usr/bin/dscl . -read "/Users/$current_user" >/dev/null 2>&1; then
logging "info" "$current_user is a valid user ..."
else
logging "error" "Specified user \"$current_user\" is invalid"
exit 1
fi
logging "info" "Checking to see if Homebew is already install on this Mac ..."
check_brew_install_status
logging "info" "Checking to see if Rosetta2 is needed ..."
rosetta2_check "$processor_brand"
logging "info" "Checking to see if Xcode cli tools are needed ..."
xcode_cli_tools
logging "info" "Determining Homebrew path prefix ..."
brew_prefix=$(set_brew_prefix $processor_brand)
logging "info" "Creating the Homebrew directory at $brew_prefix/Homebrew ..."
/bin/mkdir -p "$brew_prefix/Homebrew"
logging "info" "Downloading homebrew ..."
# Using curl to download the latest release of homebrew tarball and put it in brew_prefix/Homebew
# If brew updates to master to main, the url will need to be adjusted.
/usr/bin/curl --fail --silent --show-error --location --url "https://github.com/Homebrew/brew/tarball/master" | /usr/bin/tar xz --strip 1 -C "$brew_prefix/Homebrew" | /usr/bin/tee "$LOG_PATH"
# checking to see if brew was downloaded successfully
if [[ -f "$brew_prefix/Homebrew/bin/brew" ]]; then
logging "info" "Homebrew binary found at $brew_prefix/Homebrew/bin/brew ..."
logging "info" "Creating the brew environment ..."
create_brew_environment "$brew_prefix" "$current_user"
else
logging "info" "Homebrew binary not found ..."
/bin/echo "Check $LOG_PATH for more details ..."
exit 1
fi
logging "info" "Running brew update --force ..."
/usr/bin/su - "$current_user" -c "$brew_prefix/bin/brew update --force" 2>&1 | /usr/bin/tee "$LOG_PATH"
logging "info" "Running brew cleanup ..."
/usr/bin/su - "$current_user" -c "$brew_prefix/bin/brew cleanup" 2>&1 | /usr/bin/tee "$LOG_PATH"
# Check for missing PATH
get_path_cmd=$(/usr/bin/su - "$current_user" -c "$brew_prefix/bin/brew doctor 2>&1 | /usr/bin/grep 'export PATH=' | /usr/bin/tail -1")
# Add Homebrew's "bin" to target user PATH
if [[ -n ${get_path_cmd} ]]; then
logging "info" "Adding brew to path"
/usr/bin/su - "$current_user" -c "${get_path_cmd}"
fi
logging "info" "Resetting the user's shell source file so that brew doctor can find it..."
reset_source
logging "info" "Running brew doctor to validate the install ..."
brew_doctor "$brew_prefix" "$current_user"
logging "info" "--- End homebrew install log ---"
exit 0