Skip to content

Repository files navigation

dotfiles

Personal Linux and macOS config deployed with GNU Stow.

To set proxy:

export MY_PROXY_PORT=10808
export http_proxy="http://127.0.0.1:$MY_PROXY_PORT"
export https_proxy="http://127.0.0.1:$MY_PROXY_PORT"
export ftp_proxy="http://127.0.0.1:$MY_PROXY_PORT"
export no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com"
git config --global http.proxy "http://127.0.0.1:$MY_PROXY_PORT"
git config --global https.proxy "http://127.0.0.1:$MY_PROXY_PORT"

To unset proxy:

unset http_proxy https_proxy ftp_proxy no_proxy MY_PROXY_PORT
git config --global --unset http.proxy
git config --global --unset https.proxy

Contents

Quick setup

scripts/setup.sh installs selected applications and deploys the complete platform profile. It has three explicit commands: plan is read-only, apply is deterministic and scriptable, and interactive detects installed components before opening a numbered menu. The one-line setup uses the interactive command:

Detected components are listed afterward, marked [installed], and default to off; select one explicitly to rerun its installer. Detection is local and best-effort. Source components are detected from their Git checkouts, while command and application components are detected from their executable or app. Explicit plan and apply runs do not filter their selections through this interactive detection.

curl -fsSL https://gist.githubusercontent.com/Jamie-Cui/3c22bc591b4d372813b6ce26086e1c63/raw/setup-gist.sh | \
    bash -s -- interactive

The interactive interface groups missing and installed components, shows a four-step review flow, and uses color only when attached to a capable terminal. Set NO_COLOR to disable color, or SETUP_NO_CLEAR=1 to keep previous menu screens in the terminal scrollback.

For an unattended, reproducible run, use apply with a profile and explicitly accept the confirmation:

curl -fsSL https://gist.githubusercontent.com/Jamie-Cui/3c22bc591b4d372813b6ce26086e1c63/raw/setup-gist.sh | \
    bash -s -- apply --profile recommended --yes

The profiles are minimal (prerequisites plus dotfiles), recommended (common shell, font, editor, terminal, and Rime tools; plus AeroSpace on macOS), and all. Individual components can be adjusted with repeatable --with and --without arguments; each occurrence accepts one component:

# Inspect without sudo, downloads, clone, generated files, or logs.
curl -fsSL https://gist.githubusercontent.com/Jamie-Cui/3c22bc591b4d372813b6ce26086e1c63/raw/setup-gist.sh | \
    bash -s -- plan --profile minimal --with emacs

# Prepare Emacs through configure; add --build-emacs to compile and install it.
curl -fsSL https://gist.githubusercontent.com/Jamie-Cui/3c22bc591b4d372813b6ce26086e1c63/raw/setup-gist.sh | \
    bash -s -- apply --profile minimal --with emacs --yes

Components are shell, font, vim, nvim, emacs, ctags, org-root, git-overleaf, tdlib, kitty, flameshot, rime, x11, i3, rofi, hyprland, waybar, dunst, mail, secrets, aerospace, and borders. Dependencies are added automatically; explicitly excluding a required dependency is an error. The clone-only ctags, org-root, git-overleaf, and tdlib components are selected only with --with or the all profile. Software selection does not filter Stow packages: the complete macOS or Linux dotfiles profile is always previewed and deployed.

The script inherits exported proxy variables without printing their values. Export them before the initial curl so that the loader, Git, package managers, and the proxy-only sudo environment see the same settings:

export http_proxy=http://127.0.0.1:10808
export https_proxy=$http_proxy
curl -fsSL https://gist.githubusercontent.com/Jamie-Cui/3c22bc591b4d372813b6ce26086e1c63/raw/setup-gist.sh | \
    bash -s -- interactive

Existing configuration is never adopted or cleaned. A dirty/diverged clone, component failure, or Stow conflict stops immediately. Rerun setup after fixing the reported cause; installation actions are designed to be idempotent. Expected GUI, authentication, Rime, and logout steps are reported separately as pending work. Full logs are written below ~/.local/state/dotfiles/ after the install is confirmed.

The Gist loader downloads one repository archive so scripts/setup.sh and its modules always arrive together. This repository remains the source of truth. To inspect the loader before running it:

tmp=$(mktemp)
curl -fsSL https://gist.githubusercontent.com/Jamie-Cui/3c22bc591b4d372813b6ce26086e1c63/raw/setup-gist.sh -o "$tmp"
less "$tmp"
bash "$tmp" interactive

Prerequisites

macOS

xcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install stow

Fedora

sudo dnf install curl git make stow

Deploy

mkdir -p ~/opt
git clone https://github.com/Jamie-Cui/dotfiles.git ~/opt/dotfiles
cd ~/opt/dotfiles
make dry-run
make bootstrap

Encrypted secrets

Only GPG ciphertext is managed by Git. The private key, its revocation certificate, and plaintext credentials must remain outside this repository. Install GnuPG with brew install gnupg on macOS or sudo dnf install gnupg2 on Fedora, then create the ciphertext yourself (replace the placeholder with the fingerprint of an encryption-capable public key):

gpg --output packages/secrets/.authinfo.gpg \
    --encrypt --recipient YOUR_ENCRYPTION_KEY_FINGERPRINT \
    ~/.authinfo

The secrets Stow package is selected automatically after the ciphertext exists. Preview and deploy it, verify that your private key can decrypt it, then securely remove or archive the old plaintext file:

make dry-run
make restow
gpg --decrypt ~/.authinfo.gpg >/dev/null

Emacs reads only ~/.authinfo.gpg through auth-source. On Linux, mbsync decrypts that same file through gpg-agent. Import the private key from a separate backup before deploying on a new machine.

Import the GPG key on a new machine

The backup must be an armored OpenPGP secret key created by gpg, not an SSH key or SSH fingerprint. Retrieve it from Bitwarden or another backup location outside this repository, then import it into the local GnuPG keyring:

chmod 600 ~/Downloads/dotfiles-secrets-private.asc
gpg --import ~/Downloads/dotfiles-secrets-private.asc
gpg --list-secret-keys --with-subkey-fingerprint

Confirm that the expected primary fingerprint is present and that it has an encryption subkey marked [E]. Test decryption before deploying the profile:

cd ~/opt/dotfiles
gpg --decrypt packages/secrets/.authinfo.gpg >/dev/null
make dry-run
make bootstrap

After the import and decryption test succeed, securely remove the downloaded private-key export. GnuPG keeps the imported key in its own home directory (normally ~/.gnupg); never copy that directory or the exported private key into this repository.

Cross-platform apps

Run make bootstrap once after installing the apps below.

macOS

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

Fedora

sudo dnf install zsh curl util-linux
chsh -s "$(command -v zsh)"
RUNZSH=no CHSH=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

Log out and back in after changing the login shell.

macOS

brew install --cask font-maple-mono-nl-nf-cn

Fedora

sudo dnf install curl unzip fontconfig
mkdir -p ~/.local/share/fonts/maple-mono
curl -fL https://github.com/subframe7536/maple-font/releases/latest/download/MapleMonoNL-NF-CN-unhinted.zip -o /tmp/MapleMonoNL-NF-CN-unhinted.zip
unzip -o /tmp/MapleMonoNL-NF-CN-unhinted.zip -d ~/.local/share/fonts/maple-mono
fc-cache -fv

Vim (vim)

brew install vim                 # macOS
sudo dnf install vim-enhanced    # Fedora

Neovim (nvim)

brew install neovim              # macOS
sudo dnf install neovim          # Fedora

Emacs (emacs)

The setup script’s emacs component installs the dependencies, shallow-clones the emacs-31 source with depth 1 or safely fast-forwards an existing checkout, and runs autogen.sh and configure. Compilation and installation remain manual unless --build-emacs is supplied; that explicit mode uses at most four jobs by default and requires 10 GiB of available space.

git clone --branch emacs-31 --single-branch --depth 1 https://github.com/emacs-mirror/emacs.git ~/opt/emacs-src

macOS

brew install autoconf automake texinfo libgccjit gcc gnutls tree-sitter@0.25 pkg-config coreutils sqlite librsvg

cd ~/opt/emacs-src
export PATH="$(brew --prefix texinfo)/bin:$PATH"
export PKG_CONFIG_PATH="$(brew --prefix tree-sitter@0.25)/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
./autogen.sh
./configure --with-ns --with-native-compilation --with-tree-sitter --with-sqlite3 --with-rsvg --without-pop --without-mailutils
make -j"$(sysctl -n hw.logicalcpu)"
make install
cp -R nextstep/Emacs.app /Applications/
sudo mkdir -p /usr/local/bin
sudo ln -sf /Applications/Emacs.app/Contents/MacOS/Emacs /usr/local/bin/emacs
sudo ln -sf /Applications/Emacs.app/Contents/MacOS/bin/emacsclient /usr/local/bin/emacsclient

Fedora

sudo dnf install dnf-plugins-core gcc gcc-c++ make autoconf automake texinfo
sudo dnf builddep emacs
sudo dnf install libgccjit-devel tree-sitter-devel sqlite-devel librsvg2-devel gtk3-devel

cd ~/opt/emacs-src
./autogen.sh
./configure --with-pgtk --with-native-compilation --with-tree-sitter --with-sqlite3 --with-rsvg --without-pop --without-mailutils
make -j"$(nproc)"
sudo make install

Configure

emacs --version

Edit the managed ~/.emacs.d/init.el and ~/.emacs.d/early-init.el sources for Emacs settings.

The ctags component shallow-clones the source with depth 1 into ~/opt/ctags. It does not configure, build, or install Universal Ctags.

scripts/setup.sh apply --profile minimal --with ctags --yes

org-root (org-root)

The org-root component clones the private repository with its full history over SSH into ~/opt/org-root, which is the default path used by the managed Emacs configuration. GitHub SSH access must already be configured.

scripts/setup.sh apply --profile minimal --with org-root --yes

git-overleaf (git-overleaf)

The git-overleaf component clones the repository with its full history over SSH into ~/opt/git-overleaf. GitHub SSH access must already be configured.

scripts/setup.sh apply --profile minimal --with git-overleaf --yes

TDLib (tdlib)

The tdlib component shallow-clones the source with depth 1 into ~/opt/tdlib. It does not run CMake, build, or install TDLib.

scripts/setup.sh apply --profile minimal --with tdlib --yes

For all four clone-only components, an existing Git checkout is left exactly as-is; setup does not fetch, pull, or validate its origin. An existing non-Git path is reported as a component failure.

Kitty (kitty)

brew install --cask kitty        # macOS
sudo dnf install kitty           # Fedora

Flameshot (flameshot)

macOS

Download the latest .dmg from Flameshot Releases, copy Flameshot to /Applications, open it once, then enable Screen & System Audio Recording permission. If macOS blocks the first launch:

xattr -rd com.apple.quarantine /Applications/flameshot.app

Fedora

sudo dnf install flameshot grim

The tracked configuration is deployed only by the Linux profile. It leaves the save directory to Flameshot instead of pinning a machine-specific path.

Rime (rime)

macOS

brew install --cask squirrel-app

Add Squirrel in System Settings → Keyboard → Input Sources. After make bootstrap, choose Squirrel → Redeploy from the input-method menu; log out and back in if Squirrel does not work in existing applications.

Fedora

sudo dnf install fcitx5 fcitx5-rime imsettings im-chooser
im-chooser

Select Fcitx 5 in im-chooser, then log out and back in.

proxyctl (bin)

proxyctl init
source ~/.zshrc

Codex skills (skills)

No setup beyond make bootstrap.

GNU/Linux (Fedora) apps

Run make bootstrap once after installing the apps needed by the chosen X11 or Wayland session.

X11 (x11)

sudo dnf install xorg-x11-server-Xorg xorg-x11-xinit xorg-x11-xauth

i3 (i3)

sudo dnf install i3 i3lock picom feh xclip dex-autostart network-manager-applet pulseaudio-utils ibus jq xss-lock setxkbmap

i3blocks (i3blocks)

sudo dnf install i3blocks alsa-utils sysstat perl

Rofi (rofi)

sudo dnf install rofi firefox

Hyprland (hypr)

sudo dnf install hyprland hyprlock hyprpaper xdg-desktop-portal-hyprland brightnessctl playerctl wireplumber xclip

The main configuration uses Hyprland’s native Lua API and requires Hyprland 0.55 or newer. Hyprlock and Hyprpaper continue to use their own Hyprlang configuration files.

Waybar (waybar)

sudo dnf install waybar jq playerctl blueman network-manager-applet NetworkManager-tui pavucontrol htop baobab

Dunst (dunst)

sudo dnf install dunst

Fcitx5 (fcitx5)

sudo dnf install fcitx5 fcitx5-rime fcitx5-gtk fcitx5-qt

GTK 2 (gtk-2.0)

sudo dnf install gtk2

GTK 3 (gtk-3.0)

sudo dnf install gtk3

GTK 4 (gtk-4.0)

sudo dnf install gtk4

imsettings (imsettings)

sudo dnf install imsettings

DavMail (davmail)

sudo dnf copr enable mguessan/davmail
sudo dnf install davmail java-latest-openjdk-headless
mkdir -p ~/.local/state/davmail
systemctl --user enable --now davmail.service
journalctl --user -fu davmail.service

Keep the log open during the first mbsync run and complete the Microsoft device-code prompt it prints.

isync (isync)

sudo dnf install isync maildir-utils gnupg2
openssl rand -hex 32
umask 077
touch ~/.authinfo
$EDITOR ~/.authinfo
# Encrypt it using the Encrypted secrets instructions, then deploy it.
mbsync outlook
mu index

Use the generated value as the same LOCAL_KEY in both entries; it is not the Microsoft password:

machine 127.0.0.1 login you@outlook.com password LOCAL_KEY port 1143
machine 127.0.0.1 login you@outlook.com password LOCAL_KEY port 1025

MacOS apps

Run make bootstrap after installing the app.

AeroSpace fork (aerospace)

# The setup script installs build dependencies and clones the fork here.
cd ~/opt/aerospace-src

# Create the self-signed code-signing certificate described in
# dev-docs/development.md before building.  Homebrew's Bash must take
# precedence over the older macOS system version.
export PATH="$(brew --prefix)/bin:$PATH"
./build-release.sh --minimal

# Remove a previous official cask once, then install the locally built app and CLI.
brew uninstall --cask aerospace 2>/dev/null || true
sudo ditto .release/AeroSpace.app /Applications/AeroSpace.app
sudo mkdir -p /usr/local/bin
sudo install -m 0755 .release/aerospace /usr/local/bin/aerospace
open /Applications/AeroSpace.app

# Mission Control and Dock settings required by this layout.
defaults write com.apple.dock expose-group-apps -bool true
defaults write com.apple.dock autohide -bool true
defaults write com.apple.dock pinning -string start
killall Dock

JankyBorders is optional. The AeroSpace configuration starts it only when the borders executable exists:

brew tap FelixKratz/formulae
brew install borders

The setup script clones or safely fast-forwards Jamie-Cui/AeroSpace in ~/opt/aerospace-src and installs Bash 5, the only Homebrew dependency needed by the minimal release build. It deliberately leaves the signed release build and installation manual because the signing certificate is machine-local. It does not install borders or change Dock/Mission Control defaults, and reports the commands above as pending work when the current values differ.

If macOS blocks the first launch:

xattr -d com.apple.quarantine /Applications/AeroSpace.app

The tracked bindings use ${HOME} for user-specific paths.

About

Personal Linux and macOS config deployed with GNU Stow.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages