syncer/syncer.py

113 lines
4.0 KiB
Python
Raw Permalink Normal View History

2024-08-05 16:24:37 +03:00
import time
import logging
import argparse
import sysrsync
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger()
class SyncHandler(FileSystemEventHandler):
def __init__(self, local_dir, remote_dir, remote_user, remote_host, exclude_dirs, sync_interval, jump_host=None):
self.local_dir = local_dir
self.remote_dir = remote_dir
self.remote_user = remote_user
self.remote_host = remote_host
self.exclude_dirs = exclude_dirs
self.jump_host = jump_host
self.sync_interval = sync_interval
self.observer = Observer()
# Initial sync
logger.info("Initial sync in both directions")
self.sync_local_to_remote()
self.sync_remote_to_local()
self.observer.schedule(self, path=self.local_dir, recursive=True)
self.observer.start()
def on_any_event(self, event):
if any(excluded in event.src_path for excluded in self.exclude_dirs):
return
logger.info(f"Local change detected: {event.src_path} - {event.event_type}")
self.sync_local_to_remote()
def sync_local_to_remote(self):
ssh_command = f"ssh -J {self.jump_host}" if self.jump_host else "ssh"
try:
sysrsync.run(
source=self.local_dir,
destination=f"{self.remote_user}@{self.remote_host}:{self.remote_dir}",
options=[
"--archive",
"--compress",
f"-e {ssh_command}"
],
exclusions=self.exclude_dirs
)
logger.info("Local => Remote complete")
except Exception as e:
logger.error(f"Error during local to remote sync: {e}")
def sync_remote_to_local(self):
ssh_command = f"ssh -J {self.jump_host}" if self.jump_host else "ssh"
try:
sysrsync.run(
source=f"{self.remote_user}@{self.remote_host}:{self.remote_dir}",
destination=self.local_dir,
options=[
"--archive",
"--compress",
f"-e {ssh_command}"
],
exclusions=self.exclude_dirs
)
logger.info("Local <= Remote complete")
except Exception as e:
logger.error(f"Error during remote to local sync: {e}")
def start(self):
count = 0
try:
while True:
time.sleep(self.sync_interval)
self.sync_remote_to_local()
if count % 10 == 0:
self.sync_local_to_remote()
count += 1
except KeyboardInterrupt:
self.observer.stop()
self.observer.join()
def main():
parser = argparse.ArgumentParser(description="Two-way auto sync script using rsync")
parser.add_argument("--local-dir", required=True, help="Local directory to sync")
parser.add_argument("--remote-dir", required=True, help="Remote directory to sync")
parser.add_argument("--remote-user", default="root", help="Remote user")
parser.add_argument("--remote-host", required=True, help="Remote host")
parser.add_argument("--sync-interval", type=int, default=5, help="Sync interval in seconds")
parser.add_argument("--jump-host", help="Jump host for SSH")
parser.add_argument("--exclude-dirs", nargs='*', default=[".git", ".venv", "__pycache__", "node_modules", ".DS_Store"], help="Directories to exclude from sync")
args = parser.parse_args()
sync_handler = SyncHandler(
local_dir=args.local_dir,
remote_dir=args.remote_dir,
remote_user=args.remote_user,
remote_host=args.remote_host,
exclude_dirs=args.exclude_dirs,
sync_interval=args.sync_interval,
jump_host=args.jump_host
)
# Start the sync handler
sync_handler.start()
if __name__ == "__main__":
main()