dotfiles/.local/bin/obsd-python-2026-07-26
Osaigbovo Omere 43007d310e
update...
2026-10-06 10:54:24 +01:00

114 lines
3.8 KiB
Python

#!/usr/bin/env python3
# AUTHOR: Daesorin
# CREATED: 2026-06-03 20:12 +0100
# UPDATED: 2026-07-24 09:40 +0100
"""
The script was modified to replace the archive flag with explicit metadata-stripping and checksum evaluation flags (`-rltz`, `--checksum`, `--no-perms`, `--omit-dir-times`) to prevent inode timestamp updates during rsync pulls, while introducing a native post-pull sanitisation function that removes iOS-injected carriage returns without altering file modification times.
"""
import argparse
import os
import subprocess
import time
from pathlib import Path
from threading import Timer
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
# PRODUCTION CONFIGURATION
local_dir = "//home/sgbv/obsidian/"
remote_dest = "ish:/root/obsidian/"
debounce_seconds = 2.0
# ignore workspace ui states to prevent infinite sync loops
excludes = [
"--exclude=.obsidian/workspace*",
"--exclude=.trash/",
"--exclude=.git/"
]
def build_rsync_cmd(src, dest):
# use checksum and strip posix metadata mapping to prevent inode ctime destruction
flags = [
"rsync",
"-rltz",
"--checksum",
"--delete",
"--no-perms",
"--no-owner",
"--no-group",
"--omit-dir-times"
]
return flags + excludes + [src, dest]
def sanitize_line_endings():
# strip dos carriage returns natively while preserving file modification timestamps
print("sanitising line endings...")
vault_path = Path(local_dir)
for file_path in vault_path.rglob("*.md"):
try:
content = file_path.read_bytes()
if b"\r\n" in content:
clean_content = content.replace(b"\r\n", b"\n")
# record original timestamps before writing
stat_data = file_path.stat()
file_path.write_bytes(clean_content)
# restore original access and modification timestamps
os.utime(file_path, (stat_data.st_atime, stat_data.st_mtime))
print(f"cleaned carriage returns: {file_path.name}")
except Exception as error:
print(f"sanitisation error on {file_path.name}: {error}")
def pull_sync():
print("pulling changes from ish to arch...")
subprocess.run(build_rsync_cmd(remote_dest, local_dir))
sanitize_line_endings()
print("pull complete.")
def push_sync():
print("pushing changes from arch to ish...")
subprocess.run(build_rsync_cmd(local_dir, remote_dest))
print("push complete.")
class watchdog_push_handler(FileSystemEventHandler):
def __init__(self):
self.sync_timer = None
def on_any_event(self, event):
# ignore directory changes and workspace or hidden cache updates
if event.is_directory or any(x in event.src_path for x in [".obsidian", ".trash"]):
return
print(f"local change detected: {event.event_type}")
if self.sync_timer:
self.sync_timer.cancel()
self.sync_timer = Timer(debounce_seconds, push_sync)
self.sync_timer.start()
def watch_mode():
print(f"arch watchdog listening on {local_dir}")
handler = watchdog_push_handler()
observer = Observer()
observer.schedule(handler, local_dir, recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\nstopping watchdog...")
observer.stop()
observer.join()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="arch sync node")
parser.add_argument("mode", nargs="?", default="watch", choices=["watch", "push", "pull"])
args = parser.parse_args()
if args.mode == "pull":
pull_sync()
elif args.mode == "push":
push_sync()
else:
watch_mode()