This commit is contained in:
Ben Kyd
2026-03-26 20:29:35 +00:00
parent 2241b6fc7d
commit 585a5298ad
11 changed files with 3682 additions and 46 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "themes/catppuccin-iterm"]
path = themes/catppuccin-iterm
url = https://github.com/catppuccin/iterm

View File

@@ -1,36 +1,61 @@
# Dotfiles # dotfiles
Filesystem overlay. Structure mirrors actual paths on disk. Centralized database of how my machines should be set up. All Macs are identical to each other, all Arch machines are identical to each other. OS is the only axis of variation.
``` ```
dotfiles/ dotfiles/
home/benk/ -> symlinked into $HOME home/benk/ mirrors $HOME — files here get synced to the machine
.config/nvim/ etc/ mirrors /etc — applied with sudo on Linux
.config/fish/ packages/
.config/wezterm/ brew.txt Homebrew formulae (all Macs)
.tmux.conf brew-casks.txt Homebrew casks (all Macs)
.vimrc arch.txt yay packages (all Arch machines)
... themes/
etc/ -> copied into /etc (prompted, needs sudo) catppuccin-iterm/ submodule: catppuccin/iterm
fstab setup.toml declarative tooling config (fish + tmux plugins)
nginx/nginx.conf bootstrap.py the manager — run this
packages/ -> package lists for brew/yay
bootstrap.sh -> does everything
``` ```
## Usage ## setup
``` ```bash
git clone git@github.com:benkyd/dotfiles.git ~/dotfiles git clone --recurse-submodules git@github.com:benkyd/dotfiles.git ~/dotfiles
cd ~/dotfiles cd ~/dotfiles
./bootstrap.sh python3 bootstrap.py
``` ```
The bootstrap will: Dependencies (`rich`, `questionary`) are auto-installed on first run.
1. Detect OS (macOS / Arch)
2. Optionally install packages (Homebrew or yay)
3. Install oh-my-fish, fisher, tmux plugin manager
4. Symlink `home/benk/` into `$HOME`
5. Optionally copy `etc/` to `/etc` (prompted)
Existing files are backed up to `~/dotfiles.bak/`. ## usage
Running `python3 bootstrap.py` drops you into a TUI. Pick a mode:
- **Set up this machine** — install packages, shell tooling, sync dotfiles. use this on a fresh machine.
- **Apply dotfiles** — push repo → `$HOME`. skips packages.
- **Save changes** — pull edits you made on this machine back into the repo.
- **Check status** — see what's drifted. nothing is changed.
Or pass a mode directly to skip the TUI:
```bash
python3 bootstrap.py install
python3 bootstrap.py copy
python3 bootstrap.py pull
python3 bootstrap.py status
python3 bootstrap.py add ~/.config/something.conf # start tracking a new file
```
## per-file variation
If you've edited something on a work machine and don't want to clobber it or pull it back, the sync steps show a per-file checkbox for any file that's newer on the machine than in the repo. just uncheck what you want to skip.
## OS-specific paths
Some paths only make sense on one OS and are excluded automatically:
- `Library/` — macOS only (iTerm2 prefs, etc.)
- `.config/wezterm/` — Linux only (iTerm2 is used on Mac)
## themes
Catppuccin Mocha is the colour scheme. The iTerm2 colour preset lives at `themes/catppuccin-iterm/colors/catppuccin-mocha.itermcolors`. Import it via iTerm2 → Profiles → Colors → Color Presets → Import.

1068
bootstrap.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,25 @@ DOTFILES_DIR="$(cd "$(dirname "$0")" && pwd)"
BACKUP_DIR="$HOME/dotfiles.bak" BACKUP_DIR="$HOME/dotfiles.bak"
USER="$(whoami)" USER="$(whoami)"
# --- Detect OS early (needed by sync functions) ---
OS="unknown"
if [[ "$(uname)" == "Darwin" ]]; then
OS="macos"
elif [[ -f /etc/arch-release ]]; then
OS="arch"
elif [[ -f /etc/os-release ]]; then
OS="linux"
fi
# Files/dirs in the home overlay that are macOS-only
MACOS_ONLY=(Library/)
HOME_EXCLUDES=()
if [[ "$OS" != "macos" ]]; then
for item in "${MACOS_ONLY[@]}"; do
HOME_EXCLUDES+=(--exclude "$item")
done
fi
usage() { usage() {
echo "Usage: ./bootstrap.sh [--pull | --copy]" echo "Usage: ./bootstrap.sh [--pull | --copy]"
echo "" echo ""
@@ -14,6 +33,34 @@ usage() {
exit 0 exit 0
} }
# --- rsync wrapper: prompts for confirmation if existing files would be overwritten ---
# Usage: rsync_confirm [--sudo] <rsync args...>
rsync_confirm() {
local rsync_cmd=(rsync)
if [[ "${1:-}" == "--sudo" ]]; then
rsync_cmd=(sudo rsync)
shift
fi
local overwrites
overwrites=$("${rsync_cmd[@]}" --dry-run --itemize-changes "$@" 2>/dev/null \
| awk '/^>f/ && !/\+{9}/ { print $2 }')
if [ -n "$overwrites" ]; then
echo "The following existing files would be overwritten:"
echo "$overwrites" | sed 's/^/ /'
echo ""
read -p "Proceed with overwrite? [y/N] " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo " Aborted."
return 1
fi
fi
"${rsync_cmd[@]}" "$@"
}
# --- Pull mode: copy live files back into the repo --- # --- Pull mode: copy live files back into the repo ---
pull_back() { pull_back() {
OVERLAY_HOME="$DOTFILES_DIR/home/$USER" OVERLAY_HOME="$DOTFILES_DIR/home/$USER"
@@ -22,13 +69,13 @@ pull_back() {
fi fi
echo "==> Pulling home files back into repo..." echo "==> Pulling home files back into repo..."
rsync -av --itemize-changes --existing --update \ rsync_confirm -av --itemize-changes --existing --update \
"$HOME/" "$OVERLAY_HOME/" "${HOME_EXCLUDES[@]}" "$HOME/" "$OVERLAY_HOME/"
echo "" echo ""
if [ -d "$DOTFILES_DIR/etc" ]; then if [ -d "$DOTFILES_DIR/etc" ]; then
echo "==> Pulling /etc files back into repo..." echo "==> Pulling /etc files back into repo..."
sudo rsync -av --itemize-changes --existing --update \ rsync_confirm --sudo -av --itemize-changes --existing --update \
/etc/ "$DOTFILES_DIR/etc/" /etc/ "$DOTFILES_DIR/etc/"
echo "" echo ""
fi fi
@@ -56,8 +103,8 @@ copy_home() {
echo "==> Syncing $OVERLAY_HOME -> $HOME" echo "==> Syncing $OVERLAY_HOME -> $HOME"
mkdir -p "$BACKUP_DIR/home" mkdir -p "$BACKUP_DIR/home"
rsync -av --itemize-changes --backup --backup-dir="$BACKUP_DIR/home" \ rsync_confirm -av --itemize-changes --backup --backup-dir="$BACKUP_DIR/home" \
"$OVERLAY_HOME/" "$HOME/" "${HOME_EXCLUDES[@]}" "$OVERLAY_HOME/" "$HOME/"
echo "" echo ""
echo "Done. Backups at ~/dotfiles.bak/" echo "Done. Backups at ~/dotfiles.bak/"
exit 0 exit 0
@@ -77,15 +124,6 @@ echo "Repo: $DOTFILES_DIR"
echo "User: $USER" echo "User: $USER"
echo "" echo ""
# --- Detect OS ---
OS="unknown"
if [[ "$(uname)" == "Darwin" ]]; then
OS="macos"
elif [[ -f /etc/arch-release ]]; then
OS="arch"
elif [[ -f /etc/os-release ]]; then
OS="linux"
fi
echo "Detected OS: $OS" echo "Detected OS: $OS"
echo "" echo ""
@@ -205,8 +243,8 @@ chmod 700 "$HOME/.ssh"
echo "==> Syncing $OVERLAY_HOME -> $HOME" echo "==> Syncing $OVERLAY_HOME -> $HOME"
mkdir -p "$BACKUP_DIR/home" mkdir -p "$BACKUP_DIR/home"
rsync -av --itemize-changes --backup --backup-dir="$BACKUP_DIR/home" \ rsync_confirm -av --itemize-changes --backup --backup-dir="$BACKUP_DIR/home" \
"$OVERLAY_HOME/" "$HOME/" "${HOME_EXCLUDES[@]}" "$OVERLAY_HOME/" "$HOME/"
echo "" echo ""
# --- Overlay: etc/ -> /etc --- # --- Overlay: etc/ -> /etc ---
@@ -224,7 +262,7 @@ if [ -d "$DOTFILES_DIR/etc" ]; then
if [[ $REPLY =~ ^[Yy]$ ]]; then if [[ $REPLY =~ ^[Yy]$ ]]; then
mkdir -p "$BACKUP_DIR/etc" mkdir -p "$BACKUP_DIR/etc"
sudo rsync -av --itemize-changes --backup --backup-dir="$BACKUP_DIR/etc" \ rsync_confirm --sudo -av --itemize-changes --backup --backup-dir="$BACKUP_DIR/etc" \
"$DOTFILES_DIR/etc/" /etc/ "$DOTFILES_DIR/etc/" /etc/
echo " Originals backed up to ~/dotfiles.bak/etc/" echo " Originals backed up to ~/dotfiles.bak/etc/"
else else

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ fish
tmux tmux
neovim neovim
wezterm wezterm
fastfetch
btop btop
ripgrep ripgrep
fd fd
@@ -18,3 +19,5 @@ nodejs
python python
nginx nginx
base-devel base-devel
obsidian
syncthing

4
packages/brew-casks.txt Normal file
View File

@@ -0,0 +1,4 @@
# casks — installed via `brew install --cask`
iterm2
obsidian
thunderbird

View File

@@ -1,7 +1,7 @@
# formulae — installed via `brew install`
fish fish
tmux tmux
nvim neovim
wezterm
btop btop
ripgrep ripgrep
fd fd
@@ -9,11 +9,12 @@ git
curl curl
wget wget
rsync rsync
fastfetch
lua-language-server lua-language-server
typescript-language-server typescript-language-server
pyright pyright
bash-language-server bash-language-server
cmake cmake
node node
python3 python@3.14
xclip syncthing

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
rich>=13.0.0
questionary>=2.0.0

12
setup.toml Normal file
View File

@@ -0,0 +1,12 @@
# setup.toml — declarative tooling config
# Packages live in packages/brew.txt and packages/arch.txt.
# This file covers everything that isn't a package manager install.
[fish]
# Fisher plugins to install. Run `fisher list` to see what's currently installed.
plugins = []
[tmux]
# TPM plugins to install headlessly via ~/.tmux/plugins/tpm/bin/install_plugins.
# Format: "owner/repo" matching the TPM convention.
plugins = []