Spawning/spawning/reloader_svn.py

144 lines
4.9 KiB
Python

# Copyright (c) 2008, Donovan Preston
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Watch the svn revision returned from svn info and send a SIGHUP
to a process when the revision changes.
"""
import commands, optparse, os, signal, sys, tempfile, time
try:
from procname import setprocname
except ImportError, e:
setprocname = lambda n: None
def get_revision(directory):
cmd = 'svn info'
if directory is not None:
cmd = '%s %s' % (cmd, directory)
try:
out = commands.getoutput(cmd).split('\n')
except IOError:
return
for line in out:
if line.startswith('Revision: '):
return int(line[len('Revision: '):])
def watch_forever(directories, pid, interval):
setprocname("spawn: svn reloader")
if directories is None:
directories = ['.']
## Look for externals
all_svn_repos = set(directories)
def visit(parent, subdirname, children):
if '.svn' in children:
children.remove('.svn')
status, out = commands.getstatusoutput('svn propget svn:externals %s' % (subdirname, ))
if status:
return
for line in out.split('\n'):
line = line.strip()
if not line:
continue
name, _external_url = line.split()
fulldir = os.path.join(parent, subdirname, name)
## Don't keep going into the external in the walk()
try:
children.remove(name)
except ValueError:
print "*** An entry in svn externals doesn't exist, ignoring:", name
else:
directories.append(fulldir)
all_svn_repos.add(fulldir)
while directories:
dirname = directories.pop(0)
os.path.walk(dirname, visit, dirname)
revisions = {}
for dirname in all_svn_repos:
revisions[dirname] = get_revision(dirname)
print "(%s) svn watcher watching directories: %s" % (
os.getpid(), list(all_svn_repos))
while True:
if pid:
## Check to see if our controller is still alive; if not, just exit.
try:
os.getpgid(pid)
except OSError:
print "(%s) reloader_svn is orphaned; controller %s no longer running. Exiting." % (
os.getpid(), pid)
os._exit(0)
for dirname in all_svn_repos:
new_revision = get_revision(dirname)
if new_revision is not None and new_revision != revisions[dirname]:
revisions[dirname] = new_revision
if pid:
print "(%s) * SVN revision changed on %s to %s; Sending SIGHUP to %s at %s" % (
os.getpid(), dirname, new_revision, pid, time.asctime())
os.kill(pid, signal.SIGHUP)
os._exit(0)
else:
print "(%s) Revision changed, dying at %s" % (
os.getpid(), time.asctime())
os._exit(5)
time.sleep(interval)
def main():
parser = optparse.OptionParser()
parser.add_option("-d", "--dir", dest='dirs', action="append",
help="The directories to do svn info in. If not given, use cwd.")
parser.add_option("-p", "--pid",
type="int", dest="pid",
help="A pid to SIGHUP when the svn revision changes. "
"If not given, just print a message to stdout and kill this process instead.")
parser.add_option("-i", "--interval",
type="int", dest="interval",
help="The time to wait between scans, in seconds.", default=10)
options, args = parser.parse_args()
print "(%s) svn watcher running, controller pid %s" % (os.getpid(), options.pid)
if options.pid is None:
options.pid = os.getpid()
try:
watch_forever(options.dirs, int(options.pid), options.interval)
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()