Shell, vim and ssh configuration shared across the HPC systems and personal
machines I work on. One repo, symlinked into $HOME, with the per-machine
differences resolved at shell startup instead of by keeping separate branches.
git clone --recursive git@github.com:mjaehn/dotfiles.git ~/git/dotfiles
cd ~/git/dotfiles
./install_tools.sh # local + IAC only; skips apt where there is no root
./install_oh_my_zsh_plugins_theme.sh # oh-my-zsh + plugins + powerlevel10k
./install.sh # symlink everything into $HOME
exec zshThen run :PluginInstall inside vim once to build the vim plugins.
On a cluster without a system zsh (balfrin), run ./install_zsh_user.sh first
to compile one into ~/local.
install.sh is safe to re-run; it only refreshes symlinks.
install_tools.sh installs Miniforge into ~/miniforge3 and builds the
default conda environment (the one the shells auto-activate) from
conda_packages. To pull in new packages afterwards, add them there and run:
mamba install -n default -c conda-forge --file conda_packagesHost detection lives in lib/hostinfo.sh and sets DOTFILES_HOST (the machine)
and DOTFILES_CLUSTER (the site). Everything else keys off those two variables.
DOTFILES_HOST |
DOTFILES_CLUSTER |
Matched on | Notes |
|---|---|---|---|
santis |
alps |
santis* |
CSCS, uenv + Lmod |
balfrin |
alps |
balfrin* |
CSCS/MeteoSwiss, Tcl modules, locally built zsh |
clariden |
alps |
clariden* |
CSCS |
eiger |
alps |
eiger* |
CSCS |
euler |
eth |
eu* |
ETH; stays in bash, the module command misbehaves under zsh |
levante |
dkrz |
levante* |
DKRZ; non-interactive sessions stop early |
co2 |
iac |
co2 |
ETH IAC server |
atmos |
iac |
atmos |
ETH IAC server |
iac-laptop |
local |
iacpc* |
work laptop |
home-pc |
local |
desktop* |
personal desktop |
surface |
local |
surfacepro* |
personal laptop, WSL2 on aarch64 |
Hostnames are lowercased before matching, so SurfacePro11, IACPC-42 and
DESKTOP-ABC all match. On Alps, $CLUSTER_NAME is used as a fallback because
inside a uenv the hostname is generic.
conda is only bootstrapped where it is actually used: co2, atmos, and the
three local machines. The clusters use modules or uenv instead. All of them
use $HOME/miniforge3; since co2 and atmos share a filesystem, running
install_tools.sh on either one provisions both.
On Alps, $HOME is small and $SCRATCH (capstor) has a 1 million inode quota
that cache-heavy tools exhaust by themselves: a uv cache is ~80k files, a GT4Py
build cache ~25k. lib/hostinfo.sh therefore points every cache at the
flash-backed iopsstor scratch, which has no inode limit and is the right
filesystem for many small files:
/iopsstor/scratch/cscs/$USER/cache # $DOTFILES_CACHE_ROOT, = $XDG_CACHE_HOME
├── pip uv matplotlib numba cupy gt4py
└── tmp # $TMPDIR
XDG_CACHE_HOME covers everything that follows the spec (gh, fontconfig,
pyright, the Claude CLI); PIP_CACHE_DIR, UV_CACHE_DIR, MPLCONFIGDIR,
NUMBA_CACHE_DIR, CUPY_CACHE_DIR, GT4PY_BUILD_CACHE_DIR and TMPDIR are
set individually because those tools ignore it. Export DOTFILES_CACHE_ROOT
before the rc file runs to put the whole tree somewhere else.
Scratch is subject to the CSCS cleanup policy, so everything under that root
must be regenerable. Check the quota that motivates this with quota.
| Path | Linked to | Purpose |
|---|---|---|
lib/hostinfo.sh |
— | host/cluster detection and per-host environment |
lib/common.sh |
— | environment, aliases and functions for both shells |
lib/conda.sh |
— | conda bootstrap, where conda is used |
bashrc |
~/.bashrc |
bash prompt and the hand-off to zsh |
zshrc |
~/.zshrc |
oh-my-zsh, powerlevel10k, zsh options |
aliases.zsh |
~/.oh-my-zsh/custom/aliases.zsh |
the few zsh-only additions |
profile |
~/.profile |
minimal login-shell setup |
vimrc |
~/.vimrc |
vim configuration |
tmux.conf |
~/.tmux.conf |
tmux configuration |
gitconfig |
~/.gitconfig |
git config: delta pager, LFS filters, push defaults |
config |
~/.ssh/config |
ssh hosts, grouped by site |
p10k.zsh |
~/.p10k.zsh |
powerlevel10k prompt, generated |
vim-extensions/ |
~/.vim/vim-extensions/ |
vim plugins, git submodules |
keybindings.json |
not linked | VS Code keybindings, see below |
lib/ is not symlinked. bashrc and zshrc resolve their own symlink back to
this repo and source lib/ from there.
Anything shared between bash and zsh belongs in lib/, not in one of the rc
files. That includes the machine-specific aliases, so both shells behave the
same everywhere.
gitconfig holds everything that is the same everywhere. The identity is not in
it: the last line includes ~/.gitconfig.local, which is per machine and not
tracked, so the work and personal machines can commit under different addresses.
Write it by hand on a new machine, git silently ignores a missing include:
cat > ~/.gitconfig.local <<'EOF'
[user]
email = you@example.com
name = Your Name
EOFThe pager is delta || less, because delta is not installed on the clusters and
an unconditional pager = delta breaks git log there. install_tools.sh
installs delta on the Debian/Ubuntu/WSL machines.
bashrc execs into zsh on every interactive machine except Euler, and except
inside Slurm jobs. Euler sets DOTFILES_USE_ZSH=0 because its module command
does not work properly under zsh.
Git aliases use the oh-my-zsh names (gst, gco, gl, gp, ...). zsh gets
them from the plugin; lib/common.sh defines the same ones for bash so the two
shells match. The pretty graph log is gll.
keybindings.json restores the usual ctrl+a/c/x/v/z/y/f/h and tab-indent
behaviour when the Vim extension is active. It is not symlinked, because VS Code
stores its settings outside $HOME on WSL. Copy it into your VS Code user
directory by hand.
Quick cheat sheet for the plugins in vim-extensions/ (managed via Vundle, see
vimrc for the full Plugin list and custom mappings).
| Plugin | Purpose | Key bindings |
|---|---|---|
| NERDTree | File tree | ff toggle/reveal current file (custom); o open; t new tab; i/s split/vsplit; R refresh |
| ctrlp.vim | Fuzzy file/buffer finder | <C-p> open; <C-j>/<C-k> navigate; <C-t>/<C-v>/<C-s> open in tab/vsplit/split; <C-f>/<C-b> switch mode |
| vim-gitgutter | Git diff markers in the sign column | ]c/[c next/prev hunk; <leader>hs stage hunk; <leader>hu undo hunk; <leader>hp preview hunk |
| indentLine | Indent guides | :IndentLinesToggle |
| vim-rainbow | Rainbow parentheses | :RainbowToggle |
| lightline.vim | Status line | none, purely visual |
| vim-autotag | Regenerates ctags on save | none, automatic (disabled on Euler/Levante, too slow on those filesystems) |
| vim-fugitive | Git wrapper | :Git/:G status window (- stage/unstage, cc commit, dv diff); :Gdiffsplit; :Git blame |
| vim-surround | Change surrounding pairs/quotes/tags | ds<char> delete; cs<old><new> change; ys<motion><char> add; S wrap visual selection |
| vim-commentary | Toggle comments | gcc current line; gc + motion (e.g. gcap); gc in visual mode |
| tagbar | ctags sidebar | tl<space> toggle (custom); inside: <CR>/o jump to tag, p preview |
| ale | Async lint/build checks | :ALENext/:ALEPrevious cycle errors; :ALEFix run fixers; sign column shows severity |
| vim-tmux-navigator | Seamless vim/tmux pane movement | <C-h/j/k/l> move between vim splits and tmux panes; needs matching bindings in tmux.conf, not currently set there |
- Add a case arm to
lib/hostinfo.shmatching the lowercased hostname, settingDOTFILES_HOSTandDOTFILES_CLUSTER. - Put machine-specific environment in the per-host
casefurther down the same file; put machine-specific aliases in themachine-specificsection oflib/common.sh. - Add it to the conda case in
lib/conda.shonly if that machine uses conda. - Add a row to the table above.
- Check it:
zsh -c 'source lib/hostinfo.sh; echo $DOTFILES_HOST $DOTFILES_CLUSTER'
GPL-3.0, see LICENSE.