---
title: New Laptop Configuration
date: 2019-04-10
description: Step-by-step guide for setting up a new macOS development environment with Homebrew, Git, Python, Go, Vim, and tmux.
tags: [shell, vim]
---

## Introduction

I'm an engineer with a _new_ laptop, which requires setting up with various
development tools and configuration. This post is my attempt to capture and
document my process for getting a new dev environment set-up.

I used to try and automate a lot of this with bash scripts, but realised over
time that things go out of date quite quickly (e.g. OS configurations can change
substantially, as well as my preferred ways of working).

I also find that if an error occurs with an automated script (unless you've
coded things defensively enough) you can end up with your machine in a weirdly
broken state.

Given a straight forward set of instructions, doing things _manually_ doesn't
take long at all, and you can modify things at that point in time without just
blindly installing various things you no longer need.

## Defaults

It's good to begin by surveying your current system and understanding what you
have already installed. For me this looked something like:

- OS: macOS Mojave (`10.14.4`)
- Curl: `/usr/bin/curl` (`7.54.0`)
- Bash: `/bin/bash` (`3.2.57`)
- Python: `/usr/bin/python` (`2.7.10`)
- Ruby: `/usr/bin/ruby` (`2.3.7p456`)
- Git: `/usr/bin/git` (`2.20.1`)
- $PATH: `/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`

What's worth me noting additionally here is that I primarily use two programming
languages: Go and Python. The reason I mention this is because Python has an
interesting history with the name of its binaries.

The binary name `python` generally refers to Python version `2.x`. Where as
Python `3.x` has traditionally been named `python3` to help distinguish the two.
So looking above we can see `which python` reveals the location as
`/usr/bin/python` and without checking the version (e.g. `python --version`) I
was fairly certain it would be a `2.x` version (based on the naming history).

This has been the generally accepted rule for a while, _except!_ when dealing
with tools that handle virtual environments.

For example, [pipenv](https://pipenv.readthedocs.io/) is a tool that helps you
to manage not only different Python versions but also the dependencies installed
for different projects (referred to as virtual environments). A tool like pipenv
will proxy a command such as `python` through a shim script (e.g.
`/Users/integralist/.pyenv/shims/python`) and that shim script will then
determine which Python interpreter/binary to execute.

A shim script typically identifies the virtual environment you're working under
and will then figure out the most appropriate Python interpreter to invoke. So
within that virtual env if you call `python`, then you may not necessarily get
the Python2 interpreter, as your virtual env might be configured such that the
expectation is to proxy your invocation to a Python3 interpreter.

This is why, when setting up a new laptop, getting a good development
environment setup is essential because it can get quite confusing untangling a
mess of default Python's vs `brew install ...` versions of Python 3 and then
subsequently using multiple environment tools like `pipenv` which confuse things
further by hiding the actual versions behind the generically named `python`
command.

The situation reminds me a lot of [XKCD's classic comic
strip](https://xkcd.com/1987/)...

![python env](/assets/img/python_env.png)

## Package Manager

Let's begin our journey by first installing a 'package manager'. This software
will enable us to search and install various pieces of software. The macOS
provides its own GUI implementation referred to as the 'App Store', but it's
heavily moderated by Apple and an app can only be found there if it abides by
Apple's own set of rules and criteria for what they consider to be 'safe'
software.

> [!INFO]
> there are many apps that aren't available in the App Store because Apple
> can be a bit anti-competition (see [Spotify's "time to play fair"
> campaign](https://timetoplayfair.com)).

So we have to download our own package manager, and the defacto standard for
macOS is a program called [Homebrew](https://brew.sh/) (which is a terminal
based tool, so no GUI). In fact, I'm not actually sure what _alternatives_ to
Homebrew exist (other than [MacPorts](https://www.macports.org), which if you
want to understand the differences between it and Homebrew then [read
this](https://saagarjha.com/blog/2019/04/26/thoughts-on-macos-package-managers/))?
On Linux you have tools such as `yum` or `apt` but for macOS you either use the
built-in App Store or find your own alternative (so in this case, we'll use
Homebrew).

To install Homebrew, execute the following command in your terminal:

```
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

> [!INFO]
> notice that installation command uses the default installation of Ruby
> which Homebrew presumes is available (and for the most part is a safe
> presumption to make as Ruby as been provided by macOS for the longest time).

If you need to update Homebrew you can execute a `brew update` command, but the
installation will install the latest version any way, so that won't be
necessary.

## Essential Packages

OK, so I start with what I refer to as a 'essential packages', and specifically
these are packages that do not require any configuration on my part. Meaning, I
can install them and consider the job done, where as with other packages I
install I'll have to make some additional tweaks to (which we'll see as we move
on past the 'essential' segments of this post).

To install a package via Homebrew, execute the following command in your
terminal:

```
brew install <package_name>
```

Here is a list of the packages I'll install:

- `ag`: a `grep` like tool (aka. the_silver_searcher)
- `gnu-sed`: it's the gnu version of `sed` (`gsed`) used for filtering/transforming text
- `jq`: tool for parsing/inspecting json output
- `docker`: useful for running containerized programs
- `hugo`: static site generator (used to make this website)
- `node`: server-side js programming language (used to compile a static search feature for my website)
- `pwgen`: generates random passwords
- `reattach-to-user-namespace`: used by tmux for clipboard storage
- `shellcheck`: bash linter
- `transmission`: torrent client † (alt. `npm install -g t-get`)
- `tree`: displays directory heirarchy structures as a tree
- `watch`: executes given command every N seconds

> [!TIP]
> † see [transmission user guide](https://cli-ck.io/transmission-cli-user-guide/)

Here's a handy one-liner:

```
brew install ag gnu-sed jq docker hugo node pwgen reattach-to-user-namespace shellcheck tree watch
```

## Essential Apps

Homebrew now allows you to also install GUI applications, not just command line
tools, but to do this you'll need to configure Homebrew to use `Cask`:

```
brew tap homebrew/cask
```

Once that's done you can install an app via Homebrew using the command:

```
brew cask install <app_name>
```

Here is a list of the apps I'll install:

- `alfred`: like Apple's Spotlight search, but better
- `caffeine`: stops the Mac from going to sleep
- `dash`: offline documentation
- `docker`: this is the counter-part to the 'package' installed earlier †
- `google-backup-and-sync`: syncs files between computer and Google Drive
- `google-chrome`: web browser
- `lepton`: GitHub Gist UI
- `slack`: communication tool
- `spotify`: music streaming service
- `vlc`: video player with support of lots of codecs

> [!IMPORTANT]
> † if you installed the docker 'package', then you _need_ the docker 'app' as
> well for it to work. You can't have one without the other (this is because the
> app sets up the interface for macOS to interact with the underlying docker
> client/server implementation).

Here's a handy one-liner:

```bash
brew cask install alfred caffeine dash docker google-backup-and-sync google-chrome lepton slack spotify vlc
```

The [Dash](https://kapeli.com/dash) app will ask you what documentation you
would like to download so it's available offline. I use the following docsets (I
used to have _lots_ more but realised I never really used them, so this is my
'essential' docs list):

- boto3
- Go
- HTTP Header Fields
- HTTP Status Codes
- NGINX
- Python2
- Python3
- Regular Expressions
- tmux
- Tornado
- vim

A couple of apps probably worth mentioning are:

- https://github.com/dwarvesf/hidden
- https://github.com/leits/MeetingBar

## Curl

I like to use a more modern version of `curl` (e.g. supports HTTP/2, and other features):

```bash
brew install curl
```

But in order to use this version of curl you'll need to modify your `$PATH`:

```bash
export PATH="/usr/local/opt/curl/bin:$PATH"
```

## Git

Similarly to curl, I like to have the most recent version of `git` installed:

```bash
brew install git
```

Once that's installed I configure it like so:

```bash
curl -LSso ~/.git-prompt.sh https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh
curl -LSso ~/.gitignore-global https://raw.githubusercontent.com/Integralist/dotfiles/master/.gitignore-global
curl -LSso ~/.gitconfig https://raw.githubusercontent.com/Integralist/dotfiles/master/.gitconfig
```

> [!TIP]
> it's always worth checking `~/.gitignore-global` is up to date (i.e. not
> referencing file types I no longer work with).

## Shell

To install and configure latest version of the Bash shell, execute the following
commands:

```bash
brew install bash
echo /usr/local/bin/bash | sudo tee -a /etc/shells
chsh -s /usr/local/bin/bash
```

Also make sure to install auto-complete for bash:

```bash
brew install bash-completion
```

Finally, we'll configure Bash like so:

```bash
curl -LSso ~/.bash-preexec.sh https://raw.githubusercontent.com/rcaloras/bash-preexec/master/bash-preexec.sh
curl -LSso ~/.bashrc https://raw.githubusercontent.com/Integralist/dotfiles/master/.bashrc
curl -LSso ~/.bash_profile https://raw.githubusercontent.com/Integralist/dotfiles/master/.bash_profile
```

> [!TIP]
> `~/.bashrc` references `~/.fzf.bash` which is needed, and comes from
> installing the FZF vim plugin (which we'll sort out shortly).

## Terminal

To install my custom terminal theme:

```bash
curl -LSso /tmp/Integralist.terminal \
https://raw.githubusercontent.com/Integralist/mac-os-terminal-theme-integralist/master/Integralist.terminal
open /tmp/Integralist.terminal
rm /tmp/Integralist.terminal
```

> [!TIP]
> don't forget to change the terminal font to menlo (if not already set)
> and also set `Integralist` theme as your default. I used to do this via
> `defaults write com.apple.Terminal "Default Window Settings" Integralist` and
> `defaults write com.apple.Terminal "Startup Window Settings" Integralist` but
> those have changed now in the latest macOS (see `defaults read`).

### UPDATE 2020.09.08

I've reverted to using the "Basic" theme provided by macOS, and just modifying
the font to be "Menlo Regular 16 pt."

## GitHub

Let's now set-up a new SSH key for GitHub access:

```bash
mkdir ~/.ssh
cd ~/.ssh && ssh-keygen -t rsa -b 4096 -C 'foobar@example.com'
eval "$(ssh-agent -s)"
ssh-add -K ~/.ssh/github_rsa
```

Don't forget to `pbcopy < ~/.ssh/github_rsa.pub` and paste your public key into
the GitHub UI. Once that's done you can execute the following command to test
your SSH key is set-up correctly and working:

```bash
ssh -T git@github.com
```

> [!WARNING]
> there is a slight catch-22 here which is if your password for GitHub is
> in your Password Store (see next section), then that makes things trickier.
> For me I also have a copy of the encrypted store on my phone and so I can
> utilise that to access the password. But failing that, you can just 'reset
> your password' in GitHub UI's and follow the email instructions to gain access
> and thus login and add your new SSH key.

## Password Store

I use the open-source [password store](https://www.passwordstore.org) for
handling secrets and passwords. This tool provides the `pass` command, and that
requires the use of `gpg`, so let's start by installing GPG:

```bash
brew install gpg
```

Now you have `gpg`, make sure you pull your private key from wherever you have
it stored (e.g. external USB stick), then execute:

```bash
gpg --import <private-key>
gpg --export <key-id> # public key by default
```

> [!TIP]
> don't forget you can _sign_ your git commits:\
> `git config --global user.signingkey <key-id>`

Next, install `pass` and `pass otp` commands:

```
brew install pass pass-otp zbar
```

You can now pass a QR code into `pass otp` and use the terminal for generating
one-time pass codes for 2FA/MFA authentication:

```bash
zbarimg -q --raw /tmp/qr.png | pass otp insert Work/Acme/totp/foo`  

pass otp -c Work/Acme/totp/foo
```

> [!INFO]
> installing `zbar` provides the `zbarimg` command

Lastly, we need to setup a new Password Store, and to do that we need to provide
our GPG key id:

```bash
# <ref> needs to be your email, or part of the key's description
keyid=$(gpg --list-keys <ref> | head -n 2 | tail -n 1 | cut -d ' ' -f 7)

pass init $keyid
```

Now we can pull our Password Store from a _private_ repository:

```bash
pass git init
pass git remote add origin git@github.com:Foo/Bar.git
pass git pull
git branch --set-upstream-to=origin/master master
```

> [!TIP]
> I also like to ensure my encrypted datastore is sync'ed up to other
> online providers, and symlinked to `~/.password-store` as well so changes are
> backed up automatically in multiple places.

### Mobile Password Store

There is also a [mobile app for
Android](https://github.com/android-password-store/Android-Password-Store) that
you can download from the Google Play store (and other places) that allows you
to access the Password Store if it has been pushed to a distributed
version-control system such as GitHub (better still if the repository is private
-- "out of sight, out of mind").

To get set-up, go through the following steps:

- Install Password Store app.
- Select "Clone from Server" option.
- Add in github credentials (e.g. `git@github.com:Foo/Bar.git`)
- Create new SSH key via Password Store app (give it a password).
- Encrypt your SSH key with symmetrical encryption (e.g. `gpg --symmetric`)
- Email SSH key to self.
- Decrypt SSH key and copy it into GitHub UI.
- Password Store app will ask for SSH key password, then it'll clone the repo.

Before you can access the content of the Password Store (remember all the
content is individual GPG keys) you'll need the GPG _private_ key in order to
decrypt files that would have been encrypted using your public key.

- Export your private key as ASCII (via laptop: `gpg --armor --output passkey.txt --export-secret-keys <key_id>`).
- Encrypt your exported private key with symmetrical encryption (`gpg --symmetric passkey.txt`)
- Email your private key to yourself.
- Download private key to your phone.
- Install OpenKeychain app (via Google Play)
- Locate the downloaded encrypted private key and open with OpenKeychain app.
- Enter password used to encrypt the private key.
- Then click into the extracted `passkey.txt` file and then click "Import".
- In Password Store app set the options:
  - Select "OpenPGP Provider" (choose: OpenKeychain)
  - Select "OpenPGP Key id" (choose: the imported public key)

## Go

We'll install the latest version of `go` (as far as Homebrew is concerned):

```bash
brew install go
```

This is required because in order to handle different versions of `go` we'll
want to [manually compile
go](https://gist.github.com/af300f602fa4da8cc14863f36a24bd1e), and _that_
ironically requires _a_ version of the go compiler.

Finally, make sure the default Go directory is set in your `$PATH` so that any
installed binaries will be available:

```bash
export PATH="$HOME/go/bin:$PATH"
```

## Python

The macOS comes only with Python 2.x and although the specific version _should_
(according to the Python docs) have the `pip` command available, that's not the
case. So we have to install pip for Python2 manually using the _very old_ (but
built-in) `easy_install` command:

```bash
sudo easy_install pip
```

Now when running `pip --version` we should see:

```
pip 19.0.3 from /Library/Python/2.7/site-packages/pip-19.0.3-py2.7.egg/pip (python 2.7)
```

At this point I'm going to ask you to read [Python Management and Project
Dependencies](/posts/python-app-dependencies/) which is separate/dedicated post
I wrote about installing multiple Python versions and how to utilize virtual
environments.

### Python Packages

Here are some packages I like to install as a general rule...

- [black](https://github.com/python/black): formatter (like golang's `gofmt`)
- [flake8](http://flake8.pycqa.org): linter
- [flake8-import-order](https://github.com/PyCQA/flake8-import-order): validates imports
- [mypy](http://www.mypy-lang.org): static analysis
- [ipython](https://ipython.org): REPL
- [pytest](https://pytest.org): testing framework
- [structlog](http://www.structlog.org/en/stable/): structured logging
- [tornado](https://www.tornadoweb.org): async web framework
- [boto3](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html): AWS CLI tool
- [tox](https://tox.readthedocs.io/en/latest/): generic virtualenv management and testing tool

> [!TIP]
> for an example of how to configure Flake8 and its plugins, see [this
> gist](https://gist.github.com/0ce27db1d7294f3af9896c0807ccfeed).

I would also strongly recommend installing the following tools:

- `isort`
- `autopep8`
- `unimport`

Here's a one liner to install some of these packages that I'm guaranteed to use
in all projects...

```
python3 -m pip install isort autopep8 unimport tox mypy flake8 flake8-import-order
```

I then reference these in my `.vimrc`:

```
" Execute Python isort
autocmd BufWritePost *.py :execute '!isort %' | edit

" Execute Python autopep8
autocmd BufWritePost *.py :execute \
'!autopep8 --experimental --verbose --aggressive --aggressive --recursive --in-place %' | edit

" Execute Python unimport
autocmd BufWritePost *.py :execute '!unimport --remove %' | edit
```

If you're using pipx (a tool that helps to install packages as self isolated
binaries) just be sure the `pipx ensurepath` call doesn't update the shell
`PATH` by _appending_ the `/Users/integralist/.local/bin` but by _prepending_ it
instead. This might require you to manually update your `~/.bash_profile` like
so:

```
export PATH="/Users/integralist/.local/bin:$PATH"
```

> [!WARNING]
> if you install pipx via Homebrew then it'll be attached to that Python
> version. Meaning if you upgrade your Python version, then pipx could break
> (e.g. none of the installed packages will work). The solution is to run `pipx reinstall-all --python python3`.

## Vim

You can either install more recent version of vim via Homebrew:

```bash
brew install vim
```

Or you can manually compile vim yourself:

> [!INFO]
> I manually compile vim as I need Python3 support baked in, which
> Homebrew's version no longer does (it used to, but not any more). Python3
> support means my Python linting tools will work as expected.

So let's start at the beginning. To manually compile Vim you would think to do
something like the following...

```bash
git clone https://github.com/vim/vim.git

cd vim

./configure --with-features=huge \
            --enable-multibyte \
            --enable-rubyinterp=yes \
            --enable-python3interp=yes \  # relies on `brew install python3`
            --enable-perlinterp=yes \
            --enable-luainterp=yes \
            --enable-gui=gtk2 \
            --enable-cscope \
            --prefix=/usr/local
            
make && make install
```

The above code will copy the compiled vim binary into `/usr/local/bin` so `which vim` will show `/usr/local/bin/vim`

This works but I've found that it only works when the `python3` interpreter is
the same version as what Vim is itself internally expecting to be available.

What I mean by that is I recently upgraded my Python version to `3.7.7` and Vim
suddenly broke. I tried the above compilation and it didn't work. I could see
from the errors being printed that Vim was looking around my system for a Python
version `3.7.4` which didn't exist (hence Python3 support wasn't compiled into
vim).

The solution was to firstly to tell Vim what version of Python3 to use, and
secondly (and just as important) to ignore any previously cached aspects of a
compilation (e.g. if I tell you to use Python `3.7.7` don't then go and try to
be helpful and use a cached run where I was using `3.7.4` -- which really
confused me for a long time!):

> [!TIP]
> now I should say, that if you can use the above example compilation
> code, but just run `make clean distclean` first, then do that! I suspect I
> could have done that and Vim would have been compiled with Python `3.7.7` just
> by using the `--enable-python3interp` flag set. I didn't think about that at
> the time though, hence the following still worked.

```bash
make clean distclean

./configure --with-features=huge \
  --enable-multibyte \
  --enable-rubyinterp=yes \
  --enable-python3interp=yes \
  --with-python3-command=\
  /usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/bin/python3.7 \
  --with-python3-config-dir=\
  /usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/config-3.7m-darwin/ \
  --enable-perlinterp=yes \
  --enable-luainterp=yes \
  --enable-gui=gtk2 \
  --enable-cscope \
  --prefix=/usr/local

make && make install
```

The key flags are...

- `--enable-python3interp`: tell the compilation you want Python3 support
- `--with-python3-command`: give it a path to a Python3 interpreter/binary (†)
- `--with-python3-config-dir`: a configuration directory used by the version of Python3 you want to use.

> [!INFO]
> † e.g. if I run that full path in my terminal shell it'll actually run the
> Python3 REPL so I know it's a valid path to provide.

Things get even more confusing when you are using a Python version manager like
`pyenv` as that overrides the Python interpreter version. So although Vim might
report it's using Python `3.7.7` (as shown in the vim Ex command below), if you
shell out to a command like `isort` (e.g. `!isort %`) you'll find that the shell
will complain no such command exists.

This is because the command doesn't exist. Not for the version of Python that's
running in the shell! The shell is running whatever version of Python `pyenv`
has activated. So you need to make sure when you start vim that you activate a
virtual environment that has these tools available.

Here is the Ex command to see what version of Python vim is compiled with:

```
:py3 import sys; print(sys.version)

3.7.7 (default, Mar 10 2020, 15:43:03) 
[Clang 11.0.0 (clang-1100.0.33.17)]
```

So as I mentioned, the approach I take with Vim is to activate a specific
virtual environment when in a project repo.

This could be a `pyenv` virtual environment but actually it can just be a
standard Homebrew Python virtual environment:

```
# in a new shell where pyenv has no affect on the python interpreter

python3 -m venv venv/vim
source venv/vim/bin/activate
python3 -m pip install isort autopep8 unimport tox mypy flake8 flake8-import-order
```

Now I know that if I start up a new shell and `cd` to my project repo, and even
if `pyenv` has set the python interpreter I can activate the Homebrew Python
virtual environment I created (which is going via the Homebrew installed version
of Python) and Vim will know about the packages installed in that virtual
environment.

> [!WARNING]
> even if I do `python3 --version` the shell will now report the Homebrew
> version of Python (so it overrides the `pyenv` version that might have been
> set via a `.python-version` file)!

Next, I configure vim with [vim-plug](https://github.com/junegunn/vim-plug)
plugin manager:

```bash
curl -fLo ~/.vim/autoload/plug.vim \
  --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
curl -LSso ~/.vimrc https://raw.githubusercontent.com/Integralist/dotfiles/master/.vimrc
```

Ensure Vim is configured with spell checking options:

```bash
vim -E -s <<EOF
:set spell
:quit
EOF
```

Install plugins by opening vim and executing:

```
:PlugInstall
```

> [!TIP]
> [fzf](https://github.com/junegunn/fzf) doesn't need a brew install when
> installed via vim. See my `.vimrc` configuration file for more details, but in
> essence it contains: `Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }`

Also ensure the Golang environment has what it needs by executing:

```
:GoInstallBinaries
```

## Tmux

Install `tmux`:

```bash
brew install tmux
```

Configure tmux and expose `tmuxy` command (defined in my `~/.bashrc` for quickly
spinning up a new working environment):

```bash
curl -LSso ~/.tmux.conf https://raw.githubusercontent.com/Integralist/dotfiles/master/.tmux.conf
curl -LSso ~/tmux.sh https://raw.githubusercontent.com/Integralist/dotfiles/master/tmux.sh
```

> [!WARNING]
> check `$PATH` to make sure tmux isn't double setting values in your PATH
> as it starts up. If it does you can check an older version of my
> [`~/.bash_profile`](https://github.com/Integralist/dotfiles/blob/cc906bd14636543e71d9c034d6507f5986a80bbd/.bash_profile#L18-L21)
> for a work-around.

## Miscellaneous

Not every app can be installed via Homebrew.
[Monosnap](https://monosnap.com/welcome) is one such example.

If you want an easy way to hide menu bar items, then try the
[hidden-bar](https://apps.apple.com/app/hidden-bar/id1452453066) app
([github](https://github.com/dwarvesf/hidden))

Also, if you're into torrents, then the [transmission
server/client](https://cli-ck.io/transmission-cli-user-guide/) (or alternatively
`npm install -g t-get`) might be of interest to you.

## macOS

It can be cool to configure the macOS via the terminal, things like mouse cursor
speed or keyboard repeat key performance. But unfortunately that all changed
with macOS Mojave and I couldn't be bothered to figure out the correct way to do
it via the terminal when doing the setup via the GUI is just as quick (and I
know the few things I like to tweak off-by-heart).

For example, you used to be able to do things like:

```bash
defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false
```

But since macOS Mojave those settings and namespaces seem to have changed. If
you're interested in figuring it out, then I'd recommend starting with:

```bash
defaults read
```

The above command will display _all_ the current macOS settings for you. From
there you can drill down into individual namespaces like so:

```bash
defaults read "Apple Global Domain" com.apple.mouse.tapBehavior
```

> [!TIP]
> [here](https://github.com/Integralist/dotfiles/blob/cc906bd14636543e71d9c034d6507f5986a80bbd/bootstrap.sh#L7-L53)
> are the settings I used to configure via the terminal.

One thing I like to do is to make sure macOS' "Spaces" feature doesn't rearrange
spaces based on their recent usage, and to do that you need to open up the
'Mission Control' settings panel and disable the option:

```
Automatically rearrange Spaces based on most recent use
```
