Skip to main content

LiveSync

Repeatedly synchronize local workspace with a (slow) remote machine. It is available as PyPI package and hosted on GitHub.

PyPI version PyPI - Downloads GitHub commit activity GitHub issues GitHub license

Use Case

VS Code Remote Development and similar tools are great as long as your remote machine is powerful enough. But if your target is a Raspberry Pi, Jetson Nano/Xavier/Orin, Beagle Board or similar, it feels like coding in jelly. Especially if you run powerful extensions like Pylance, GitHub Copilot or Duet AI. LiveSync solves this by watching your code for changes and just copying the modifications to the slow remote machine. So you can develop on your own machine (and run tests there in the background) while all your changes appear also on the remote. It works best if you have some kind of reload mechanism in place on the target (NiceGUI, FastAPI or Flask for example).

Usage

BASH

livesync <source> <username>@<host>

LiveSync uses rsync (SSH) to copy the files, so the <username>@<host> must be accessible via SSH (ideally by key, not password or passphrase, because it will be called over and over).

Press CTRL-C to abort the synchronization.

Positional arguments:

  • <source> local folder
  • <target> target user, host and path (e.g. user@host:~/path; path defaults to source folder name in home directory)
  • <rsync_args> arbitrary rsync parameters after "--"

Options:

  • --ssh-port SSH_PORT SSH port on target (default: 22)
  • --on-change ON_CHANGE command to be executed on remote host after any file change (default: None)
  • --mutex-interval MUTEX_INTERVAL interval in which mutex is updated (default: 10 seconds)
  • --ignore-mutex ignore mutex (use with caution) (default: False)
  • --no-watch don't keep watching the copied folders for changes after the sync (default: False)

Python

Simple example (where robot is the ssh hostname of the target system):

from livesync import Folder, sync

sync(
    Folder('.', 'robot:~/navigation'),
    Folder('../rosys', 'robot:~/rosys'),
)

The sync call will block until the script is aborted. Only if watch=False is used, the sync call will end after copying the folders to the target once. The Folder class allows to set the port and an on_change bash command which is executed after a sync has been performed. Via the rsync_args build method you can pass additional options to configure rsync.

Version in the mutex

The mutex file (~/.livesync_mutex) on the target contains, per folder, a block with the source path, the current git revision in brackets and the git status. If the source repository has git tags, LiveSync derives a dunamai-style version from them and writes it into the brackets instead of the bare commit hash, so the remote side can tell which revision it currently holds:

/path/to/navigation --> robot:~/navigation
[0.1.0.post43.dev0+3f6ee0e]
## main

The format is always [<base>.post<N>.dev0+<short-hash>]:

  • <base> is the latest tag (a leading v directly followed by a digit is stripped, as in v1.2.3), <N> the number of commits since that tag, and <short-hash> the current commit — so the exact revision is always included, even sitting right on a tag ([0.1.0.post0.dev0+3f6ee0e]).
  • Without any tag the base is 0.0.0 and the distance is the total number of commits, e.g. [0.0.0.post12.dev0+63e867f].
  • A repository without any commit yet, or a source that is not a git repository, gets no revision brackets at all.

The version is recomputed from git on every mutex update (roughly every mutex_interval seconds), so it stays live while syncing. It is derived purely from git (no extra dependency): this is close to dunamai's PEP 440 output but not identical (dunamai drops the .post0.dev0+<hash> suffix on an exact tag, and does not replicate custom tag patterns, pre-releases, epochs or dirty markers). The string contains no " characters, so it survives the "' replacement LiveSync applies across the whole summary.

Parser contract for the target side: read the content of the [...] brackets on the revision line of each folder block; it is <base>.post<N>.dev0+<short-hash>. If you only need the bare commit hash, take the part after the last +.

Advanced example:

import argparse
from livesync import Folder, sync

parser = argparse.ArgumentParser(description='Sync local code with robot.')
parser.add_argument('robot', help='Robot hostname')

args = parser.parse_args()

touch = 'touch ~/robot/main.py'
sync(
    Folder('.', f'{args.robot}:~/navigation', on_change='touch ~/navigation/main.py'),
    Folder('../rosys', f'{args.robot}:~/rosys').rsync_args(add='-L', remove='--checksum'),
    mutex_interval=30,
)

Notes

  • We suggest you have some auto-reloading in place on the (slow) target machine, like NiceGUI.

  • Only one user per target host should run LiveSync at a time. Therefore LiveSync provides a mutex mechanism.

  • You can create a .syncignore file in any source directory to skip additional files and directories from syncing.

  • If a .syncignore file doesn't exist, it is automatically created containing .git/, .jj/, __pycache__/, .DS_Store, *.tmp, .env, and .venv.

  • .syncignore uses gitignore syntax, including ! to re-include a file that an earlier pattern excluded:

    /build/
    !/build/lizard.bin
    

    Note that this deviates from git itself, which refuses to re-include a file whose parent directory is excluded. Patterns are matched relative to the source directory, so an anchored pattern like /build/ applies only at the top level. Two rare pattern shapes are known to diverge between rsync and the file watcher: an interior ** matching zero directories (a/**/b/ should also cover a/b/) and backslash-escaped literals like \!name — both are pinned as xfail tests in tests/test_syncignore.py.

Installation

python3 -m pip install livesync

Development

For development we use uv:

git clone git@github.com:zauberzeug/livesync.git
cd livesync
uv sync

This creates a virtual environment in .venv with LiveSync installed in editable mode. Now you can change the code and run the livesync command with the modified code via uv run livesync ... (or activate the environment with source .venv/bin/activate).

To run the unit tests:

uv run pytest

We use pre-commit to keep uv.lock in sync with pyproject.toml and to run formatting (autopep8, isort) and linting (mypy, pylint). Install the git hooks once with:

uv run pre-commit install

Testing

We have build a small testing infrastructure with two docker containers. See tests/README.md for details.

Releases

Just create and push a new tag with the new version name (v0.2.1 for example). After a successful build a new release will be created. This should be edited to describe the changes in the release notes.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

livesync-0.6.0.tar.gz (13.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

livesync-0.6.0-py3-none-any.whl (13.5 kB view details)

Uploaded Python 3

File details

Details for the file livesync-0.6.0.tar.gz.

File metadata

  • Download URL: livesync-0.6.0.tar.gz
  • Upload date:
  • Size: 13.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for livesync-0.6.0.tar.gz
Algorithm Hash digest
SHA256 be7611cda267b19645e78b9e76f1e81b80d409adf8f6e940ee229e19e854422e
MD5 bcb6c88fe9cdc08f574f2551fb708b0a
BLAKE2b-256 914c885ec98dafcb1aa14bbe9088135480cccc8a3893ebeb57f7337a38daaf46

See more details on using hashes here.

File details

Details for the file livesync-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: livesync-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 13.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for livesync-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3cefc5b4e4ae3841e155f9014cd673b491a5a1e6319a738acaa2a7674fdc827b
MD5 94f43ecea6be43a51d97515d1eeb573b
BLAKE2b-256 3e5be641059f944af206a8bfb0901fe59c8463782c7b6f892a5fb30631718765

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 files

0.5.0

2 files

0.4.0

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.2

2 files

0.2.1

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

0.0.6

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page