# Switching Machines
The actual flow I run when I get a new laptop or spin up a new
server. Written out so future-me doesn't have to remember.
The rules that constrain what's in this repo are in
[docs/convention.md](/src/dotfiles/docs-convention-md/).
## Assumptions
- I've already booted the new machine and gotten to a shell.
- I have internet.
- I have access to my password manager (1Password) via their
browser extension or CLI for secrets.
- I'm on macOS or Debian/Ubuntu. Other distros work too but I
don't write the commands down.
## Phase 1: bootstrap (10 minutes)
The minimum I need before dotfiles are useful.
### macOS
# Xcode CLI tools - needed for git
xcode-select --install
# Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# the handful of tools I cannot live without for the next hour
brew install git stow zsh tmux neovim fzf starship ripgrep fd
### Debian / Ubuntu
sudo apt update
sudo apt install -y git stow zsh tmux neovim fzf ripgrep fd-find curl
# starship
curl -sS https://starship.rs/install.sh | sh
### Both
- Change shell to zsh: `chsh -s $(which zsh)` and re-log in.
- Generate a new SSH key if the box doesn't have one:
`ssh-keygen -t ed25519 -C "$(hostname)-$(date +%Y)"`. Upload
the public key to my account(s). Add the private key path to
the 1Password SSH agent so signing works right away.
## Phase 2: clone dotfiles (2 minutes)
mkdir -p ~/src
git clone https://mercemay.top/src/dotfiles.git ~/.dotfiles
cd ~/.dotfiles
./install.sh
[`install.sh`](/src/dotfiles/install-sh/) runs `stow` for each
package (`zsh`, `tmux`, `git`, `nvim`) and refuses to overwrite
existing non-symlink files without asking. Commit `4df2a88` added
the prompt behaviour.
On a brand new machine, nothing conflicts and the script completes
silently. On a machine where the home dir already has hand-rolled
config, I answer the prompts interactively.
## Phase 3: machine-specific bits (5 minutes)
Three files I create by hand, not from the repo:
~/.zshrc.local
~/.gitconfig.local
~/.config/nvim/lua/local.lua
`~/.gitconfig.local` is the most important:
[user]
name = Your Name
email = you@example.com
signingkey = ssh-ed25519 AAA...
[commit]
gpgsign = true
Without this, commits go in anonymous.
`~/.zshrc.local` gets any PATH entries for this machine. A work
machine might have a bunch of paths for corp tooling; a server
might have `GOPATH` tweaks. Personal laptops usually have none.
`~/.config/nvim/lua/local.lua` exists only if I'm doing something
experimental with neovim on this machine.
## Phase 4: secrets (5 minutes)
1Password CLI takes care of the rest.
# install
brew install --cask 1password 1password-cli # macOS
# on Linux, download from 1password.com
# sign in
eval $(op signin)
What I pull from 1Password:
- SSH keys (loaded by the agent, not the filesystem). `ssh-add -L`
should list my keys after signing in.
- API tokens for the handful of CLIs I use (AWS, GH, Cloudflare).
These go in my shell's environment via an alias that runs
`op run` with a template file.
Nothing from this repo references secrets directly. If you see a
token in a dotfile, that's a bug and I want to know.
## Phase 5: editor (10 minutes)
On first `nvim` launch, [lazy.nvim](/src/dotfiles/nvim-plugins-lua/)
bootstraps itself and installs all my plugins. Takes a minute or
two depending on network.
After that:
- Open any file. Language servers install themselves on demand
via `mason.nvim`. First time I open a Go file, `gopls` is
installed; first time I open Rust, `rust-analyzer`; etc.
- Run `:checkhealth` to see if anything is missing.
- `:TSUpdate` to grab tree-sitter parsers for the languages I use.
If something looks weird, I rerun `:Lazy sync` and usually that's
it.
## Phase 6: tmux (2 minutes)
tmux
`.tmux.conf` is loaded automatically. First run compiles the
`tmux-thumbs` Rust helper if present (macOS only; I don't bother
on servers). Plugin manager is TPM, installed by the stow
package:
# install tpm if not present
[[ -d ~/.tmux/plugins/tpm ]] || \
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
# inside tmux: C-Space I to install plugins
# (C-Space is my prefix; see convention.md)
## Phase 7: the Brewfile / apt list (varies)
Parallel to this repo I maintain a `Brewfile` (macOS) and a small
apt list (Linux). Running them installs every program I expect to
have, in one shot. They live in the same password manager vault
as the rest of my stuff because I don't share the list publicly.
# macOS
brew bundle --file=~/1p/Brewfile
# Linux
xargs -a ~/1p/apt-packages sudo apt install -y
This is the thing that takes longest (downloading) but requires
zero attention.
## Phase 8: sanity checks
Before declaring the machine "set up":
- `git commit --allow-empty -m "test"` in a test repo. Check the
commit is signed and attributed correctly.
- `tmux` and cycle through a couple of windows. Copy text with
`y`; paste elsewhere. Copy-mode should feel right.
- Open neovim, edit some code, save, run tests. Language servers
respond. Linter doesn't shout about something installable.
- `rg` and `fd` work. (`fd` on Debian is `fdfind`; I alias it in
`.aliases.zsh` to `fd`.)
If any of those fail, the thing to fix is usually in
`~/.zshrc.local` or a missing binary.
## Time budget
For a fresh laptop:
- Bootstrap: 10 min
- Clone and install: 2 min
- Local config: 5 min
- Secrets: 5 min
- Neovim first-run plugins: 10 min
- Package install (Brewfile): 30 min (background)
- Sanity checks: 5 min
So 35 minutes of active work, plus an hour of package downloads I
don't need to sit with. I have done this in under an hour on
several occasions.
## Per-environment variants
### Work laptop
- Add the work email and signing key in `~/.gitconfig.local`.
- Install VPN tooling per IT's instructions (not in this repo).
- Add `${workspace}/bin` to PATH in `~/.zshrc.local`.
### Personal laptop
- As the standard flow.
### Server (Debian)
- Skip neovim plugin install unless I'll be editing code there.
Most servers get only `zsh/`, `tmux/`, `git/` packages.
- Don't install Brewfile obviously.
- `install.sh --packages=zsh,tmux,git` skips nvim stow.
## What I check in after a fresh setup
Usually nothing. If I've had to change something to make things
work, it's either (a) a real improvement, which gets committed, or
(b) machine-specific, which goes in the `.local` files and stays
out of git.
If I find myself copying the same "machine-specific" change to
three machines, it's not machine-specific and I promote it to a
tracked config.
## Rollback
If something from a new config commit breaks my setup:
cd ~/.dotfiles
git log --oneline -20
git revert <hash>
./install.sh
Since `install.sh` is idempotent, re-running it after a revert
just re-stows the reverted files. No manual cleanup.