init
This commit is contained in:
commit
8fd8b08fe2
13
Pipfile
Normal file
13
Pipfile
Normal file
@ -0,0 +1,13 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
sysrsync = "*"
|
||||
watchdog = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.11"
|
77
Pipfile.lock
generated
Normal file
77
Pipfile.lock
generated
Normal file
@ -0,0 +1,77 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "9e22bfb09a5c4ca9d55498299d1b5eebe2d649be0adc7e90821d3c4a9195c038"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.11"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"sysrsync": {
|
||||
"hashes": [
|
||||
"sha256:435f9eb620e68ffb18ca5cbad32b113396a432361c7722038eab65c97dd83bd5",
|
||||
"sha256:9c8877f162dce9c480804445fca56cd19bf41b998d2172651468ccebfdc60850"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.6' and python_version < '4.0'",
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
|
||||
"version": "==0.10.2"
|
||||
},
|
||||
"watchdog": {
|
||||
"hashes": [
|
||||
"sha256:0144c0ea9997b92615af1d94afc0c217e07ce2c14912c7b1a5731776329fcfc7",
|
||||
"sha256:03e70d2df2258fb6cb0e95bbdbe06c16e608af94a3ffbd2b90c3f1e83eb10767",
|
||||
"sha256:093b23e6906a8b97051191a4a0c73a77ecc958121d42346274c6af6520dec175",
|
||||
"sha256:123587af84260c991dc5f62a6e7ef3d1c57dfddc99faacee508c71d287248459",
|
||||
"sha256:17e32f147d8bf9657e0922c0940bcde863b894cd871dbb694beb6704cfbd2fb5",
|
||||
"sha256:206afc3d964f9a233e6ad34618ec60b9837d0582b500b63687e34011e15bb429",
|
||||
"sha256:4107ac5ab936a63952dea2a46a734a23230aa2f6f9db1291bf171dac3ebd53c6",
|
||||
"sha256:4513ec234c68b14d4161440e07f995f231be21a09329051e67a2118a7a612d2d",
|
||||
"sha256:611be3904f9843f0529c35a3ff3fd617449463cb4b73b1633950b3d97fa4bfb7",
|
||||
"sha256:62c613ad689ddcb11707f030e722fa929f322ef7e4f18f5335d2b73c61a85c28",
|
||||
"sha256:667f3c579e813fcbad1b784db7a1aaa96524bed53437e119f6a2f5de4db04235",
|
||||
"sha256:6e8c70d2cd745daec2a08734d9f63092b793ad97612470a0ee4cbb8f5f705c57",
|
||||
"sha256:7577b3c43e5909623149f76b099ac49a1a01ca4e167d1785c76eb52fa585745a",
|
||||
"sha256:998d2be6976a0ee3a81fb8e2777900c28641fb5bfbd0c84717d89bca0addcdc5",
|
||||
"sha256:a3c2c317a8fb53e5b3d25790553796105501a235343f5d2bf23bb8649c2c8709",
|
||||
"sha256:ab998f567ebdf6b1da7dc1e5accfaa7c6992244629c0fdaef062f43249bd8dee",
|
||||
"sha256:ac7041b385f04c047fcc2951dc001671dee1b7e0615cde772e84b01fbf68ee84",
|
||||
"sha256:bca36be5707e81b9e6ce3208d92d95540d4ca244c006b61511753583c81c70dd",
|
||||
"sha256:c9904904b6564d4ee8a1ed820db76185a3c96e05560c776c79a6ce5ab71888ba",
|
||||
"sha256:cad0bbd66cd59fc474b4a4376bc5ac3fc698723510cbb64091c2a793b18654db",
|
||||
"sha256:d10a681c9a1d5a77e75c48a3b8e1a9f2ae2928eda463e8d33660437705659682",
|
||||
"sha256:d4925e4bf7b9bddd1c3de13c9b8a2cdb89a468f640e66fbfabaf735bd85b3e35",
|
||||
"sha256:d7b9f5f3299e8dd230880b6c55504a1f69cf1e4316275d1b215ebdd8187ec88d",
|
||||
"sha256:da2dfdaa8006eb6a71051795856bedd97e5b03e57da96f98e375682c48850645",
|
||||
"sha256:dddba7ca1c807045323b6af4ff80f5ddc4d654c8bce8317dde1bd96b128ed253",
|
||||
"sha256:e7921319fe4430b11278d924ef66d4daa469fafb1da679a2e48c935fa27af193",
|
||||
"sha256:e93f451f2dfa433d97765ca2634628b789b49ba8b504fdde5837cdcf25fdb53b",
|
||||
"sha256:eebaacf674fa25511e8867028d281e602ee6500045b57f43b08778082f7f8b44",
|
||||
"sha256:ef0107bbb6a55f5be727cfc2ef945d5676b97bffb8425650dadbb184be9f9a2b",
|
||||
"sha256:f0de0f284248ab40188f23380b03b59126d1479cd59940f2a34f8852db710625",
|
||||
"sha256:f27279d060e2ab24c0aa98363ff906d2386aa6c4dc2f1a374655d4e02a6c5e5e",
|
||||
"sha256:f8affdf3c0f0466e69f5b3917cdd042f89c8c63aebdb9f7c078996f607cdb0f5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==4.0.1"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
112
syncer.py
Normal file
112
syncer.py
Normal file
@ -0,0 +1,112 @@
|
||||
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()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user