1

Introduction

2

To be able to have a consistent experience between my laptop at home, and the one I use at work, I try to have as much

3

of my configuration as possible under source control. Hence, I have a large repository with different types of

4

configuration files, and shell scripts for symlinking them.

5

6

I also keep scripts for managing my installed applications, and in this post I'll share some parts of the script I

7

use for installing applications on macOS.

8

9

At the beginning of the script, I ensure that Homebrew is installed:

10

11
# Install homebrew if it doesn't exist.
12
if test ! $(which brew); then
13
   /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
14
   echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/$USER/.zprofile
15
   eval "$(/opt/homebrew/bin/brew shellenv)"
16
fi
12

13

If you are unfamiliar with Homebrew, it divides programs into two categories. Programs with a GUI are called

14

casks. Below, I have created two arrays where I list both the casks and the regular packages that I wish to

15

install:

16

17
# Packages we want to have installed
18
WANTED_PACKAGES=(
19
  awscli
20
  btop
21
  coreutils
22
  direnv
23
  dive
24
  dockutil
25
  efm-langserver
26
  flyctl
27
  fzf
28
  git-crypt
29
  git-lfs
30
  gnupg
31
  go
32
  golang-migrate
33
  goreleaser
34
  hey
35
  hugo
36
  jq
37
  mongodb-community
38
  ncdu
39
  neovim
40
  orlangure/tap/gocovsh
41
  pnpm
42
  postgresql
43
  protobuf
44
  ripgrep
45
  rust
46
  starship
47
  tfenv
48
  tig
49
  tldr
50
  tmux
51
  tree
52
  volta
53
  wget
54
  youtube-dl
55
  zsh-autosuggestions
56
  zsh-syntax-highlighting
57
)
58
 
59
# Casks we want to have installed
60
WANTED_CASKS=(
61
  1password
62
  1password-cli
63
  amethyst
64
  centered
65
  discord
66
  figma
67
  firefox
68
  firefox-developer-edition
69
  goland
70
  google-chrome
71
  insomnia
72
  kap
73
  kitty
74
  mongodb-compass
75
  monodraw
76
  nordvpn
77
  obsidian
78
  postman
79
  raycast
80
  redisinsight
81
  slack
82
  spotify
83
  vlc
84
)
18

19

For subsequent runs of the script, I don't want to add a bunch of noise by trying to install the same application over

20

and over again. Therefore, I filter out the wanted packages against the ones that I have installed already:

21

22
# Already installed packages and casks
23
INSTALLED_PACKAGES=$(brew leaves)
24
INSTALLED_CASKS=$(brew list --cask)
25
 
26
# Remove the packages that we already have installed from WANTED_PACKAGES
27
for index in "${!WANTED_PACKAGES[@]}"; do
28
  if [[ "${INSTALLED_PACKAGES[*]}" =~ "${WANTED_PACKAGES[$index]}" ]]; then
29
    unset -v WANTED_PACKAGES[$index]
30
  fi
31
done
32
 
33
# Remove the casks that we already have installed from WANTED_CASKS
34
for index in "${!WANTED_CASKS[@]}"; do
35
  if [[ "${INSTALLED_CASKS[*]}" =~ "${WANTED_CASKS[$index]}" ]]; then
36
    unset -v WANTED_CASKS[$index]
37
  fi
38
done
23

24

Most packages are included in the global homebrew formula. However, there are some that require us to add

25

third-party repositories. To add third-party repositories, we use brew tap <repository>.

26

27
# Add third party repositories
28
brew tap mongodb/brew
29
brew tap homebrew/cask-versions
30
brew tap homebrew/cask-fonts
28

29

At this point we are ready to update homebrew, upgrade any of our existing packages, and install new ones:

30

31
# Make sure that homebrew is using the latest formula
32
brew update
33
# Upgrade any existing applications
34
# NOTE: If we want to lock a package to a specific version we can use "brew pin"
35
brew upgrade
36
 
37
# Install missing packages
38
if [ ${#WANTED_PACKAGES[@]} -eq 0 ]; then
39
  echo "All packages are already installed"
40
else
41
  brew install ${WANTED_PACKAGES[@]}
42
fi
43
 
44
# Install missing casks
45
if [ ${#WANTED_CASKS[@]} -eq 0 ]; then
46
  echo "All casks are already installed"
47
else
48
  brew install ${WANTED_CASKS[@]} --cask
49
fi
32

33

If you want to see more of my scripts, you can check out my repository on GitHub. I continuously add new

34

scripts for things such as setting up folder structures, generating SSH keys, orchestraiting docker containers,

35

managing my tmux sessions, and so on.

35

36

The end

37

I usually tweet something when I've finished writing a new post. You can find me on Twitter

38

by clicking 

normalintroduction.md
||156:39

Recently Edited

Recently Edited

File name

Tags

Time to read

Created at

context

  • go
  • context
8 minutes2024-02-28

circular-buffers

  • go
  • concurrency
  • data processing
5 minutes2024-02-04

go-directives

  • go
  • compiler
  • performance
4 minutes2023-10-21

async-tree-traversals

  • node
  • trees
  • graphs
  • typescript
19 minutes2023-09-10

All Files

All Files

  • go

    5 files

  • node

    2 files

  • typescript

    1 file

  • frontend

    1 file

  • workflow

    7 files