Skip to content

Instantly share code, notes, and snippets.

@telnet2
Last active July 30, 2026 21:42
Show Gist options
  • Select an option

  • Save telnet2/15dda81d00576870584252a92f3a7398 to your computer and use it in GitHub Desktop.

Select an option

Save telnet2/15dda81d00576870584252a92f3a7398 to your computer and use it in GitHub Desktop.
Local macOS bootstrap: Homebrew, Ghostty, fonts, shell utilities, editor plugins, and private dotfile links
#!/bin/zsh
set -eu
setopt PIPE_FAIL
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:${HOME}/.fzf/bin:${PATH:-}"
DOTFILES_REPO="https://github.com/telnet2/dotfiles.git"
DOTFILES_DIR="${HOME}/dotfiles"
BACKUP_ROOT="${HOME}/.local-setup-backups/$(date +%Y%m%d-%H%M%S)"
GHOSTTY_CONFIG="${HOME}/Library/Application Support/com.mitchellh.ghostty/config.ghostty"
if [[ -f "${HOME}/Library/Application Support/com.mitchellh.ghostty/config" ]]; then
GHOSTTY_CONFIG="${HOME}/Library/Application Support/com.mitchellh.ghostty/config"
fi
note() {
print -r -- "==> $*"
}
fail() {
print -ru2 -- "error: $*"
exit 1
}
exists() {
[[ -e "$1" || -L "$1" ]]
}
backup_target() {
local target="$1"
if exists "$target"; then
mkdir -p "$BACKUP_ROOT"
mv "$target" "$BACKUP_ROOT/${target:t}"
note "Backed up $target"
fi
}
link_target() {
local source="$1"
local target="$2"
[[ -e "$source" ]] || fail "Missing link source: $source"
if [[ -L "$target" && "${target:A}" == "${source:A}" ]]; then
return
fi
backup_target "$target"
ln -s "$source" "$target"
note "Linked $target -> $source"
}
check_install() {
local missing=0
local required_path
for required_path in \
"$DOTFILES_DIR" \
"$HOME/.zprezto" \
"$HOME/.fzf" \
"$HOME/.vim/bundle/Vundle.vim" \
"$HOME/.vimrc" \
"$HOME/.zpreztorc" \
"$HOME/.ssh" \
"$HOME/.gitconfig" \
"$HOME/.git-credentials"; do
if exists "$required_path"; then
print -r -- "OK $required_path"
else
print -r -- "MISSING $required_path"
missing=1
fi
done
local utility
for utility in brew gh mosh fzf vim; do
if [[ "$utility" == "fzf" && -x "$HOME/.fzf/bin/fzf" ]]; then
print -r -- "OK fzf -> $HOME/.fzf/bin/fzf"
elif command -v "$utility" >/dev/null 2>&1; then
print -r -- "OK $utility -> $(command -v "$utility")"
else
print -r -- "MISSING $utility"
missing=1
fi
done
if [[ "$(uname -s)" == "Darwin" ]]; then
[[ -f "$GHOSTTY_CONFIG" ]] || {
print -r -- "MISSING $GHOSTTY_CONFIG"
missing=1
}
fi
return "$missing"
}
if [[ "${1:-}" == "--check" ]]; then
check_install
exit $?
fi
[[ "$(uname -s)" == "Darwin" ]] ||
fail "This bootstrap currently supports macOS only."
[[ "$SHELL" == */zsh ]] ||
fail "Set zsh as the default shell first: chsh -s $(command -v zsh)"
if ! command -v brew >/dev/null 2>&1; then
note "Installing Homebrew"
/bin/bash -c \
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
if [[ -x /opt/homebrew/bin/brew ]]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
elif [[ -x /usr/local/bin/brew ]]; then
eval "$(/usr/local/bin/brew shellenv)"
else
fail "Homebrew installation did not produce a brew executable."
fi
fi
note "Installing command-line utilities"
brew install gh mosh
note "Installing Ghostty and terminal fonts"
brew install --cask \
ghostty \
font-d2coding \
font-meslo-lg-nerd-font \
font-jetbrains-mono-nerd-font \
font-fira-code-nerd-font \
font-hack-nerd-font
if ! gh auth status >/dev/null 2>&1; then
fail "Authenticate GitHub first with: gh auth login"
fi
visibility="$(gh repo view telnet2/dotfiles --json visibility --jq .visibility)"
[[ "$visibility" == "PRIVATE" ]] ||
fail "Refusing to clone dotfiles because repository visibility is $visibility."
if [[ -d "$DOTFILES_DIR/.git" ]]; then
if [[ -n "$(git -C "$DOTFILES_DIR" status --porcelain)" ]]; then
note "Dotfiles has local changes; skipping pull"
else
git -C "$DOTFILES_DIR" pull --ff-only
fi
elif exists "$DOTFILES_DIR"; then
fail "$DOTFILES_DIR exists but is not a Git checkout."
else
gh repo clone telnet2/dotfiles "$DOTFILES_DIR"
fi
if [[ ! -d "$HOME/.zprezto" ]]; then
note "Installing Prezto"
git clone --recursive https://github.com/sorin-ionescu/prezto.git "$HOME/.zprezto"
fi
if [[ ! -d "$HOME/.zprezto/modules/fast-syntax-highlighting" ]]; then
git clone https://github.com/zdharma/fast-syntax-highlighting \
"$HOME/.zprezto/modules/fast-syntax-highlighting"
fi
for rcfile in "$HOME"/.zprezto/runcoms/^README.md(.N); do
[[ "${rcfile:t}" == "zpreztorc" ]] && continue
link_target "$rcfile" "$HOME/.${rcfile:t}"
done
backup_target "$HOME/.zpreztorc"
cat > "$HOME/.zpreztorc" <<'EOF'
zstyle ':prezto:*:*' color 'yes'
zstyle ':prezto:load' pmodule \
'fasd' \
'environment' \
'terminal' \
'editor' \
'history' \
'directory' \
'spectrum' \
'utility' \
'autosuggestions' \
'completion' \
'prompt' \
'git' \
'node' \
'fast-syntax-highlighting' \
'history-substring-search'
zstyle ':prezto:module:editor' key-bindings 'emacs'
zstyle ':prezto:module:prompt' theme 'sorinx'
EOF
curl -fsSL \
"https://gist.githubusercontent.com/telnet2/affe231f01922b1b0ec28a2702fff0f9/raw/prompt_sorinx_setup" \
-o "$HOME/.zprezto/modules/prompt/functions/prompt_sorinx_setup"
if [[ ! -d "$HOME/.fzf" ]]; then
note "Installing fzf"
git clone --depth 1 https://github.com/junegunn/fzf.git "$HOME/.fzf"
"$HOME/.fzf/install" --all
fi
python3 - "$HOME/.zshrc" <<'PY'
import pathlib
import sys
path = pathlib.Path(sys.argv[1])
start = "# >>> local-setup dotfiles >>>"
end = "# <<< local-setup dotfiles <<<"
text = path.read_text()
before, marker, rest = text.partition(start)
if marker:
_, closing, after = rest.partition(end)
if not closing:
raise SystemExit("zshrc managed dotfiles block is missing its closing marker")
text = before.rstrip() + "\n" + after.lstrip()
block = """# >>> local-setup dotfiles >>>
source "$HOME/dotfiles/tt/alias.zsh"
source "$HOME/dotfiles/tt/funcs.zsh"
# <<< local-setup dotfiles <<<
"""
path.write_text(text.rstrip() + "\n\n" + block)
PY
if [[ ! -d "$HOME/.vim/bundle/Vundle.vim" ]]; then
git clone https://github.com/VundleVim/Vundle.vim.git \
"$HOME/.vim/bundle/Vundle.vim"
fi
if [[ ! -f "$HOME/.vimrc" ]]; then
curl -fsSL \
"https://gist.githubusercontent.com/telnet2/048a582b5a3354ad70cab9c6f787390c/raw/870f6742fa15bf6c30a826dcfb863821db8e1aa9/.vimrc" \
-o "$HOME/.vimrc"
fi
note "Installing Vim plugins"
if ! vim -E -s -u "$HOME/.vimrc" +PluginInstall +qall; then
vim +PluginInstall +qall </dev/null >/dev/null 2>&1
fi
mkdir -p "${GHOSTTY_CONFIG:h}"
python3 - "$GHOSTTY_CONFIG" <<'PY'
import pathlib
import sys
path = pathlib.Path(sys.argv[1])
start = "# >>> local-setup fonts >>>"
end = "# <<< local-setup fonts <<<"
text = path.read_text() if path.exists() else ""
before, marker, rest = text.partition(start)
if marker:
_, closing, after = rest.partition(end)
if not closing:
raise SystemExit("Ghostty managed font block is missing its closing marker")
text = before.rstrip() + "\n" + after.lstrip()
block = """# >>> local-setup fonts >>>
font-family = ""
font-family = D2Coding
font-family = JetBrainsMono Nerd Font Mono
font-family = MesloLGM Nerd Font Mono
# <<< local-setup fonts <<<
"""
path.write_text(text.rstrip() + "\n\n" + block if text.strip() else block)
PY
chmod 700 "$DOTFILES_DIR/tt/tt.ssh"
find "$DOTFILES_DIR/tt/tt.ssh" -type f -exec chmod 600 {} +
chmod 600 \
"$DOTFILES_DIR/tt/tt.gitconfig" \
"$DOTFILES_DIR/tt/tt.git-credentials"
link_target "$DOTFILES_DIR/tt/tt.ssh" "$HOME/.ssh"
link_target "$DOTFILES_DIR/tt/tt.gitconfig" "$HOME/.gitconfig"
link_target "$DOTFILES_DIR/tt/tt.git-credentials" "$HOME/.git-credentials"
note "Verifying installation"
check_install
note "Setup complete. Fully quit and reopen Ghostty, or run: exec zsh"
name local-setup
description Bootstrap or refresh Joohwi's macOS development environment with Homebrew, Ghostty, terminal fonts, GitHub CLI, mosh, Prezto, fzf, Vundle, the sorinx prompt, Vim plugins, and private dotfile symlinks. Use on a new Mac, after migrating machines, or when asked to reproduce or repair the local shell and terminal setup.

Local setup

Set up the machine with the bundled script, preserving existing configuration through timestamped backups.

Safety

  • Never print or commit credential contents, SSH private keys, or tokens.
  • Treat ~/dotfiles as private. Stop if gh repo view telnet2/dotfiles reports public visibility.
  • Never request a password in chat. Let Homebrew, sudo, or chsh prompt in the user's terminal.
  • Before replacing a real file or directory with a symlink, back it up.
  • Do not push dotfile changes unless the user explicitly requests it.

Install

Download the bootstrap script from this gist and inspect it before execution:

curl -fsSL \
  "https://gist.githubusercontent.com/telnet2/15dda81d00576870584252a92f3a7398/raw/bootstrap.zsh" \
  -o /tmp/local-setup.zsh
zsh -n /tmp/local-setup.zsh
sed -n '1,260p' /tmp/local-setup.zsh

Run it from an interactive terminal so administrator and installer prompts are visible:

zsh /tmp/local-setup.zsh

The script is idempotent. Existing non-symlink configuration is moved into ~/.local-setup-backups/<timestamp>/ before links are created.

What the script configures

  • Homebrew on Apple Silicon or Intel macOS.
  • gh and mosh.
  • Ghostty.
  • D2Coding plus Meslo LG, JetBrains Mono, Fira Code, and Hack Nerd Fonts.
  • Ghostty's font fallback chain: D2Coding, JetBrains Mono Nerd Font, Meslo LG Nerd Font.
  • Prezto with fast-syntax-highlighting.
  • fzf completion and key bindings.
  • Vundle and the configured Vim plugins.
  • The sorinx Prezto prompt.
  • ~/dotfiles cloned from the private telnet2/dotfiles repository.
  • Permission-safe links:
    • ~/.ssh~/dotfiles/tt/tt.ssh
    • ~/.gitconfig~/dotfiles/tt/tt.gitconfig
    • ~/.git-credentials~/dotfiles/tt/tt.git-credentials

Verify

Run the non-mutating check:

zsh /tmp/local-setup.zsh --check

Then verify a real login shell and key utilities:

zsh -lic 'command -v gh mosh fzf vim; echo shell-ok'
gh auth status
ssh -G devbox | grep -E '^(hostname|user|port) '

On macOS, fully quit Ghostty with Cmd+Q and reopen it after changing fonts; font-family changes do not apply through live configuration reload.

Known constraints

  • Homebrew installation may require an administrator account.
  • GitHub authentication may require gh auth login.
  • The private dotfiles repository historically contains live credentials and SSH keys. Keep it private and rotate any credential exposed elsewhere.
  • Do not run Linux-only locale-gen or localedef on macOS.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment