docs/switching-machines.md

# 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.