#!/usr/bin/env bash # # Consolidated setup script for setting up a new MacOS installation. # # This script tries to be as flexible and as helpful as possible, while keeping the user intervention to a minimum. # # Reading/Thanks: # # - https://gist.github.com/MatthewMueller/e22d9840f9ea2fee4716 # - https://gist.github.com/codeinthehole/26b37efa67041e1307db # - https://github.com/mikeprivette/yanmss/blob/master/setup.sh # - https://gist.github.com/bradp/bea76b16d3325f5c47d4 # - https://gist.github.com/ryanhanwu/059e210f8fe15e7eadc4a28e8b3e6b27 # - https://gist.github.com/rcugut/c7abd2a425bb65da3c61d8341cd4b02d # - https://github.com/Homebrew/homebrew-cask-fonts # - https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins # - https://github.com/ryanoasis/nerd-fonts # - https://github.com/ohmyzsh/ohmyzsh/wiki/Themes # - https://opensource.com/article/19/5/python-3-default-mac # - https://opensource.com/article/19/5/python-3-default-mac # - https://github.com/mathiasbynens/dotfiles # - https://gist.github.com/simonista/8703722 # ## 0 - Either chmod this file or ask for the administrator password upfront: sudo -v echo "Starting setup for $(whoami)" # # # Notes: # # # # # # - If installing full Xcode, it's better to install that first from the app # # # store before running the bootstrap script. Otherwise, Homebrew can't access # # # the Xcode libraries as the agreement hasn't been accepted yet. # # ############################################################################### # # # 0 - For Xcode and CLI # # ############################################################################### # # Install command line tools. When finished press [Enter] xcode-select --install read -p "Press [Enter] when apple command line tools installation is completed." # # ############################################################################### # # # 1 - MAIN COMPONENTS # # ############################################################################### # # Check for Homebrew, install if we don't have it if test ! $(which brew); then echo "Installing homebrew..." /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" fi # Update homebrew recipes echo "Updating homebrew recipes" brew update # # Install and set zsh as default shell in case we are not on big sur or above if test ! $(which zsh); then echo "Setting ZSH as shell..." echo "Installing zsh as default shell..." brew install zsh chsh -s /bin/zsh OR sh -c "echo $(which zsh) >> /etc/shells" && chsh -s $(which zsh) echo "zsh installed and set as default shell" fi ## Install any system or core utility e.g. GNU core utilities (those that come with OS X are outdated) echo "Installing system core utilities..." brew install coreutils brew install openssh ###################### uncomment if you understand whats below # brew install gnu-sed --with-default-names # brew install gnu-tar --with-default-names # brew install gnu-indent --with-default-names # brew install gnu-which --with-default-names # brew install gnu-grep --with-default-names ## Install GNU `find`, `locate`, `updatedb`, and `xargs`, g-prefixed ## Installs default moreutils # brew install moreutils ## There is a conflict between the GNU parallel utility and the shipped parallel on the moreutils package. If you want to install GNU parallel instead of the moreutils’ parallel, use the following commands: # brew install moreutils --without-parallel # brew install parallel ###################### brew install findutils # Install latest Bash brew install bash echo "Core utilities installation complete" # 1.2 - Add your preferred packages PACKAGES=( git tree vim wget speedtest-cli ) echo "Installing CLI packages..." brew install ${PACKAGES[@]} echo "CLI packages installation complete" # 1.3 - Add your preferred apps CASKS=( flux iterm2 the-unarchiver appcleaner transmission ) echo "Installing apps..." brew install --cask ${CASKS[@]} # # To install apps in a dir other than "/Applications" # brew install --cask --appdir="/Applications" ${CASKS[@]} echo "Apps installation complete" ############################################################################### # 2 - Development ############################################################################### # # ## Uncomment the ones you need or use...if any. # # Python and Ruby should be already installed # # ############# # # # GIT # # ############# echo "Configuring GIT" ## To increase security of your key this will automatically add it to the keychain echo "Creating your SSH key...add a passphrase" ssh-keygen -t ed25519 -f ~/.ssh/github_id_ed25519 -C "your@email.com" echo "Adding SSH key to Keychain" ssh-add ~/.ssh/github_id_ed25519 echo "Setting SSH to always use keychain for the passphrase" cat <> ~/.ssh/config Host * IgnoreUnknown UseKeychain UseKeychain yes AddKeysToAgent yes IdentityFile ~/.ssh/github_id_ed25519 EOT echo "Configuring GPG signing" # Setup GPG - Does 50% of the signing brew install gpg2 gnupg pinentry-mac # Make the gnupg dir# mkdir ~/.gnupg # Write our configuration to it echo 'pinentry-program /usr/local/bin/pinentry-mac' > ~/.gnupg/gpg-agent.conf # This tells gpg to use the gpg-agent echo 'use-agent' > ~/.gnupg/gpg.conf export GPG_TTY=`tty` chmod 700 ~/.gnupg # When the script is done go here to complete the gpg setup # https://gist.github.com/troyfontaine/18c9146295168ee9ca2b30c00bd1b41e#step-7-create-your-gpg-key ## Add your git configs below # brew install git-extras ## Or configure aliases below echo "Adding git global configuration file..." cat > ~/.gitconfig <<- "EOF" [user] name = your name email = your email [color] # Enable colors in color-supporting terminals ui = auto [init] templatedir = ~/.git-templates defaultBranch = main [alias] # List available aliases aliases = !git config -l | grep alias | cut -c 7- # Display state of working dir s = status # Add files to the staging area a = add . # Creates a commit and opens default editor e.g. vim c = commit # Creates a commit, bypasses default editor and adds the message e.g. git cm "Add whatever..." cm = commit -m # Checkout a specific branch co = checkout # Create branch and checkout cob = checkout -b # Show all remote branches remotes = remote -v # Show all branches ab = branch -av # Delete branch db = branch -D # Deletes all branches except master or main (change the name) CAREFUL deleteAll = !git branch | grep -v master | xargs git branch -D # Rename current branch rename = branch -M # Disables push to upstream disable = remote set-url --push upstream DISABLED # Log with super powers lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' # Log that shows the last 10 commits...change the number for more lgs = log -10 --color=always --all --graph --topo-order --pretty='%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative # Push current feature branch pb = !git push --set-upstream origin \"$(git rev-parse --abbrev-ref HEAD)\" # Ensure that force-pushing won't lose someone else's work (only mine). push-with-lease = push --force-with-lease # Rebase won’t trigger hooks on each "replayed" commit. # This is an ugly hack that will replay each commit during rebase with the # standard `commit` command which will trigger hooks. rebase-with-hooks = rebase -x 'git reset --soft HEAD~1 && git commit -C HEAD@{1}' # List local commits that were not pushed to remote repository review-local = !git lg @{push}.. # Edit last commit message amend = commit --amend # Undo last commit but keep changed files in stage undo = reset --soft HEAD~1 # Remove file(s) from Git but not from disk untrack = rm --cached # Deletes any local branch which has been deleted from the remote fp = fetch --prune # Ensures that when you stash, you catch the new files you haven’t caught with a git add yet. a.k.a stash-all sa = stash save --include-untracked # Pop the stash in the current branch sp = stash pop # Removes the saved stash sd = stash drop # Fetch origin fo = fetch origin # Fetch upstream fu = fetch upstream # CAREFUL WITH BELOW COMMANDS >> See Rebase docs # Rebase origin/master ro = rebase origin/master # Rebase upstream/master ru = rebase upstream/master # Rebase master rem = rebase master [status] # Shows you all of the files underneath that new directory during a git status (slow in very large repos) showUntrackedFiles = all [commit] # Git commit default message template template = ~/.git-templates/.git-commit-msg-template.txt # Prevent commit status from attaching below the custom template status = false # Ensures that all of your commits are signed by your GPG key. # gpgsign = true [merge] # Get an error unless every merge is fast-forward ff = only # Intended changes from the ‘left’ and the intended changes from the ‘right, with third section of the original changes before ‘left’ and ‘right’ tried to change it. conflictstyle = diff3 [core] # Hooks directory hooksPath = ~/.git-templates/hooks # Global ignore file excludesfile = ~/.git-templates/.gitignore_global [diff] # Use better, descriptive initials (c, i, w) instead of a/b. mnemonicPrefix = true # Show renames/moves as such renames = true # When using --word-diff, assume --word-diff-regex=. wordRegex = . # Display submodule-related information (commit listings) submodule = log [log] # Use abbrev SHAs whenever possible/relevant instead of full 40 chars abbrevCommit = true # Automatically --follow when given a single path follow = true # Disable decorate for reflog # (because there is no dedicated `reflog` section available) decorate = false [color "branch"] # Blue on black is hard to read in git branch -vv: use cyan instead upstream = cyan [color "status"] added = green changed = yellow untracked = red [gpg] program = /usr/local/bin/gpg [push] default = simple EOF echo "Adding commit message validation hook..." ## https://gist.github.com/ivanrodjr/e1b5454ec1593ea753776bdc5b44ce9a#file-git-commit-msg-template mkdir -p ~/.git-templates/hooks cat > ~/.git-templates/hooks/commit-msg <<- "EOF" #!/bin/bash commit_msg="${1}" commit_msg_lines=() error_msg="[COMMIT FAILED]" allowed_subject_verbs=('Add' 'Create' 'Update' 'Change' 'Fix' 'Refactor' 'Clean up' 'Remove' 'Move' 'Delete' 'Resolve' 'Merge' 'Initial') JIRA_URL="https://yourjiraurl.net/browse" # Splits the commit msg into separate lines and adds them to an array split_commit_msg() { while IFS= read -r line; do # Trim trailing spaces from lines shopt -s extglob line="${line%%*( )}" shopt -u extglob # Ignore comments (lines starting with #) if [[ ! "${line}" =~ ^# ]]; then commit_msg_lines+=("${line}") fi done < <(cat ${commit_msg}) } # Validates the commit msg validate_commit_msg() { split_commit_msg # Store the subject local subject="${commit_msg_lines[0]}" # Stop validation if the message is empty or no subject is set if [[ -z "${commit_msg_lines[*]}" ]] || [[ -z "${subject}" ]]; then exit 0 fi # Check if the subject has leading whitespace(s) if [[ "${subject}" =~ ^[[:space:]]+ ]]; then echo "${error_msg} The subject can not have leading whitespace" exit 1; fi # Check if the subject contains more than 1 word if [[ $(echo "${subject}" | wc -w) -eq 1 ]]; then echo "${error_msg} The subject has to contain more than 1 word" exit 1 fi # Check if the subject is separated from the body with a blank line if [[ ${#commit_msg_lines[@]} -gt 1 ]] && [[ -n "${commit_msg_lines[1]}" ]]; then echo "${error_msg} Separate subject from body with a blank line" exit 1 fi # Check if the subject line is limited to 50 characters if [[ "${#subject}" -gt 50 ]]; then echo "${error_msg} Limit the subject line to 50 characters (${#commit_msg_lines[0]} characters used)" exit 1 fi # Check if the subject line is capitalized if [[ ! "${subject}" =~ ^[A-Z] ]]; then echo "${error_msg} Capitalize the subject line" exit 1 fi # Check if the subject line does not end with a period if [[ ! "${subject}" =~ [^\.]$ ]]; then echo "${error_msg} Do not end the subject line with a period" exit 1 fi # Check if the subject starts with one of the allowed verbs local first_word=$(echo "${subject}" | awk '{print $1;}') local is_allowed=false for verb in "${allowed_subject_verbs[@]}"; do if [[ "${verb}" == "${first_word}" ]]; then is_allowed=true fi done if [[ "${is_allowed}" == false ]]; then echo "${error_msg} Use the imperative mood in the subject line" echo "Your subject has to start with one of the following verbs:" printf -- ' - %s\n' "${allowed_subject_verbs[@]}" exit 1 fi # Check if the body is wrapped at 72 characters (except for url's) for line in "${commit_msg_lines[@]}"; do if [[ "${line}" =~ ^[[:space:]]*(https?|ftp|file):\/\/[-A-Za-z0-9\+\&\@\#\/\%\?\=\~\_\|\!\:\,\.\;]*[-A-Za-z0-9\+\&\@\#\/\%\=\~\_\|] ]]; then continue elif [[ "${#line}" -gt 72 ]]; then echo "${error_msg} Wrap the body at 72 characters (${#line} characters used)" exit 1 fi done } validate_commit_msg # Remove comment lines from the message sed -i.bak '/^#/ d' $1 # Add Jira ticket number from branch to the commit message if [ -z "$BRANCHES_TO_SKIP" ]; then BRANCHES_TO_SKIP=(master main develop staging test) fi # Get the current branch name and check if it is excluded BRANCH_NAME=$(git symbolic-ref --short HEAD) BRANCH_EXCLUDED=$(printf "%s\n" "${BRANCHES_TO_SKIP[@]}" | grep -c "^$BRANCH_NAME$") # Trim it down to get the parts we're interested in TRIMMED=$(echo $BRANCH_NAME | sed -e 's:^\([^-]*-[^-]*\)-.*:\1:' -e \ 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/') # If it isn't excluded, preprend the trimmed branch identifier to the given message if [ -n "$BRANCH_NAME" ] && ! [[ $BRANCH_EXCLUDED -eq 1 ]]; then sed -i.bak -e "1s/^/[$TRIMMED] /" $1 fi # Add the ticket url at the end of the commit description printf "\n${JIRA_URL}/${TRIMMED}" >> $1 # Remove trailing blank lines sed -i.bak -e :a -e '/^\n*$/{$d;N;ba' -e '}' $1 EOF # Make the above hook executable chmod 755 ~/.git-templates/hooks/commit-msg ## Add global .gitignore file ## https://gist.github.com/jacobtomlinson/aace65a6920e44348d501da5e13a5a17#file-gitignore_global echo "Adding git global ignore file..." cat > ~/.git-templates/.gitignore_global<<- "EOF" # Compiled source # ################### *.com *.class *.dll *.exe *.o *.so *.pyc *.pyo # Packages # ############ # it's better to unpack these files and commit the raw source # git has its own built in compression methods *.7z *.dmg *.gz *.iso *.jar *.rar *.tar *.zip *.msi # Logs and databases # ###################### *.log *.sql *.sqlite npm-debug.log # OS generated files # ###################### .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db desktop.ini # Temporary files # ################### *.bak *.swp *.swo *~ *# # IDE files # ############# .vscode .idea .iml *.sublime-workspace EOF ## Add default commit message ## https://gist.github.com/ivanrodjr/e1b5454ec1593ea753776bdc5b44ce9a#file-commit-msg echo "Adding default commit message..." cat > ~/.git-templates/.git-commit-msg-template.txt <<- "EOF" # If applied, this commit will... (max 50 characters long) # Body - Explain things in more detail, possibly giving some # background about what is being added or the issue being fixed, etc. # (Wrap at 72 characters) # Fixes #XXXX # Provide links to any relevant tickets, articles or other resources #------------------------------------------------@---------------------* # # Remember the seven rules of a great Git commit message: # - Separate subject from body with a blank line # - Limit the subject line to 50 characters (@) # - Capitalize the subject line # - Do not end the subject line with a period # - Use the imperative mood in the subject line # - Wrap the body at 72 characters (*) # - Use the body to explain what and why vs. how # # More info: https://chris.beams.io/posts/git-commit/ # # The allowed first words for the subject are: # - Add # - Create # - Update # - Change # - Fix # - Refactor # - Clean up # - Remove # - Move # - Delete # - Resolve # - Merge # - Initial # Edit allowed works in commit-msg hook EOF ############ ## VIM ############ ## Add your vimrc config below ## https://gist.github.com/simonista/8703722 echo "Adding vimrc configuration..." cat > ~/.vimrc <<- "EOF" set textwidth=72 set colorcolumn=73 syntax on EOF # # ############# # # # JAVA # # ############# echo "Installing Java and setting up the environment..." JAVA_VERSION=11 brew tap AdoptOpenJDK/openjdk ## brew install --cask | For version https://github.com/AdoptOpenJDK/homebrew-openjdk echo "Installing Java $JAVA_VERSION..." brew install --cask adoptopenjdk$JAVA_VERSION # Set JAVA_HOME echo "Setting JAVA_HOME environment variable..." echo export "JAVA_HOME=\$(/usr/libexec/java_home)" >> ~/.zshrc # # https://www.mindissoftware.com/post/2020/05/jenv-setup/ echo "Installing jenv..." brew install jenv echo "Configuring jenv..." echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc echo 'eval "$(jenv init -)"' >> ~/.zshrc echo "Unsetting the JAVA_TOOL_OPTIONS environment variable..." unset JAVA_TOOL_OPTIONS # # ############# # # # Node # # ############# # # ## NODE > Uncomment your preffered option # # ## 1 . Install complete node # # # brew install node # # OR Install with yarn package manager # # brew install yarn # # ## 3. NPM new home (Optional) # # # Normally, with Homebrew, your user should own the entire `/usr/local` folder, if not: # # # sudo chown -R `whoami` /usr/local # # # mkdir -p /usr/local/npm_packages # # ## This is the root dir where all globally installed node packages will go # # export NPM_PACKAGES="/usr/local/npm_packages" # # export NODE_PATH="$NPM_PACKAGES/lib/node_modules:$NODE_PATH" # # ## Add to PATH # # #export PATH="$NPM_PACKAGES/bin:$PATH" # # ############# # # # Deno # # ############# # # brew install deno # # ############# # # # Scala # # ############# # # brew install scala # # ############# # # # Golang # # ############# # # brew install golang # # export GOPATH="${HOME}/.go" # # export GOROOT="$(brew --prefix golang)/libexec" # # export PATH="$PATH:${GOPATH}/bin:${GOROOT}/bin" # # ############# # # # Rust # # ############# # # brew install rustup # # rustup-init # # ############# # # # Docker # # ############# # # # brew install --cask docker # # ############################################################################### # # # 3 - Customization & Mac OS Configuration # # ############################################################################### # Get your desired fonts from: https://github.com/Homebrew/homebrew-cask-fonts/tree/master/Casks And add below brew tap homebrew/cask-fonts FONTS=( font-inconsolata font-fira-code font-fira-code-nerd-font font-jetbrains-mono font-jetbrains-mono-nerd-font ) echo "Installing FONTS..." brew install ${FONTS[@]} echo "Cleaning up the cellar..." brew cleanup echo "Configuring MacOS..." echo "" # FIND MORE CONFIGS HERE : # https://gist.github.com/bradp/bea76b16d3325f5c47d4#file-setup-sh-L137 # https://gist.github.com/MatthewMueller/e22d9840f9ea2fee4716 echo "Disabling OS X Gate Keeper" #"(You'll be able to install any app you want from here on, not just Mac App Store apps)" sudo spctl --master-disable sudo defaults write /var/db/SystemPolicy-prefs.plist enabled -string no defaults write com.apple.LaunchServices LSQuarantine -bool false echo "Expanding the save panel by default" defaults write NSGlobalDomain NSNavPanelExpandedStateForSaveMode -bool true defaults write NSGlobalDomain PMPrintingExpandedStateForPrint -bool true defaults write NSGlobalDomain PMPrintingExpandedStateForPrint2 -bool true echo "Disable smart quotes and smart dashes as they are annoying when typing code" defaults write NSGlobalDomain NSAutomaticQuoteSubstitutionEnabled -bool false defaults write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false echo "Enabling full keyboard access for all controls (e.g. enable Tab in modal dialogs)" defaults write NSGlobalDomain AppleKeyboardUIMode -int 3 echo "Showing all filename extensions in Finder by default" defaults write NSGlobalDomain AppleShowAllExtensions -bool true echo "Disabling the warning when changing a file extension" defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false echo "Use column view in all Finder windows by default" defaults write com.apple.finder FXPreferredViewStyle Clmv echo "Avoiding the creation of .DS_Store files on network volumes" defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true echo "Setting the icon size of Dock items to 36 pixels for optimal size/screen-realestate" defaults write com.apple.dock tilesize -int 36 echo "Speeding up Mission Control animations and grouping windows by application" defaults write com.apple.dock expose-animation-duration -float 0.1 defaults write com.apple.dock "expose-group-by-app" -bool true echo "Setting Dock to auto-hide and removing the auto-hiding delay" defaults write com.apple.dock autohide -bool true defaults write com.apple.dock autohide-delay -float 0 defaults write com.apple.dock autohide-time-modifier -float 0 echo "Setting email addresses to copy as 'foo@example.com' instead of 'Foo Bar ' in Mail.app" defaults write com.apple.mail AddressesIncludeNameOnPasteboard -bool false echo "Preventing Time Machine from prompting to use new hard drives as backup volume" defaults write com.apple.TimeMachine DoNotOfferNewDisksForBackup -bool true echo "Speeding up wake from sleep to 24 hours from an hour" # http://www.cultofmac.com/221392/quick-hack-speeds-up-retina-macbooks-wake-from-sleep-os-x-tips/ sudo pmset -a standbydelay 86400 echo "Disable annoying backswipe in Chrome" defaults write com.google.Chrome AppleEnableSwipeNavigateWithScrolls -bool false echo "Setting screenshots location to ~/Desktop" defaults write com.apple.screencapture location -string "$HOME/Desktop" echo "Setting screenshot format to PNG" defaults write com.apple.screencapture type -string "png" echo "Enabling the Develop menu and the Web Inspector in Safari" defaults write com.apple.Safari IncludeDevelopMenu -bool true defaults write com.apple.Safari WebKitDeveloperExtrasEnabledPreferenceKey -bool true defaults write com.apple.Safari "com.apple.Safari.ContentPageGroupIdentifier.WebKit2DeveloperExtrasEnabled" -bool true echo "Don't prompt for confirmation before downloading in Transmission" defaults write org.m0k.transmission DownloadAsk -bool false echo "Trash original torrent files" defaults write org.m0k.transmission DeleteOriginalTorrent -bool true echo "Hide the donate message" defaults write org.m0k.transmission WarningDonate -bool false echo "Hide the legal disclaimer" defaults write org.m0k.transmission WarningLegal -bool false echo "Disable 'natural' (Lion-style) scrolling" defaults write NSGlobalDomain com.apple.swipescrolldirection -bool false echo "Don’t rearrange Spaces automatically based on most recent use" defaults write com.apple.dock mru-spaces -bool false echo "Show Library dir in Finder" chflags nohidden ~/Library echo "Show Hidden Files in Finder" defaults write com.apple.finder AppleShowAllFiles YES echo "Show Path Bar in Finder" defaults write com.apple.finder ShowPathbar -bool true echo "Show Status Bar in Finder" defaults write com.apple.finder ShowStatusBar -bool true echo "Require password as soon as screensaver or sleep mode starts" defaults write com.apple.screensaver askForPassword -int 1 defaults write com.apple.screensaver askForPasswordDelay -int 0 echo "Enable tap-to-click" defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true defaults -currentHost write NSGlobalDomain com.apple.mouse.tapBehavior -int 1 echo "" # ############################################################################### # # 4 - CREATE DIR STRUCTURE - Create any addditional dirs # ############################################################################### echo "Creating directory structure..." [[ ! -d Workspace ]] && mkdir Workspace echo "Directory structure created" echo "Setup for $(whoami) COMPLETE" killall Finder # ############################################################################### # # 5 - Install oh-my-zsh # ############################################################################### # Install oh-my-zsh # After installation finishes execute oh-my-custom for personalisation echo "Installing oh-my-zsh..." /bin/bash -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"