Skip to content

Instantly share code, notes, and snippets.

@architeacher
Last active August 29, 2023 09:54
Show Gist options
  • Save architeacher/56d8359556c2ec3642189b4793f75280 to your computer and use it in GitHub Desktop.
Save architeacher/56d8359556c2ec3642189b4793f75280 to your computer and use it in GitHub Desktop.
First mac installation.
#!/usr/bin/env bash
# Install using
# bash <(curl -sSfL https://gist.githubusercontent.com/architeacher/56d8359556c2ec3642189b4793f75280/raw/aef18c031714bfb70b5b9db200ce8a5789b27a47/mac.sh) -e <YOUR_EMAIL> -n "<YOUR_NAME>"
set -eo pipefail
exit_status=0
#--- Logging
readonly LOG_LEVEL_DEBUG=7
readonly LOG_LEVEL_INFO=6
readonly LOG_LEVEL_NOTICE=5
readonly LOG_LEVEL_WARNING=4
readonly LOG_LEVEL_ERROR=3
readonly LOG_LEVEL_CRITICAL=2
readonly LOG_LEVEL_ALERT=1
readonly LOG_LEVEL_EMERGENCY=0
_log_level="${LOG_LEVEL_INFO}"
# string formatters, True if go_file descriptor FD is open and refers to a terminal.
tty_escape() { :; }
if [[ -t 1 ]]
then
tty_escape() { printf "\x1b[%sm" "${1}"; }
fi
tty_4bit_mk() { tty_escape "${1}"; }
tty_8bit_mk() { tty_escape "38;5;${1}"; }
tty_4bit_mkbold() { tty_4bit_mk "${1};1"; }
tty_8bit_mkbold() { tty_8bit_mk "${1};1"; }
tty_bold="$(tty_4bit_mkbold 39)"
tty_underline="$(tty_escape "4;39")"
tty_reset="$(tty_escape 0)"
tty_blue="$(tty_4bit_mk 34)"
tty_corn="$(tty_8bit_mk 178)"
tty_cyan="$(tty_8bit_mk 45)"
tty_green="$(tty_8bit_mk 35)"
tty_imperial="$(tty_4bit_mk 91)"
tty_lime="$(tty_8bit_mk 113)"
tty_magenta="$(tty_8bit_mk 170)"
tty_move="$(tty_4bit_mk 35)"
tty_olive="$(tty_8bit_mk 64)"
tty_orange="$(tty_8bit_mk 208)"
tty_pink="$(tty_8bit_mk 198)"
tty_red="$(tty_8bit_mkbold 196)"
tty_teal="$(tty_4bit_mk 36)"
tty_yellow="$(tty_8bit_mk 227)"
log_set_level() {
_log_level="${1}"
}
log_priority() {
local log_level="${1}"
if test -z "${log_level}"; then
echo "${log_level}"
return
fi
[ "${log_level}" -le "${_log_level}" ]
}
display_message() {
echo -e "${@}" 1>&2
}
log_color() {
local log_level="${1}" \
tty_color=""
case "${log_level}" in
"${LOG_LEVEL_DEBUG}")
tty_color="${tty_cyan}"
;;
"${LOG_LEVEL_INFO}")
tty_color="${tty_olive}"
;;
"${LOG_LEVEL_NOTICE}")
tty_color="${tty_green}"
;;
"${LOG_LEVEL_WARNING}")
tty_color="${tty_corn}"
;;
"${LOG_LEVEL_ERROR}")
tty_color="${tty_magenta}"
;;
"${LOG_LEVEL_CRITICAL}")
tty_color="${tty_imperial}"
;;
"${LOG_LEVEL_ALERT}")
tty_color="${tty_pink}"
;;
"${LOG_LEVEL_EMERGENCY}")
tty_color="${tty_red}"
;;
*)
tty_color="${tty_bold}"
;;
esac
printf "%s" "${tty_color}"
}
log_tag() {
local log_level="${1}" \
tag=""
case "${log_level}" in
"${LOG_LEVEL_DEBUG}")
tag="Debug"
;;
"${LOG_LEVEL_INFO}")
tag="Info"
;;
"${LOG_LEVEL_NOTICE}")
tag="Notice"
;;
"${LOG_LEVEL_WARNING}")
tag="Warning"
;;
"${LOG_LEVEL_ERROR}")
tag="Error"
;;
"${LOG_LEVEL_CRITICAL}")
tag="Critical"
;;
"${LOG_LEVEL_ALERT}")
tag="Alert"
;;
"${LOG_LEVEL_EMERGENCY}")
tag="Emergency"
;;
*)
tag=""
;;
esac
printf "%s" "${tag}"
}
log_prefix() {
echo "==>"
}
log() {
local log_level="${1}" \
message="" \
tty_color="" \
tag=""
{
read -r tty_color
} <<< "$(log_color "${log_level}")"
{
read -r tag
} <<< "$(log_tag "${log_level}")"
shift
message="${*}"
if [ "${tty_color}" != "" ]; then
display_message "${tty_color}" "$(log_prefix)" "${tag}: " "${message}" "${tty_reset}"
else
printf "%s %b" "$(log_prefix)" "${message}"
fi
}
log_debug() {
log_priority "${LOG_LEVEL_DEBUG}" || return 0
log "${LOG_LEVEL_DEBUG}" "${@}"
}
log_info() {
log_priority "${LOG_LEVEL_INFO}" || return 0
log "${LOG_LEVEL_INFO}" "${@}"
}
log_notice() {
log_priority "${LOG_LEVEL_NOTICE}" || return 0
log "${LOG_LEVEL_NOTICE}" "${@}"
}
log_warning() {
log_priority "${LOG_LEVEL_WARNING}" || return 0
log "${LOG_LEVEL_WARNING}" "${@}"
}
log_error() {
log_priority "${LOG_LEVEL_ERROR}" || return 0
log "${LOG_LEVEL_ERROR}" "${@}"
}
log_critical() {
log_priority "${LOG_LEVEL_CRITICAL}" || return 0
log "${LOG_LEVEL_CRITICAL}" "${@}"
}
log_alert() {
log_priority "${LOG_LEVEL_ALERT}" || return 0
log "${LOG_LEVEL_ALERT}" "${@}"
}
log_emergency() {
log_priority "${LOG_LEVEL_EMERGENCY}" || return 0
log "${LOG_LEVEL_EMERGENCY}" "${@}"
}
parse_log_level() {
local log_input="${1}" \
log_level=""
case "${log_input}" in
"debug")
log_level="${LOG_LEVEL_DEBUG}"
;;
"info")
log_level="${LOG_LEVEL_INFO}"
;;
"notice")
log_level="${LOG_LEVEL_NOTICE}"
;;
"warning")
log_level="${LOG_LEVEL_WARNING}"
;;
"error")
log_level="${LOG_LEVEL_ERROR}"
;;
"critical")
log_level="${LOG_LEVEL_CRITICAL}"
;;
"alert")
log_level="${LOG_LEVEL_ALERT}"
;;
"emergency")
log_level="${LOG_LEVEL_EMERGENCY}"
;;
*)
abort "Invalid log level: ${log_level}"
;;
esac
echo "${log_level}"
}
#---
SUDO=
# abort with an error message.
abort() {
local message="${1}"
log_error "${message}"
exit 1
}
is_command() {
command -v "${1}" 2> /dev/null
}
check_sudo() {
if [ "$(id -u)" -eq 0 ]; then
return
fi
SUDO=$(is_command "sudo")
[ ! -x "${SUDO}" ] && abort "This script must be executed as root."
}
get_profile() {
local shell_profile="${HOME}/.bash_profile"
case "${SHELL}" in
*/bash*)
if [[ -r "${HOME}/.bash_profile" ]]
then
shell_profile="${HOME}/.bash_profile"
else
shell_profile="${HOME}/.profile"
fi
;;
*/zsh*)
shell_profile="${HOME}/.zprofile"
;;
*)
shell_profile="${HOME}/.profile"
;;
esac
print "${shell_profile}"
}
get_random_string() {
print "$(export LC_CTYPE=C; cat </dev/urandom | tr -dc 'a-zA-Z0-9\.' | fold -w 32 | head -n 1)"
}
validate_bash() {
# Fail fast with a concise message when not using bash
# Single brackets are needed here for POSIX compatibility
# shellcheck disable=SC2292
[ -z "${BASH_VERSION:-}" ] && abort "Bash is required to interpret this script."
# Check if running in a compatible bash version.
((BASH_VERSINFO[0] < 3)) && abort "Bash version 3 or above is required."
# Check if script is run in POSIX mode.
if [[ -n "${POSIXLY_CORRECT+1}" ]]
then
abort "Bash must not run in POSIX compatibility mode. Please disable by unsetting POSIXLY_CORRECT and try again."
fi
}
# --
# Environment preparations.
prepare() {
log_info "Preparing..."
if [ -n "${HOMEBREW_GITHUB_API_TOKEN+z}" ]; then
log_err "\$HOMEBREW_GITHUB_API_TOKEN is set";
return
fi
log_info "Please enter, Github api token https://github.com/settings/tokens: " && read -r github_api_token
# Github for homebrew installation.
export HOMEBREW_GITHUB_API_TOKEN="${github_api_token}"
printf "# Homebrew github token:\nHOMEBREW_GITHUB_API_TOKEN=\"%s\"" "${HOMEBREW_GITHUB_API_TOKEN}" > "$(get_profile)"
}
# Creating .netrc file https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html
create_netrc() {
log_info "Creating .netrc file..."
touch "${HOME}/.netrc" 2>&1
{
echo "# Configuration example, please uncomment the following lines."
echo "# machine host_name"
echo "# login ahmed.kamal"
echo "# password secret-$(get_random_string)"
} >> "${HOME}/.netrc"
${SUDO} chmod 600 "${HOME}/.netrc"
}
# SSH configurations
configure_ssh() {
log_info "Configuring SSH..."
cat <<'EOF' >"${HOME}/.ssh/config"
Host *
AddKeysToAgent yes
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc
ForwardAgent yes
HashKnownHosts no
# IdentitiesOnly yes
IdentityFile ~/.ssh/id_rsa
IPQoS=throughput
LogLevel ERROR
ServerAliveCountMax 3
ServerAliveInterval 10 # 600
StrictHostKeyChecking no
TCPKeepAlive yes
User ahka
UseKeychain yes # <--- mac only
UserKnownHostsFile /dev/null
EOF
}
# SSH keys generation.
generate_ssh_keys() {
log_info "Generating SSH Keys..."
ssh-keygen -t rsa
printf "pbcopy < ~/.ssh/id_rsa.pub\n"
}
# Homebrew installation.
install_brew() {
log_info "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
printf "\n\neval \"\$(/opt/homebrew/bin/brew shellenv)\"" | tee -a "$(get_profile)" > /dev/null
eval "$(/opt/homebrew/bin/brew shellenv)"
brew up
brew upgrade
}
install_browsers() {
log_info "Installing Browsers..."
brew install -f \
arc \
brave-browser \
google-chrome
}
install_dev_tools() {
log_info "Installing Dev Tools..."
brew install -f \
graphviz \
iterm2 \
jetbrains-toolbox \
medis \
postman \
visual-studio-code
}
# Docker installation
install_docker() {
log_info "Installing Docker..."
brew install -f \
Caskroom/cask/docker \
ctop \
dive \
lazydocker
# Add cleaning commands to .profile only as a reference.
cat <<'EOF' >~/.profile
# Docker cleaning configuration:
docker images -aqf dangling=true
docker image prune
docker ps -aqf status=exited -f status=dead
docker container prune
docker volume ls -qf dangling=true
docker volume prune
docker system prune
docker container ls -a --format "table {{.ID}} {{.Names}} {{json .Ports}}"
EOF
}
# Git installation
install_git() {
log_info "Installing GIT..."
brew install -f \
git \
gpg
local git_username="${1}" \
git_email="${2}"
if [ -z "${git_username}" ]; then
echo "Git config user.name: " && read -r git_username
fi
if [ -z "${git_email}" ]; then
echo "Git config user.email: " && read -r git_email
fi
git config --global user.name "${git_username}"
git config --global user.email "${git_email}"
# Colored branch names.
git config --global color.ui auto
git config --global color.diff-highlight.oldNormal "red bold"
git config --global color.diff-highlight.oldHighlight "red bold 52"
git config --global color.diff-highlight.newNormal "green bold"
git config --global color.diff-highlight.newHighlight "green bold 22"
git config --global color.diff.meta "11"
git config --global color.diff.frag "magenta bold"
git config --global color.diff.func "146 bold"
git config --global color.diff.commit "yellow bold"
git config --global color.diff.old "red bold"
git config --global color.diff.new "green bold"
git config --global color.diff.whitespace "red reverse"
# Disable auto-converting CRLF line endings into LF.
git config --global core.autocrlf false
# Working with Unix line endings (LF).
git config --global core.eol LF
# Ignoring file mode changes for git calculation of file-is-modified.
git config --global core.filemode false
# Making directories and file names case insensitive.
git config --global core.ignorecase true
# Setting the default branch for new repositories.
git config --global init.defaultBranch main
# Prevent pushing all locally modified branches if the branch to push is not specified while 'git push'.
git config --global push.default nothing
# If you are too lazy for typing "git checkout", "git status" or "git branch" all the time, it might be useful for you.
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.br branch
git config --global alias.hist 'log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=short'
# Example git count-lines "Ahmed Kamal"
git config --global alias.count-lines "! git log --author=\"\$1\" --pretty=tformat: --numstat | awk '{ add += \$1; subs += \$2; loc += \$1 - \$2 } END { printf \"added lines: %s, removed lines: %s, total lines: %s\n\", add, subs, loc }' #"
# Example git count-commits "Ahmed Kamal"
git config --global alias.count-commits "! git shortlog --author=\"\$1\" -s -n --all --no-merges #"
# Signing commits.
gpg --gen-key
# git config --global gpg.program gpg
local gpg_key_id
gpg_key_id=$(gpg --list-secret-keys --keyid-format 0xLONG | \
grep -B 2 "<${git_email}>" | \
grep -Eiom 1 '0x([0-9a-zA-Z]+)' | \
sed -r 's|0x([0-9a-zA-Z]+)|\1|g')
git config --global user.signingkey "${gpg_key_id}"
printf "\ngpg --armor --export %s | pbcopy\n" "${gpg_key_id}"
}
install_golang() {
log_info "Installing Golang..."
brew install -f \
go
local go_path="${HOME}/go"
mkdir -p "${go_path}/"{bin,pkg,src}
cat <<-EOF >>"$(get_profile)"
# Go configurations:
go_bin=\$(which go)
go_bin_dir=\$(dirname "\${go_bin}")
go_relative_bin_dir=\$(dirname \`readlink "\${go_bin}"\`)
export GOROOT=\$(cd "\${go_bin_dir}/\${go_relative_bin_dir}/../libexec"; pwd)
export GOPATH="${go_path}"
export PATH="\${PATH}:\${GOPATH}/bin:/usr/local/sbin"
EOF
}
install_go_tools() {
# Open API code generation tool.
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest
}
install_grpc() {
log_info "Installing GRPC..."
brew install -f \
bufbuild/buf/buf \
grpcui \
grpcurl \
protoc-gen-go \
protoc-gen-go-grpc
go install github.com/envoyproxy/protoc-gen-validate@latest
}
# Kubernetes installation
install_k8s() {
log_info "Installing Kubernetes..."
brew install -f \
helm \
kind \
kustomize \
k9s
#k3d \
#minikube
#minikube start --driver=virtualbox
}
install_tools() {
log_info "Installing Tools..."
#xcode-select --install
brew install -f \
ag \
bat \
bpytop \
broot \
cheat \
delta \
dog \
duf \
dust \
exa \
fd \
fig \
fx \
fzf \
glow \
gping \
hexyl \
htop-osx \
hyperfine \
jq \
lsd \
midnight-commander \
mkcert \
procs \
ripgrep \
teleport \
tldr \
travis \
tree \
watch \
wget \
xh \
yq
mkcert -install
}
# Software
install_warez() {
log_info "Installing Software..."
brew install -f \
dropbox \
kindle \
notion \
signal \
slack \
vlc \
wpsoffice
}
install_zsh_terminal() {
log_info "Installing ZSH Terminal..."
brew install -f \
zsh \
zsh-autosuggestions \
zsh-completions \
zsh-syntax-highlighting
# Install oh-my-zsh
sh -c "$(curl -fsSL "https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh")"
local brew_prefix
brew_prefix=$(brew --prefix)
# Fix to the prompt of new terminal window.
# ${SUDO} chmod 0775 "${brew_prefix}/share"
cat >> ~/.zshrc <<-EOF
# Auto suggestion
source "${brew_prefix}/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
# Auto completion
if type brew &>/dev/null; then
FPATH="${brew_prefix}/share/zsh-completions:\${FPATH}"
autoload -Uz compinit
compinit
fi
# Syntax highlighting
source "${brew_prefix}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
EOF
# Switching terminal.
zsh
}
install_zsh_theme() {
log_info "Installing ZSH Theme..."
brew install -f \
romkatv/powerlevel10k/powerlevel10k
local theme_name="powerlevel10k" \
theme_path
theme_path=$(brew --prefix "${theme_name}")
# Randomizing the default theme.
sed -ie "s|ZSH_THEME=.*|ZSH_THEME=\"random\"|g" ~/.zshrc
# Import powerlevel10k theme.
cat >> ~/.zshrc <<-EOF
# Path to ${theme_name} theme
source "${theme_path}/${theme_name}.zsh-theme"
EOF
p10k configure
local theme_config_path="${HOME}/.p10k.zsh"
# Enabling some icons
sed -ie "s|.*# load # CPU load.*| load # CPU load|g" "${theme_config_path}"
sed -ie "s|.*# disk_usage # disk usage.*| disk_usage # disk usage|g" "${theme_config_path}"
sed -ie "s|.*# ram # free RAM.*| ram # free RAM|g" "${theme_config_path}"
sed -ie "s|.*# battery # internal battery.*| battery # internal battery|g" "${theme_config_path}"
# Disabling error prompt.
sed -ie "s|.*POWERLEVEL9K_INSTANT_PROMPT=.*|typeset -g POWERLEVEL9K_INSTANT_PROMPT=quiet|g" "${theme_config_path}"
# Changing date and time format.
sed -ie "s|.*POWERLEVEL9K_TIME_FORMAT=.*|typeset -g POWERLEVEL9K_TIME_FORMAT='%D{\\\UF133 %d %b %H:%M:%S%z}'|g" "${theme_config_path}"
source "${HOME}/.zshrc"
}
iterm2_fonts() {
log_info "Installing iTerm2 Fonts..."
local fonts_path="/tmp/fonts"
git clone "https://github.com/powerline/fonts.git" --depth=1 "${fonts_path}"
"${fonts_path}"/install.sh
rm -rf "${fonts_path}"
}
iterm2_colors() {
log_info "Installing iTerm2 Colors..."
local colors_path="/tmp/iTerm2-Color-Schemes"
git clone "[email protected]:mbadolato/iTerm2-Color-Schemes.git" --depth=1 "${colors_path}"
# Import all color schemes (verbose mode).
"${colors_path}"/tools/import-scheme.sh -v "${colors_path}/schemes/*"
rm -rf "${colors_path}"
}
validate_args() {
if [ -z "${NAME:-}" ]; then
log_err 'Missing -n {{NAME}}'
exit 1
fi
if [ -z "${EMAIL:-}" ]; then
log_err 'Missing -e {{EMAIL}}'
exit 1
fi
}
parse_args() {
local opts_string="e:n:h?x"
while getopts "${opts_string}" arg; do
case "${arg}" in
e) EMAIL="${OPTARG}" ;;
n) NAME="${OPTARG}" ;;
h | \?) usage "${0}" ;;
x) set -x ;;
esac
done
shift $((OPTIND - 1))
}
# Computer wisdom
say_wisdom() {
log_info "Installing Say Wisdom..."
brew install -f \
cowsay \
fortune \
lolcat
printf "\n# Computer wisdom\nfortune -s computers | cowsay -f dragon | lolcat" >> "$(get_profile)"
}
script_cleanup() {
brew cleanup
}
usage() {
local this="${1}"
cat <<EOF
${this}: install Mac software for the first time
Usage: ${this} [-e] email [-l] log level [-n] name
-e sets user email.
-h to get help about the usage.
-l sets the log priority: 2 => critical, 3 => error, 6 => info, 7 => debug.
-n sets the full name.
-x to see the executed statements.
EOF
exit 2
}
main() {
validate_bash
check_sudo
trap 'script_cleanup; exit ${exit_status}' EXIT
parse_args "$@"
validate_args
log_info "Welcome ${NAME}<${EMAIL}>!"
prepare
configure_ssh
create_netrc
generate_ssh_keys
install_brew
install_browsers
install_dev_tools
install_docker
install_git "${NAME}" "${EMAIL}"
install_golang
install_go_tools
install_grpc
install_k8s
install_tools
install_warez
install_zsh_terminal
install_zsh_theme
iterm2_fonts
iterm2_colors
say_wisdom
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment