A tiny CLI that mirrors local code to remote systems with rsync over SSH, driven by codesync.yaml. Connection details — host, user, and a pointer to your SSH key — live in the config (no ~/.ssh/config needed). The key never leaves disk; codesync just passes its path to ssh -i.
# codesync.yaml
systems:
perlmutter: # system id
host: perlmutter-p1.nersc.gov # IP or hostname
user: myuser
key: ~/.ssh/id_nersc # pointer to your private key (-> ssh -i)
locations:
my_repo: # location id
local: ~/code/myproj
remote: code/myproj./setup.shInstalls everything under $HOME/.codesync and puts codesync on your PATH:
$HOME/.codesync/env/— a virtualenv (Python + PyYAML)$HOME/.codesync/bin/codesync— linked to this repo's launcher~/.local/bin/codesync— symlink on your PATH (the dir is added to your~/.zshrcor~/.bashrc)~/.config/codesync/codesync.yaml— the default config, seeded once (edit it to define your systems)
Then open a new terminal (or source ~/.zshrc) and run codesync from anywhere — no venv activation needed; it re-execs itself under $HOME/.codesync/env. You also need rsync and ssh on your PATH.
codesync reads the first config it finds: ./codesync.yaml (project-local), then ~/.config/codesync/codesync.yaml.
codesync <system> <location> # push (local -> remote) — the default
codesync push <system> <location> # explicit push
codesync pull <system> <location> # remote -> local
# add --dry-run to preview, -v for progresscodesync perlmutter my_repo →
rsync -e 'ssh -i ~/.ssh/id_nersc' … ~/code/myproj/ myuser@perlmutter-p1.nersc.gov:code/myproj/.
Omit <location> if the system has only one; omit <system> too if there's only one. The exact rsync command is printed before it runs.
Before a push, codesync creates the remote directory inside rsync's own SSH connection — via --rsync-path='mkdir -p … && rsync' — so you authenticate once, not twice (rsync otherwise only creates the final path component, not missing parents). Before a pull it creates the local directory. --dry-run skips this — it changes nothing.
codesync.yaml supports YAML anchors (&name) and aliases (*name), resolved natively — e.g. define a key path once and reuse it across systems (see the sample). exclude / rsync_flags can be set in defaults, per-system, or per-location; the most specific wins.
codesync— thin launcher: re-exec into the venv, then dispatch to the packagecodesync_core/config.py— find/validatecodesync.yaml, resolve a (system, location)codesync_core/rsync.py— build the rsync +ssh -icommand and exec itcodesync_core/cli.py— argument parsing and top-level error handlingsetup.sh— create the venv and seed the default config
If you have loaded modules in your .bashrc or .zshrc, make sure to add this to the top your code, so that during non-interactive sessions, modules are not loaded.
case $- in
*i*) ;; # interactive shell — keep going
*) return ;; # non-interactive (ssh/rsync) — stop here, stay silent
esac