ローカルのファイルシステムの監視。WindowsではGetDirectoryChangesW(だったっけ?)とかいうAPIがあって、Linuxにはinotifyがあるし、Macにもなにか手段があるはずなんだけど、 http://packages.python.org/watchdog/installation.html watchdog というライブラリを使えばAPIの違いを気にしないで監視できる。 watchdog すげー。
ファイルのアップロードとリモートでのコマンド実行。毎回scpコマンドを叩くのでも良いけど、 http://www.lag.net/paramiko/ paramiko というライブラリを使えば、常に ssh 接続を1本用意しておいてその上でファイルのアップロードもコマンド実行もできる。 paramiko すげー。
#!/usr/bin/env python import os import sys import time import paramiko from watchdog import events, observers def make_sftp(remote): if '@' in remote: user, host = remote.split('@') else: user = None host = remote try: config_file = open(os.path.expanduser('~/.ssh/config')) config = paramiko.config.SSHConfig() config.parse(config_file) conf = config.lookup(host) if user is None: user = conf.get('user') if hostname in conf: host = hostname except Exception: pass ssh = paramiko.SSHClient() #ssh.load_host_keys() ssh.load_system_host_keys() ssh.connect(host, username=user) sftp = ssh.open_sftp() sftp.ssh = ssh return sftp REMOTE_HOST = None LOCAL_BASE = REMOTE_BASE = None SFTP = None def put_file(local_path): global SFTP if SFTP is None: SFTP = make_sftp(REMOTE_HOST) relpath = os.path.relpath(local_path, LOCAL_BASE) if relpath.startswith('..'): print "%s is out of base." return remote_path = os.path.join(REMOTE_BASE, relpath) print "%s => %s" % (local_path, remote_path) SFTP.put(local_path, remote_path) class SyncEventHandler(events.FileSystemEventHandler): def _sync(self, event): put_file(event.src_path) def on_created(self, event): self._sync(event) super(SyncEventHandler, self).on_created(event) def on_modified(self, event): self._sync(event) super(SyncEventHandler, self).on_created(event) def main(src_dir, host, remote_dir): global REMOTE_HOST, LOCAL_BASE, REMOTE_BASE, SFTP REMOTE_HOST = host REMOTE_BASE = remote_dir LOCAL_BASE = src_dir SFTP = make_sftp(REMOTE_HOST) handler = SyncEventHandler() observer = observers.Observer() observer.schedule(handler, LOCAL_BASE, True) observer.start() try: while True: observer.join(1) except KeyboardInterrupt: observer.stop() try: while observer.is_alive(): observer.join(1) except: pass USAGE = """\ sync_local_changes.py LOCAL_BASE [user@]HOST REMOTE_BASE """ if __name__ == '__main__': if len(sys.argv) == 4: main(*sys.argv[1:]) else: print USAGE