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.proxyscripts/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 -- interactiveThe 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 --yesThe 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 --yesComponents 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 -- interactiveExisting 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" interactivexcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install stowsudo dnf install curl git make stowmkdir -p ~/opt
git clone https://github.com/Jamie-Cui/dotfiles.git ~/opt/dotfiles
cd ~/opt/dotfiles
make dry-run
make bootstrapOnly 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 \
~/.authinfoThe 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/nullEmacs 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.
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-fingerprintConfirm 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 bootstrapAfter 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.
Run make bootstrap once after installing the apps below.
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"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.
brew install --cask font-maple-mono-nl-nf-cnsudo 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 -fvVim (vim)
brew install vim # macOS
sudo dnf install vim-enhanced # FedoraNeovim (nvim)
brew install neovim # macOS
sudo dnf install neovim # FedoraEmacs (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-srcbrew 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/emacsclientsudo 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 installemacs --versionEdit the managed ~/.emacs.d/init.el and ~/.emacs.d/early-init.el sources for
Emacs settings.
Universal Ctags (ctags)
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 --yesorg-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 --yesgit-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 --yesTDLib (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 --yesFor 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 # FedoraFlameshot (flameshot)
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.appsudo dnf install flameshot grimThe 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)
brew install --cask squirrel-appAdd 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.
sudo dnf install fcitx5 fcitx5-rime imsettings im-chooser
im-chooserSelect Fcitx 5 in im-chooser, then log out and back in.
proxyctl (bin)
proxyctl init
source ~/.zshrcCodex skills (skills)
No setup beyond make bootstrap.
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-xauthi3 (i3)
sudo dnf install i3 i3lock picom feh xclip dex-autostart network-manager-applet pulseaudio-utils ibus jq xss-lock setxkbmapi3blocks (i3blocks)
sudo dnf install i3blocks alsa-utils sysstat perlRofi (rofi)
sudo dnf install rofi firefoxHyprland (hypr)
sudo dnf install hyprland hyprlock hyprpaper xdg-desktop-portal-hyprland brightnessctl playerctl wireplumber xclipThe 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 baobabDunst (dunst)
sudo dnf install dunstFcitx5 (fcitx5)
sudo dnf install fcitx5 fcitx5-rime fcitx5-gtk fcitx5-qtGTK 2 (gtk-2.0)
sudo dnf install gtk2GTK 3 (gtk-3.0)
sudo dnf install gtk3GTK 4 (gtk-4.0)
sudo dnf install gtk4imsettings (imsettings)
sudo dnf install imsettingsDavMail (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.serviceKeep 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 indexUse 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
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 DockJankyBorders is optional. The AeroSpace configuration starts it only when the
borders executable exists:
brew tap FelixKratz/formulae
brew install bordersThe 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.appThe tracked bindings use ${HOME} for user-specific paths.