Skip to content

Commit

Permalink
ignore .gitignore; make excludes configurable via argparse; rename th…
Browse files Browse the repository at this point in the history
…em to "ignores" (as in .syncignore)
  • Loading branch information
falkoschindler committed Oct 21, 2023
1 parent 73146b8 commit a9ee05b
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 16 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,22 @@ Positional arguments:

Options:

- `--ignore [IGNORE ...]`
comma-separated list of paths to ignore (default: ".git/, \_\_pycache\_\_/, .DS_Store, \*.tmp, .env")
- `--target-root TARGET_ROOT`
subfolder on target to synchronize to
subfolder on target to synchronize to (default: "")
- `--target-port TARGET_PORT`
SSH port on target
SSH port on target (default: 22)
- `--on-change ON_CHANGE`
command to be executed on remote host after any file change
command to be executed on remote host after any file change (default: None)
- `--mutex-interval MUTEX_INTERVAL`
interval in which mutex is updated
interval in which mutex is updated (default: 10 seconds)

### Notes

- We suggest you have some auto-reloading in place on the (slow) target machine, like [NiceGUI](https://nicegui.io).
- Only one user per target host should run LiveSync at a time. Therefore LiveSync provides a mutex mechanism.
- By default `.git/` folders are not synchronized.
- All files and directories from the `.gitignore` of any source directory are also excluded from synchronization.
- By default `.git/`, `__pycache__/`, `.DS_Store`, `*.tmp`, and `.env` folders and files are not synchronized.
- You can create a `.syncignore` file in any source directory to skip additional files and directories from syncing.
- If you pass a VSCode workspace file as `source`, LiveSync will synchronize each directory listed in the `folders` section.

Expand Down
13 changes: 6 additions & 7 deletions livesync/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ def make_target_root_directory(self) -> None:

class Folder:

def __init__(self, local_dir: Path, target: Target) -> None:
def __init__(self, local_dir: Path, target: Target, ignores: List[str]) -> None:
self.local_path = local_dir.resolve() # one should avoid `absolute` if Python < 3.11
self.target = target
self.ignores = ignores

# from https://stackoverflow.com/a/22090594/3419103
match_pattern = pathspec.patterns.gitwildmatch.GitWildMatchPattern
self._ignore_spec = pathspec.PathSpec.from_lines(match_pattern, self.get_excludes())
self._ignore_spec = pathspec.PathSpec.from_lines(match_pattern, self.get_ignores())

self._stop_watching = asyncio.Event()

Expand All @@ -48,10 +49,8 @@ def target_path(self) -> Path:
def ssh_path(self) -> str:
return f'{self.target.host}:{self.target_path}'

def get_excludes(self) -> List[str]:
return ['.git/', '__pycache__/', '.DS_Store', '*.tmp', '.env'] + \
self._parse_ignore_file(self.local_path / '.syncignore') + \
self._parse_ignore_file(self.local_path / '.gitignore')
def get_ignores(self) -> List[str]:
return self.ignores + self._parse_ignore_file(self.local_path / '.syncignore')

@staticmethod
def _parse_ignore_file(path: Path) -> List[str]:
Expand Down Expand Up @@ -90,7 +89,7 @@ def stop_watching(self) -> None:
def sync(self, post_sync_command: Optional[str] = None) -> None:
args = '--prune-empty-dirs --delete -avz --checksum --no-t'
# args += ' --mkdirs' # INFO: this option is not available in rsync < 3.2.3
args += ''.join(f' --exclude="{e}"' for e in self.get_excludes())
args += ''.join(f' --exclude="{e}"' for e in self.get_ignores())
args += f' -e "ssh -p {self.target.port}"'
run_subprocess(f'rsync {args} {self.local_path}/ {self.ssh_path}/', quiet=True)
if post_sync_command:
Expand Down
11 changes: 8 additions & 3 deletions livesync/livesync.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ def git_summary(folders: List[Folder]) -> str:


async def async_main() -> None:
parser = argparse.ArgumentParser(description='Repeatedly synchronize local directories with remote machine')
parser = argparse.ArgumentParser(
description='Repeatedly synchronize local directories with remote machine',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('source', type=str, help='local source folder or VSCode workspace file')
parser.add_argument('--ignore', nargs='*', type=str, default='.git/, __pycache__/, .DS_Store, *.tmp, .env',
help='comma-separated list of paths to ignore')
parser.add_argument('--target-root', type=str, default='', help='subfolder on target to synchronize to')
parser.add_argument('--target-port', type=int, default=22, help='SSH port on target')
parser.add_argument('--on-change', type=str, help='command to be executed on remote host after any file change')
Expand All @@ -24,14 +28,15 @@ async def async_main() -> None:
args = parser.parse_args()
source = Path(args.source)
target = Target(host=args.host, port=args.target_port, root=Path(args.target_root))
ignores = [word.strip() for word in str(args.ignore).split(',')]

folders: List[Folder] = []
if source.is_file():
workspace = json.loads(source.read_text())
paths = [Path(f['path']) for f in workspace['folders']]
folders = [Folder(p, target) for p in paths if p.is_dir()]
folders = [Folder(p, target, ignores) for p in paths if p.is_dir()]
else:
folders = [Folder(source, target)]
folders = [Folder(source, target, ignores)]

for folder in folders:
if not folder.local_path.is_dir():
Expand Down

0 comments on commit a9ee05b

Please sign in to comment.