Last active 4 weeks ago

Revision 4b1c0d489368ef210e2ba6233573dfd0b7606a13

brew.sh Raw
1#!/bin/bash
2
3# We don't need return codes for "$(command)", only stdout is needed.
4# Allow `[[ -n "$(command)" ]]`, `func "$(command)"`, pipes, etc.
5# shellcheck disable=SC2312
6
7set -u
8
9abort() {
10 printf "%s\n" "$@" >&2
11 exit 1
12}
13
14# Fail fast with a concise message when not using bash
15# Single brackets are needed here for POSIX compatibility
16# shellcheck disable=SC2292
17if [ -z "${BASH_VERSION:-}" ]
18then
19 abort "Bash is required to interpret this script."
20fi
21
22# Check if script is run with force-interactive mode in CI
23if [[ -n "${CI-}" && -n "${INTERACTIVE-}" ]]
24then
25 abort "Cannot run force-interactive mode in CI."
26fi
27
28# Check if both `INTERACTIVE` and `NONINTERACTIVE` are set
29# Always use single-quoted strings with `exp` expressions
30# shellcheck disable=SC2016
31if [[ -n "${INTERACTIVE-}" && -n "${NONINTERACTIVE-}" ]]
32then
33 abort 'Both `$INTERACTIVE` and `$NONINTERACTIVE` are set. Please unset at least one variable and try again.'
34fi
35
36# Check if script is run in POSIX mode
37if [[ -n "${POSIXLY_CORRECT+1}" ]]
38then
39 abort 'Bash must not run in POSIX mode. Please unset POSIXLY_CORRECT and try again.'
40fi
41
42# Check for file that prevents Homebrew installation
43if [[ -f "/etc/homebrew/brew.no_install" ]]
44then
45 BREW_NO_INSTALL="$(cat "/etc/homebrew/brew.no_install" 2>/dev/null)"
46 if [[ -n "${BREW_NO_INSTALL}" ]]
47 then
48 abort "Homebrew cannot be installed because ${BREW_NO_INSTALL}."
49 else
50 abort "Homebrew cannot be installed because /etc/homebrew/brew.no_install exists!"
51 fi
52fi
53
54# string formatters
55if [[ -t 1 ]]
56then
57 tty_escape() { printf "\033[%sm" "$1"; }
58else
59 tty_escape() { :; }
60fi
61tty_mkbold() { tty_escape "1;$1"; }
62tty_underline="$(tty_escape "4;39")"
63tty_blue="$(tty_mkbold 34)"
64tty_red="$(tty_mkbold 31)"
65tty_bold="$(tty_mkbold 39)"
66tty_reset="$(tty_escape 0)"
67
68shell_join() {
69 local arg
70 printf "%s" "$1"
71 shift
72 for arg in "$@"
73 do
74 printf " %s" "${arg// /\ }"
75 done
76}
77
78chomp() {
79 printf "%s" "${1/"$'\n'"/}"
80}
81
82ohai() {
83 printf "${tty_blue}==>${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")"
84}
85
86warn() {
87 printf "${tty_red}Warning${tty_reset}: %s\n" "$(chomp "$1")" >&2
88}
89
90usage() {
91 cat <<EOS
92Homebrew Installer
93Usage: [NONINTERACTIVE=1] [CI=1] install.sh [options]
94 -h, --help Display this message.
95 NONINTERACTIVE Install without prompting for user input
96 CI Install in CI mode (e.g. do not prompt for user input)
97EOS
98 exit "${1:-0}"
99}
100
101while [[ $# -gt 0 ]]
102do
103 case "$1" in
104 -h | --help) usage ;;
105 *)
106 warn "Unrecognized option: '$1'"
107 usage 1
108 ;;
109 esac
110done
111
112# Check if script is run non-interactively (e.g. CI)
113# If it is run non-interactively we should not prompt for passwords.
114# Always use single-quoted strings with `exp` expressions
115# shellcheck disable=SC2016
116if [[ -z "${NONINTERACTIVE-}" ]]
117then
118 if [[ -n "${CI-}" ]]
119 then
120 warn 'Running in non-interactive mode because `$CI` is set.'
121 NONINTERACTIVE=1
122 elif [[ ! -t 0 ]]
123 then
124 if [[ -z "${INTERACTIVE-}" ]]
125 then
126 warn 'Running in non-interactive mode because `stdin` is not a TTY.'
127 NONINTERACTIVE=1
128 else
129 warn 'Running in interactive mode despite `stdin` not being a TTY because `$INTERACTIVE` is set.'
130 fi
131 fi
132else
133 ohai 'Running in non-interactive mode because `$NONINTERACTIVE` is set.'
134fi
135
136# USER isn't always set so provide a fall back for the installer and subprocesses.
137if [[ -z "${USER-}" ]]
138then
139 USER="$(chomp "$(id -un)")"
140 export USER
141fi
142
143# First check OS.
144OS="$(uname)"
145if [[ "${OS}" == "Linux" ]]
146then
147 HOMEBREW_ON_LINUX=1
148elif [[ "${OS}" == "Darwin" ]]
149then
150 HOMEBREW_ON_MACOS=1
151else
152 abort "Homebrew is only supported on macOS and Linux."
153fi
154
155# Required installation paths. To install elsewhere (which is unsupported)
156# you can untar https://github.com/Homebrew/brew/tarball/main
157# anywhere you like.
158if [[ -n "${HOMEBREW_ON_MACOS-}" ]]
159then
160 UNAME_MACHINE="$(/usr/bin/uname -m)"
161
162 if [[ "${UNAME_MACHINE}" == "arm64" ]]
163 then
164 # On ARM macOS, this script installs to /opt/homebrew only
165 HOMEBREW_PREFIX="/opt/homebrew"
166 HOMEBREW_REPOSITORY="${HOMEBREW_PREFIX}"
167 else
168 # On Intel macOS, this script installs to /usr/local only
169 HOMEBREW_PREFIX="/usr/local"
170 HOMEBREW_REPOSITORY="${HOMEBREW_PREFIX}/Homebrew"
171 fi
172 HOMEBREW_CACHE="${HOME}/Library/Caches/Homebrew"
173
174 STAT_PRINTF=("/usr/bin/stat" "-f")
175 PERMISSION_FORMAT="%A"
176 CHOWN=("/usr/sbin/chown")
177 CHGRP=("/usr/bin/chgrp")
178 GROUP="admin"
179 TOUCH=("/usr/bin/touch")
180 INSTALL=("/usr/bin/install" -d -o "root" -g "wheel" -m "0755")
181else
182 UNAME_MACHINE="$(uname -m)"
183
184 # On Linux, this script installs to /home/linuxbrew/.linuxbrew only
185 HOMEBREW_PREFIX="/home/linuxbrew/.linuxbrew"
186 HOMEBREW_REPOSITORY="${HOMEBREW_PREFIX}/Homebrew"
187 HOMEBREW_CACHE="${HOME}/.cache/Homebrew"
188
189 STAT_PRINTF=("/usr/bin/stat" "-c")
190 PERMISSION_FORMAT="%a"
191 CHOWN=("/bin/chown")
192 CHGRP=("/bin/chgrp")
193 GROUP="$(id -gn)"
194 TOUCH=("/bin/touch")
195 INSTALL=("/usr/bin/install" -d -o "${USER}" -g "${GROUP}" -m "0755")
196fi
197CHMOD=("/bin/chmod")
198MKDIR=("/bin/mkdir" "-p")
199HOMEBREW_BREW_DEFAULT_GIT_REMOTE="https://github.com/Homebrew/brew"
200HOMEBREW_CORE_DEFAULT_GIT_REMOTE="https://github.com/Homebrew/homebrew-core"
201
202# Use remote URLs of Homebrew repositories from environment if set.
203HOMEBREW_BREW_GIT_REMOTE="${HOMEBREW_BREW_GIT_REMOTE:-"${HOMEBREW_BREW_DEFAULT_GIT_REMOTE}"}"
204HOMEBREW_CORE_GIT_REMOTE="${HOMEBREW_CORE_GIT_REMOTE:-"${HOMEBREW_CORE_DEFAULT_GIT_REMOTE}"}"
205# The URLs with and without the '.git' suffix are the same Git remote. Do not prompt.
206if [[ "${HOMEBREW_BREW_GIT_REMOTE}" == "${HOMEBREW_BREW_DEFAULT_GIT_REMOTE}.git" ]]
207then
208 HOMEBREW_BREW_GIT_REMOTE="${HOMEBREW_BREW_DEFAULT_GIT_REMOTE}"
209fi
210if [[ "${HOMEBREW_CORE_GIT_REMOTE}" == "${HOMEBREW_CORE_DEFAULT_GIT_REMOTE}.git" ]]
211then
212 HOMEBREW_CORE_GIT_REMOTE="${HOMEBREW_CORE_DEFAULT_GIT_REMOTE}"
213fi
214export HOMEBREW_{BREW,CORE}_GIT_REMOTE
215
216# TODO: bump version when new macOS is released or announced
217MACOS_NEWEST_UNSUPPORTED="27.0"
218# TODO: bump version when new macOS is released
219MACOS_OLDEST_SUPPORTED="14.0"
220
221# For Homebrew on Linux
222REQUIRED_RUBY_VERSION=3.4 # https://github.com/Homebrew/brew/pull/19779
223REQUIRED_GLIBC_VERSION=2.13 # https://docs.brew.sh/Homebrew-on-Linux#requirements
224REQUIRED_CURL_VERSION=7.41.0 # HOMEBREW_MINIMUM_CURL_VERSION in brew.sh in Homebrew/brew
225REQUIRED_GIT_VERSION=2.7.0 # HOMEBREW_MINIMUM_GIT_VERSION in brew.sh in Homebrew/brew
226
227# no analytics during installation
228export HOMEBREW_NO_ANALYTICS_THIS_RUN=1
229export HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT=1
230
231unset HAVE_SUDO_ACCESS # unset this from the environment
232
233# create paths.d file for /opt/homebrew installs
234# (/usr/local/bin is already in the PATH)
235if [[ -d "/etc/paths.d" && "${HOMEBREW_PREFIX}" != "/usr/local" && -x "$(command -v tee)" ]]
236then
237 ADD_PATHS_D=1
238fi
239
240have_sudo_access() {
241 if [[ ! -x "/usr/bin/sudo" ]]
242 then
243 return 1
244 fi
245
246 local -a SUDO=("/usr/bin/sudo")
247 if [[ -n "${SUDO_ASKPASS-}" ]]
248 then
249 SUDO+=("-A")
250 elif [[ -n "${NONINTERACTIVE-}" ]]
251 then
252 SUDO+=("-n")
253 fi
254
255 if [[ -z "${HAVE_SUDO_ACCESS-}" ]]
256 then
257 if [[ -n "${NONINTERACTIVE-}" ]]
258 then
259 "${SUDO[@]}" -l mkdir &>/dev/null
260 else
261 "${SUDO[@]}" -v && "${SUDO[@]}" -l mkdir &>/dev/null
262 fi
263 HAVE_SUDO_ACCESS="$?"
264 fi
265
266 if [[ -n "${HOMEBREW_ON_MACOS-}" ]] && [[ "${HAVE_SUDO_ACCESS}" -ne 0 ]]
267 then
268 abort "Need sudo access on macOS (e.g. the user ${USER} needs to be an Administrator)!"
269 fi
270
271 return "${HAVE_SUDO_ACCESS}"
272}
273
274execute() {
275 if ! "$@"
276 then
277 abort "$(printf "Failed during: %s" "$(shell_join "$@")")"
278 fi
279}
280
281retry() {
282 local tries="$1" n="$1" pause=2
283 shift
284 if ! "$@"
285 then
286 while [[ $((--n)) -gt 0 ]]
287 do
288 warn "$(printf "Trying again in %d seconds: %s" "${pause}" "$(shell_join "$@")")"
289 sleep "${pause}"
290 ((pause *= 2))
291 if "$@"
292 then
293 return
294 fi
295 done
296 abort "$(printf "Failed %d times doing: %s" "${tries}" "$(shell_join "$@")")"
297 fi
298}
299
300execute_sudo() {
301 local -a args=("$@")
302 if [[ "${EUID:-${UID}}" != "0" ]] && have_sudo_access
303 then
304 if [[ -n "${SUDO_ASKPASS-}" ]]
305 then
306 args=("-A" "${args[@]}")
307 fi
308 ohai "/usr/bin/sudo" "${args[@]}"
309 execute "/usr/bin/sudo" "${args[@]}"
310 else
311 ohai "${args[@]}"
312 execute "${args[@]}"
313 fi
314}
315
316getc() {
317 local save_state
318 save_state="$(/bin/stty -g)"
319 /bin/stty raw -echo
320 IFS='' read -r -n 1 -d '' "$@"
321 /bin/stty "${save_state}"
322}
323
324ring_bell() {
325 # Use the shell's audible bell.
326 if [[ -t 1 ]]
327 then
328 printf "\a"
329 fi
330}
331
332wait_for_user() {
333 local c
334 echo
335 echo "Press ${tty_bold}RETURN${tty_reset}/${tty_bold}ENTER${tty_reset} to continue or any other key to abort:"
336 getc c
337 # we test for \r and \n because some stuff does \r instead
338 if ! [[ "${c}" == $'\r' || "${c}" == $'\n' ]]
339 then
340 exit 1
341 fi
342}
343
344major_minor() {
345 echo "${1%%.*}.$(
346 x="${1#*.}"
347 echo "${x%%.*}"
348 )"
349}
350
351version_gt() {
352 [[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -gt "${2#*.}" ]]
353}
354version_ge() {
355 [[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -ge "${2#*.}" ]]
356}
357version_lt() {
358 [[ "${1%.*}" -lt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -lt "${2#*.}" ]]
359}
360
361check_run_command_as_root() {
362 [[ "${EUID:-${UID}}" == "0" ]] || return
363
364 # Allow Azure Pipelines/GitHub Actions/Docker/Concourse/Kubernetes to do everything as root (as it's normal there)
365 [[ -f /.dockerenv ]] && return
366 [[ -f /run/.containerenv ]] && return
367 [[ -f /proc/1/cgroup ]] && grep -E "azpl_job|actions_job|docker|garden|kubepods" -q /proc/1/cgroup && return
368
369 abort "Don't run this as root!"
370}
371
372should_install_command_line_tools() {
373 if [[ -n "${HOMEBREW_ON_LINUX-}" ]]
374 then
375 return 1
376 fi
377
378 if version_gt "${macos_version}" "10.13"
379 then
380 ! [[ -e "/Library/Developer/CommandLineTools/usr/bin/git" ]]
381 else
382 ! [[ -e "/Library/Developer/CommandLineTools/usr/bin/git" ]] ||
383 ! [[ -e "/usr/include/iconv.h" ]]
384 fi
385}
386
387get_permission() {
388 "${STAT_PRINTF[@]}" "${PERMISSION_FORMAT}" "$1"
389}
390
391user_only_chmod() {
392 [[ -d "$1" ]] && [[ "$(get_permission "$1")" != 75[0145] ]]
393}
394
395exists_but_not_writable() {
396 [[ -e "$1" ]] && ! [[ -r "$1" && -w "$1" && -x "$1" ]]
397}
398
399get_owner() {
400 "${STAT_PRINTF[@]}" "%u" "$1"
401}
402
403file_not_owned() {
404 [[ "$(get_owner "$1")" != "$(id -u)" ]]
405}
406
407get_group() {
408 "${STAT_PRINTF[@]}" "%g" "$1"
409}
410
411file_not_grpowned() {
412 [[ " $(id -G "${USER}") " != *" $(get_group "$1") "* ]]
413}
414
415# Please sync with 'test_ruby()' in 'Library/Homebrew/utils/ruby.sh' from the Homebrew/brew repository.
416test_ruby() {
417 if [[ ! -x "$1" ]]
418 then
419 return 1
420 fi
421
422 "$1" --enable-frozen-string-literal --disable=gems,did_you_mean,rubyopt -rrubygems -e \
423 "abort if Gem::Version.new(RUBY_VERSION) < \
424 Gem::Version.new('${REQUIRED_RUBY_VERSION}')" 2>/dev/null
425}
426
427test_curl() {
428 if [[ ! -x "$1" ]]
429 then
430 return 1
431 fi
432
433 if [[ "$1" == "/snap/bin/curl" ]]
434 then
435 warn "Ignoring $1 (curl snap is too restricted)"
436 return 1
437 fi
438
439 local curl_version_output curl_name_and_version
440 curl_version_output="$("$1" --version 2>/dev/null)"
441 curl_name_and_version="${curl_version_output%% (*}"
442 version_ge "$(major_minor "${curl_name_and_version##* }")" "$(major_minor "${REQUIRED_CURL_VERSION}")"
443}
444
445test_git() {
446 if [[ ! -x "$1" ]]
447 then
448 return 1
449 fi
450
451 local git_version_output
452 git_version_output="$("$1" --version 2>/dev/null)"
453 if [[ "${git_version_output}" =~ "git version "([^ ]*).* ]]
454 then
455 version_ge "$(major_minor "${BASH_REMATCH[1]}")" "$(major_minor "${REQUIRED_GIT_VERSION}")"
456 else
457 abort "Unexpected Git version: '${git_version_output}'!"
458 fi
459}
460
461# Search for the given executable in PATH (avoids a dependency on the `which` command)
462which() {
463 # Alias to Bash built-in command `type -P`
464 type -P "$@"
465}
466
467# Search PATH for the specified program that satisfies Homebrew requirements
468# function which is set above
469# shellcheck disable=SC2230
470find_tool() {
471 if [[ $# -ne 1 ]]
472 then
473 return 1
474 fi
475
476 local executable
477 while read -r executable
478 do
479 if [[ "${executable}" != /* ]]
480 then
481 warn "Ignoring ${executable} (relative paths don't work)"
482 elif "test_$1" "${executable}"
483 then
484 echo "${executable}"
485 break
486 fi
487 done < <(which -a "$1")
488}
489
490no_usable_ruby() {
491 [[ -z "$(find_tool ruby)" ]] || ! ruby -e "require 'erb'"
492}
493
494outdated_glibc() {
495 local glibc_version
496 glibc_version="$(ldd --version | head -n1 | grep -o '[0-9.]*$' | grep -o '^[0-9]\+\.[0-9]\+')"
497 version_lt "${glibc_version}" "${REQUIRED_GLIBC_VERSION}"
498}
499
500if [[ -n "${HOMEBREW_ON_LINUX-}" ]] && no_usable_ruby
501then
502 if outdated_glibc
503 then
504 abort "$(
505 cat <<EOABORT
506Homebrew requires Ruby ${REQUIRED_RUBY_VERSION} which was not found on your system.
507Homebrew portable Ruby requires Glibc version ${REQUIRED_GLIBC_VERSION} or newer,
508and your Glibc version is too old. See:
509 ${tty_underline}https://docs.brew.sh/Homebrew-on-Linux#requirements${tty_reset}
510Please install Ruby ${REQUIRED_RUBY_VERSION} and add its location to your PATH.
511EOABORT
512 )"
513 else
514 export HOMEBREW_FORCE_VENDOR_RUBY=1
515 fi
516fi
517
518# Invalidate sudo timestamp before exiting (if it wasn't active before).
519if [[ -x /usr/bin/sudo ]] && ! /usr/bin/sudo -n -v 2>/dev/null
520then
521 trap '/usr/bin/sudo -k' EXIT
522fi
523
524# Things can fail later if `pwd` doesn't exist.
525# Also sudo prints a warning message for no good reason
526cd "/usr" || exit 1
527
528####################################################################### script
529
530# shellcheck disable=SC2016
531ohai 'Checking for `sudo` access (which may request your password)...'
532
533if [[ -n "${HOMEBREW_ON_MACOS-}" ]]
534then
535 [[ "${EUID:-${UID}}" == "0" ]] || have_sudo_access
536elif ! [[ -w "${HOMEBREW_PREFIX}" ]] &&
537 ! [[ -w "/home/linuxbrew" ]] &&
538 ! [[ -w "/home" ]] &&
539 ! have_sudo_access
540then
541 abort "$(
542 cat <<EOABORT
543Insufficient permissions to install Homebrew to "${HOMEBREW_PREFIX}" (the default prefix).
544
545Alternative (unsupported) installation methods are available at:
546https://docs.brew.sh/Installation#alternative-installs
547
548Please note this will require most formula to build from source, a buggy, slow and energy-inefficient experience.
549We will close any issues without response for these unsupported configurations.
550EOABORT
551 )"
552fi
553HOMEBREW_CORE="${HOMEBREW_REPOSITORY}/Library/Taps/homebrew/homebrew-core"
554
555check_run_command_as_root
556
557if [[ -d "${HOMEBREW_PREFIX}" && ! -x "${HOMEBREW_PREFIX}" ]]
558then
559 abort "$(
560 cat <<EOABORT
561The Homebrew prefix ${tty_underline}${HOMEBREW_PREFIX}${tty_reset} exists but is not searchable.
562If this is not intentional, please restore the default permissions and
563try running the installer again:
564 sudo chmod 775 ${HOMEBREW_PREFIX}
565EOABORT
566 )"
567fi
568
569if [[ -n "${HOMEBREW_ON_MACOS-}" ]]
570then
571 # On macOS, support 64-bit Intel and ARM
572 if [[ "${UNAME_MACHINE}" != "arm64" ]] && [[ "${UNAME_MACHINE}" != "x86_64" ]]
573 then
574 abort "Homebrew is only supported on Intel and ARM processors!"
575 fi
576else
577 if [[ "${UNAME_MACHINE}" != "x86_64" ]] && [[ "${UNAME_MACHINE}" != "aarch64" ]]
578 then
579 abort "Homebrew on Linux is only supported on Intel x86_64 and ARM64 processors!"
580 fi
581fi
582
583if [[ -n "${HOMEBREW_ON_MACOS-}" ]]
584then
585 macos_version="$(major_minor "$(/usr/bin/sw_vers -productVersion)")"
586 if version_lt "${macos_version}" "10.7"
587 then
588 abort "$(
589 cat <<EOABORT
590Your Mac OS X version is too old. See:
591 ${tty_underline}https://github.com/mistydemeo/tigerbrew${tty_reset}
592EOABORT
593 )"
594 elif version_lt "${macos_version}" "10.11"
595 then
596 abort "Your OS X version is too old."
597 elif version_ge "${macos_version}" "${MACOS_NEWEST_UNSUPPORTED}" ||
598 version_lt "${macos_version}" "${MACOS_OLDEST_SUPPORTED}"
599 then
600 who="We"
601 what=""
602 if version_ge "${macos_version}" "${MACOS_NEWEST_UNSUPPORTED}"
603 then
604 what="pre-release version"
605 else
606 who+=" (and Apple)"
607 what="old version"
608 fi
609 ohai "You are using macOS ${macos_version}."
610 ohai "${who} do not provide support for this ${what}."
611
612 echo "$(
613 cat <<EOS
614This installation may not succeed.
615After installation, you will encounter build failures with some formulae.
616Please create pull requests instead of asking for help on Homebrew\'s GitHub,
617Twitter or any other official channels. You are responsible for resolving any
618issues you experience while you are running this ${what}.
619EOS
620 )
621" | tr -d "\\"
622 fi
623fi
624
625ohai "This script will install:"
626echo "${HOMEBREW_PREFIX}/bin/brew"
627echo "${HOMEBREW_PREFIX}/share/doc/homebrew"
628echo "${HOMEBREW_PREFIX}/share/man/man1/brew.1"
629echo "${HOMEBREW_PREFIX}/share/zsh/site-functions/_brew"
630echo "${HOMEBREW_PREFIX}/etc/bash_completion.d/brew"
631echo "${HOMEBREW_REPOSITORY}"
632if [[ -n "${ADD_PATHS_D-}" ]]
633then
634 echo "/etc/paths.d/homebrew"
635fi
636
637# Keep relatively in sync with
638# https://github.com/Homebrew/brew/blob/HEAD/Library/Homebrew/keg.rb
639directories=(
640 bin etc include lib sbin share opt var
641 Frameworks
642 etc/bash_completion.d lib/pkgconfig
643 share/aclocal share/doc share/info share/locale share/man
644 share/man/man1 share/man/man2 share/man/man3 share/man/man4
645 share/man/man5 share/man/man6 share/man/man7 share/man/man8
646 var/log var/homebrew var/homebrew/linked
647 bin/brew
648)
649group_chmods=()
650for dir in "${directories[@]}"
651do
652 if exists_but_not_writable "${HOMEBREW_PREFIX}/${dir}"
653 then
654 group_chmods+=("${HOMEBREW_PREFIX}/${dir}")
655 fi
656done
657
658# zsh refuses to read from these directories if group writable
659directories=(share/zsh share/zsh/site-functions)
660zsh_dirs=()
661for dir in "${directories[@]}"
662do
663 zsh_dirs+=("${HOMEBREW_PREFIX}/${dir}")
664done
665
666directories=(
667 bin etc include lib sbin share var opt
668 share/zsh share/zsh/site-functions
669 var/homebrew var/homebrew/linked
670 Cellar Caskroom Frameworks
671)
672mkdirs=()
673for dir in "${directories[@]}"
674do
675 if ! [[ -d "${HOMEBREW_PREFIX}/${dir}" ]]
676 then
677 mkdirs+=("${HOMEBREW_PREFIX}/${dir}")
678 fi
679done
680
681user_chmods=()
682mkdirs_user_only=()
683if [[ "${#zsh_dirs[@]}" -gt 0 ]]
684then
685 for dir in "${zsh_dirs[@]}"
686 do
687 if [[ ! -d "${dir}" ]]
688 then
689 mkdirs_user_only+=("${dir}")
690 elif user_only_chmod "${dir}"
691 then
692 user_chmods+=("${dir}")
693 fi
694 done
695fi
696
697chmods=()
698if [[ "${#group_chmods[@]}" -gt 0 ]]
699then
700 chmods+=("${group_chmods[@]}")
701fi
702if [[ "${#user_chmods[@]}" -gt 0 ]]
703then
704 chmods+=("${user_chmods[@]}")
705fi
706
707chowns=()
708chgrps=()
709if [[ "${#chmods[@]}" -gt 0 ]]
710then
711 for dir in "${chmods[@]}"
712 do
713 if file_not_owned "${dir}"
714 then
715 chowns+=("${dir}")
716 fi
717 if file_not_grpowned "${dir}"
718 then
719 chgrps+=("${dir}")
720 fi
721 done
722fi
723
724if [[ "${#group_chmods[@]}" -gt 0 ]]
725then
726 ohai "The following existing directories will be made group writable:"
727 printf "%s\n" "${group_chmods[@]}"
728fi
729if [[ "${#user_chmods[@]}" -gt 0 ]]
730then
731 ohai "The following existing directories will be made writable by user only:"
732 printf "%s\n" "${user_chmods[@]}"
733fi
734if [[ "${#chowns[@]}" -gt 0 ]]
735then
736 ohai "The following existing directories will have their owner set to ${tty_underline}${USER}${tty_reset}:"
737 printf "%s\n" "${chowns[@]}"
738fi
739if [[ "${#chgrps[@]}" -gt 0 ]]
740then
741 ohai "The following existing directories will have their group set to ${tty_underline}${GROUP}${tty_reset}:"
742 printf "%s\n" "${chgrps[@]}"
743fi
744if [[ "${#mkdirs[@]}" -gt 0 ]]
745then
746 ohai "The following new directories will be created:"
747 printf "%s\n" "${mkdirs[@]}"
748fi
749
750if should_install_command_line_tools
751then
752 ohai "The Xcode Command Line Tools will be installed."
753fi
754
755non_default_repos=""
756additional_shellenv_commands=()
757if [[ "${HOMEBREW_BREW_DEFAULT_GIT_REMOTE}" != "${HOMEBREW_BREW_GIT_REMOTE}" ]]
758then
759 ohai "HOMEBREW_BREW_GIT_REMOTE is set to a non-default URL:"
760 echo "${tty_underline}${HOMEBREW_BREW_GIT_REMOTE}${tty_reset} will be used as the Homebrew/brew Git remote."
761 non_default_repos="Homebrew/brew"
762 additional_shellenv_commands+=("export HOMEBREW_BREW_GIT_REMOTE=\"${HOMEBREW_BREW_GIT_REMOTE}\"")
763fi
764
765if [[ "${HOMEBREW_CORE_DEFAULT_GIT_REMOTE}" != "${HOMEBREW_CORE_GIT_REMOTE}" ]]
766then
767 ohai "HOMEBREW_CORE_GIT_REMOTE is set to a non-default URL:"
768 echo "${tty_underline}${HOMEBREW_CORE_GIT_REMOTE}${tty_reset} will be used as the Homebrew/homebrew-core Git remote."
769 non_default_repos="${non_default_repos:-}${non_default_repos:+ and }Homebrew/homebrew-core"
770 additional_shellenv_commands+=("export HOMEBREW_CORE_GIT_REMOTE=\"${HOMEBREW_CORE_GIT_REMOTE}\"")
771fi
772
773if [[ -n "${HOMEBREW_NO_INSTALL_FROM_API-}" ]]
774then
775 ohai "HOMEBREW_NO_INSTALL_FROM_API is set."
776 echo "Homebrew/homebrew-core will be tapped during this ${tty_bold}install${tty_reset} run."
777fi
778
779if [[ -z "${NONINTERACTIVE-}" ]]
780then
781 ring_bell
782 wait_for_user
783fi
784
785if [[ -d "${HOMEBREW_PREFIX}" ]]
786then
787 if [[ "${#chmods[@]}" -gt 0 ]]
788 then
789 execute_sudo "${CHMOD[@]}" "u+rwx" "${chmods[@]}"
790 fi
791 if [[ "${#group_chmods[@]}" -gt 0 ]]
792 then
793 execute_sudo "${CHMOD[@]}" "g+rwx" "${group_chmods[@]}"
794 fi
795 if [[ "${#user_chmods[@]}" -gt 0 ]]
796 then
797 execute_sudo "${CHMOD[@]}" "go-w" "${user_chmods[@]}"
798 fi
799 if [[ "${#chowns[@]}" -gt 0 ]]
800 then
801 execute_sudo "${CHOWN[@]}" "${USER}" "${chowns[@]}"
802 fi
803 if [[ "${#chgrps[@]}" -gt 0 ]]
804 then
805 execute_sudo "${CHGRP[@]}" "${GROUP}" "${chgrps[@]}"
806 fi
807else
808 execute_sudo "${INSTALL[@]}" "${HOMEBREW_PREFIX}"
809fi
810
811if [[ "${#mkdirs[@]}" -gt 0 ]]
812then
813 execute_sudo "${MKDIR[@]}" "${mkdirs[@]}"
814 execute_sudo "${CHMOD[@]}" "ug=rwx" "${mkdirs[@]}"
815 if [[ "${#mkdirs_user_only[@]}" -gt 0 ]]
816 then
817 execute_sudo "${CHMOD[@]}" "go-w" "${mkdirs_user_only[@]}"
818 fi
819 execute_sudo "${CHOWN[@]}" "${USER}" "${mkdirs[@]}"
820 execute_sudo "${CHGRP[@]}" "${GROUP}" "${mkdirs[@]}"
821fi
822
823if ! [[ -d "${HOMEBREW_REPOSITORY}" ]]
824then
825 execute_sudo "${MKDIR[@]}" "${HOMEBREW_REPOSITORY}"
826fi
827execute_sudo "${CHOWN[@]}" "-R" "${USER}:${GROUP}" "${HOMEBREW_REPOSITORY}"
828
829if ! [[ -d "${HOMEBREW_CACHE}" ]]
830then
831 execute "${MKDIR[@]}" "${HOMEBREW_CACHE}"
832fi
833if exists_but_not_writable "${HOMEBREW_CACHE}"
834then
835 execute_sudo "${CHMOD[@]}" "g+rwx" "${HOMEBREW_CACHE}"
836fi
837if file_not_owned "${HOMEBREW_CACHE}"
838then
839 execute_sudo "${CHOWN[@]}" "-R" "${USER}" "${HOMEBREW_CACHE}"
840fi
841if file_not_grpowned "${HOMEBREW_CACHE}"
842then
843 execute_sudo "${CHGRP[@]}" "-R" "${GROUP}" "${HOMEBREW_CACHE}"
844fi
845if [[ -d "${HOMEBREW_CACHE}" ]]
846then
847 execute "${TOUCH[@]}" "${HOMEBREW_CACHE}/.cleaned"
848fi
849
850if should_install_command_line_tools && version_ge "${macos_version}" "10.13"
851then
852 ohai "Searching online for the Command Line Tools"
853 # This temporary file prompts the 'softwareupdate' utility to list the Command Line Tools
854 clt_placeholder="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress"
855 execute_sudo "${TOUCH[@]}" "${clt_placeholder}"
856
857 clt_label_command="/usr/sbin/softwareupdate -l |
858 grep -B 1 -E 'Command Line Tools' |
859 awk -F'*' '/^ *\\*/ {print \$2}' |
860 sed -e 's/^ *Label: //' -e 's/^ *//' |
861 sort -V |
862 tail -n1"
863 clt_label="$(chomp "$(/bin/bash -c "${clt_label_command}")")"
864
865 if [[ -n "${clt_label}" ]]
866 then
867 ohai "Installing ${clt_label}"
868 execute_sudo "/usr/sbin/softwareupdate" "-i" "${clt_label}"
869 execute_sudo "/usr/bin/xcode-select" "--switch" "/Library/Developer/CommandLineTools"
870 fi
871 execute_sudo "/bin/rm" "-f" "${clt_placeholder}"
872fi
873
874# Headless install may have failed, so fallback to original 'xcode-select' method
875if should_install_command_line_tools && test -t 0
876then
877 ohai "Installing the Command Line Tools (expect a GUI popup):"
878 execute "/usr/bin/xcode-select" "--install"
879 echo "Press any key when the installation has completed."
880 getc
881 execute_sudo "/usr/bin/xcode-select" "--switch" "/Library/Developer/CommandLineTools"
882fi
883
884if [[ -n "${HOMEBREW_ON_MACOS-}" ]] && ! output="$(/usr/bin/xcrun clang 2>&1)" && [[ "${output}" == *"license"* ]]
885then
886 abort "$(
887 cat <<EOABORT
888You have not agreed to the Xcode license.
889Before running the installer again please agree to the license by opening
890Xcode.app or running:
891 sudo xcodebuild -license
892EOABORT
893 )"
894fi
895
896USABLE_GIT=/usr/bin/git
897if [[ -n "${HOMEBREW_ON_LINUX-}" ]]
898then
899 USABLE_GIT="$(find_tool git)"
900 if [[ -z "$(command -v git)" ]]
901 then
902 abort "$(
903 cat <<EOABORT
904 You must install Git before installing Homebrew. See:
905 ${tty_underline}https://docs.brew.sh/Installation${tty_reset}
906EOABORT
907 )"
908 fi
909 if [[ -z "${USABLE_GIT}" ]]
910 then
911 abort "$(
912 cat <<EOABORT
913 The version of Git that was found does not satisfy requirements for Homebrew.
914 Please install Git ${REQUIRED_GIT_VERSION} or newer and add it to your PATH.
915EOABORT
916 )"
917 fi
918 if [[ "${USABLE_GIT}" != /usr/bin/git ]]
919 then
920 export HOMEBREW_GIT_PATH="${USABLE_GIT}"
921 ohai "Found Git: ${HOMEBREW_GIT_PATH}"
922 fi
923fi
924
925if ! command -v curl >/dev/null
926then
927 abort "$(
928 cat <<EOABORT
929You must install cURL before installing Homebrew. See:
930 ${tty_underline}https://docs.brew.sh/Installation${tty_reset}
931EOABORT
932 )"
933elif [[ -n "${HOMEBREW_ON_LINUX-}" ]]
934then
935 USABLE_CURL="$(find_tool curl)"
936 if [[ -z "${USABLE_CURL}" ]]
937 then
938 abort "$(
939 cat <<EOABORT
940The version of cURL that was found does not satisfy requirements for Homebrew.
941Please install cURL ${REQUIRED_CURL_VERSION} or newer and add it to your PATH.
942EOABORT
943 )"
944 elif [[ "${USABLE_CURL}" != /usr/bin/curl ]]
945 then
946 export HOMEBREW_CURL_PATH="${USABLE_CURL}"
947 ohai "Found cURL: ${HOMEBREW_CURL_PATH}"
948 fi
949fi
950
951ohai "Downloading and installing Homebrew..."
952(
953 cd "${HOMEBREW_REPOSITORY}" >/dev/null || return
954
955 # we do it in four steps to avoid merge errors when reinstalling
956 execute "${USABLE_GIT}" "-c" "init.defaultBranch=main" "init" "--quiet"
957
958 # "git remote add" will fail if the remote is defined in the global config
959 execute "${USABLE_GIT}" "config" "remote.origin.url" "${HOMEBREW_BREW_GIT_REMOTE}"
960 execute "${USABLE_GIT}" "config" "remote.origin.fetch" "+refs/heads/*:refs/remotes/origin/*"
961 execute "${USABLE_GIT}" "config" "--bool" "fetch.prune" "true"
962
963 # ensure we don't munge line endings on checkout
964 execute "${USABLE_GIT}" "config" "--bool" "core.autocrlf" "false"
965
966 # make sure symlinks are saved as-is
967 execute "${USABLE_GIT}" "config" "--bool" "core.symlinks" "true"
968
969 if [[ -z "${NONINTERACTIVE-}" ]]
970 then
971 quiet_progress=("--quiet" "--progress")
972 else
973 quiet_progress=("--quiet")
974 fi
975 retry 5 "${USABLE_GIT}" "fetch" "${quiet_progress[@]}" "--force" "origin"
976 retry 5 "${USABLE_GIT}" "fetch" "${quiet_progress[@]}" "--force" "--tags" "origin"
977
978 # Recover from partial installs created by older scripts that tracked origin/master.
979 # Keeping this stale ref can make post-install `brew update` fail when the checked-out
980 # brew revision no longer references `origin/master`.
981 if "${USABLE_GIT}" "show-ref" --verify --quiet "refs/remotes/origin/master"
982 then
983 execute "${USABLE_GIT}" "update-ref" "-d" "refs/remotes/origin/master"
984 fi
985
986 execute "${USABLE_GIT}" "remote" "set-head" "origin" "--auto" >/dev/null
987
988 LATEST_GIT_TAG="$("${USABLE_GIT}" -c "column.ui=never" tag --list --sort="-version:refname" | head -n1)"
989 if [[ -z "${LATEST_GIT_TAG}" ]]
990 then
991 abort "Failed to query latest Homebrew/brew Git tag."
992 fi
993 execute "${USABLE_GIT}" "checkout" "--quiet" "--force" "-B" "stable" "${LATEST_GIT_TAG}"
994
995 if [[ "${HOMEBREW_REPOSITORY}" != "${HOMEBREW_PREFIX}" ]]
996 then
997 if [[ "${HOMEBREW_REPOSITORY}" == "${HOMEBREW_PREFIX}/Homebrew" ]]
998 then
999 execute "ln" "-sf" "../Homebrew/bin/brew" "${HOMEBREW_PREFIX}/bin/brew"
1000 else
1001 abort "The Homebrew/brew repository should be placed in the Homebrew prefix directory."
1002 fi
1003 fi
1004
1005 if [[ -n "${HOMEBREW_NO_INSTALL_FROM_API-}" && ! -d "${HOMEBREW_CORE}" ]]
1006 then
1007 # Always use single-quoted strings with `exp` expressions
1008 # shellcheck disable=SC2016
1009 ohai 'Tapping homebrew/core because `$HOMEBREW_NO_INSTALL_FROM_API` is set.'
1010 (
1011 execute "${MKDIR[@]}" "${HOMEBREW_CORE}"
1012 cd "${HOMEBREW_CORE}" >/dev/null || return
1013
1014 execute "${USABLE_GIT}" "-c" "init.defaultBranch=main" "init" "--quiet"
1015 execute "${USABLE_GIT}" "config" "remote.origin.url" "${HOMEBREW_CORE_GIT_REMOTE}"
1016 execute "${USABLE_GIT}" "config" "remote.origin.fetch" "+refs/heads/*:refs/remotes/origin/*"
1017 execute "${USABLE_GIT}" "config" "--bool" "fetch.prune" "true"
1018 execute "${USABLE_GIT}" "config" "--bool" "core.autocrlf" "false"
1019 execute "${USABLE_GIT}" "config" "--bool" "core.symlinks" "true"
1020 retry 5 "${USABLE_GIT}" "fetch" "--force" "${quiet_progress[@]}" \
1021 "origin" "refs/heads/main:refs/remotes/origin/main"
1022 execute "${USABLE_GIT}" "remote" "set-head" "origin" "--auto" >/dev/null
1023 execute "${USABLE_GIT}" "reset" "--hard" "origin/main"
1024
1025 cd "${HOMEBREW_REPOSITORY}" >/dev/null || return
1026 ) || exit 1
1027 fi
1028
1029 if [[ -n "${ADD_PATHS_D-}" ]]
1030 then
1031 execute_sudo "${MKDIR[@]}" /etc/paths.d
1032 echo "${HOMEBREW_PREFIX}/bin" | execute_sudo tee /etc/paths.d/homebrew
1033 execute_sudo "${CHOWN[@]}" root:wheel /etc/paths.d/homebrew
1034 execute_sudo "${CHMOD[@]}" "a+r" /etc/paths.d/homebrew
1035 elif [[ ":${PATH}:" != *":${HOMEBREW_PREFIX}/bin:"* ]]
1036 then
1037 PATH_WARN=1
1038 fi
1039
1040 execute "${HOMEBREW_PREFIX}/bin/brew" "update" "--force" "--quiet"
1041
1042 if [[ -n "${PATH_WARN-}" ]]
1043 then
1044 warn "${HOMEBREW_PREFIX}/bin is not in your PATH.
1045 Instructions on how to configure your shell for Homebrew
1046 can be found in the 'Next steps' section below."
1047 fi
1048) || exit 1
1049
1050ohai "Installation successful!"
1051echo
1052
1053ring_bell
1054
1055# Use an extra newline and bold to avoid this being missed.
1056ohai "Homebrew has enabled anonymous aggregate formulae and cask analytics."
1057echo "$(
1058 cat <<EOS
1059${tty_bold}Read the analytics documentation (and how to opt-out) here:
1060 ${tty_underline}https://docs.brew.sh/Analytics${tty_reset}
1061No analytics data has been sent yet (nor will any be during this ${tty_bold}install${tty_reset} run).
1062EOS
1063)
1064"
1065
1066ohai "Homebrew is run entirely by unpaid volunteers. Please consider donating:"
1067echo "$(
1068 cat <<EOS
1069 ${tty_underline}https://github.com/Homebrew/brew#donations${tty_reset}
1070EOS
1071)
1072"
1073
1074(
1075 cd "${HOMEBREW_REPOSITORY}" >/dev/null || return
1076 execute "${USABLE_GIT}" "config" "--replace-all" "homebrew.analyticsmessage" "true"
1077 execute "${USABLE_GIT}" "config" "--replace-all" "homebrew.caskanalyticsmessage" "true"
1078) || exit 1
1079
1080ohai "Next steps:"
1081case "${SHELL}" in
1082 */bash*)
1083 shellenv_suffix=" bash"
1084 if [[ -n "${HOMEBREW_ON_LINUX-}" ]]
1085 then
1086 shell_rcfile="${HOME}/.bashrc"
1087 else
1088 shell_rcfile="${HOME}/.bash_profile"
1089 fi
1090 ;;
1091 */zsh*)
1092 shellenv_suffix=" zsh"
1093 if [[ -n "${HOMEBREW_ON_LINUX-}" ]]
1094 then
1095 shell_rcfile="${ZDOTDIR:-"${HOME}"}/.zshrc"
1096 else
1097 shell_rcfile="${ZDOTDIR:-"${HOME}"}/.zprofile"
1098 fi
1099 ;;
1100 */fish*)
1101 shellenv_suffix=" fish"
1102 shell_rcfile="${HOME}/.config/fish/config.fish"
1103 ;;
1104 *)
1105 shellenv_suffix=""
1106 shell_rcfile="${ENV:-"${HOME}/.profile"}"
1107 ;;
1108esac
1109
1110if grep -qs "eval \"\$(${HOMEBREW_PREFIX}/bin/brew shellenv[^\"]*)\"" "${shell_rcfile}"
1111then
1112 if ! [[ -x "$(command -v brew)" ]]
1113 then
1114 cat <<EOS
1115- Run this command in your terminal to add Homebrew to your ${tty_bold}PATH${tty_reset}:
1116 eval "\$(${HOMEBREW_PREFIX}/bin/brew shellenv${shellenv_suffix})"
1117EOS
1118 fi
1119else
1120 cat <<EOS
1121- Run these commands in your terminal to add Homebrew to your ${tty_bold}PATH${tty_reset}:
1122 echo >> ${shell_rcfile}
1123 echo 'eval "\$(${HOMEBREW_PREFIX}/bin/brew shellenv${shellenv_suffix})"' >> ${shell_rcfile}
1124 eval "\$(${HOMEBREW_PREFIX}/bin/brew shellenv${shellenv_suffix})"
1125EOS
1126fi
1127
1128if [[ -n "${non_default_repos}" ]]
1129then
1130 plural=""
1131 if [[ "${#additional_shellenv_commands[@]}" -gt 1 ]]
1132 then
1133 plural="s"
1134 fi
1135 printf -- "- Run these commands in your terminal to add the non-default Git remote%s for %s:\n" "${plural}" "${non_default_repos}"
1136 printf " echo '# Set non-default Git remote%s for %s.' >> %s\n" "${plural}" "${non_default_repos}" "${shell_rcfile}"
1137 printf " echo '%s' >> ${shell_rcfile}\n" "${additional_shellenv_commands[@]}"
1138 printf " %s\n" "${additional_shellenv_commands[@]}"
1139fi
1140
1141if [[ -n "${HOMEBREW_ON_LINUX-}" ]]
1142then
1143 echo "- Install Homebrew's dependencies if you have sudo access:"
1144
1145 if [[ -x "$(command -v apt-get)" ]]
1146 then
1147 echo " sudo apt-get install build-essential"
1148 elif [[ -x "$(command -v dnf)" ]]
1149 then
1150 echo " sudo dnf group install development-tools"
1151 elif [[ -x "$(command -v yum)" ]]
1152 then
1153 echo " sudo yum groupinstall 'Development Tools'"
1154 elif [[ -x "$(command -v pacman)" ]]
1155 then
1156 echo " sudo pacman -S base-devel"
1157 elif [[ -x "$(command -v apk)" ]]
1158 then
1159 echo " sudo apk add build-base"
1160 fi
1161
1162 cat <<EOS
1163 For more information, see:
1164 ${tty_underline}https://docs.brew.sh/Homebrew-on-Linux${tty_reset}
1165- We recommend that you install GCC:
1166 brew install gcc
1167EOS
1168fi
1169
1170cat <<EOS
1171- Run ${tty_bold}brew help${tty_reset} to get started
1172- Further documentation:
1173 ${tty_underline}https://docs.brew.sh${tty_reset}
1174
1175EOS