ローカルのファイルシステムの監視。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