summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xgitnoti.py130
1 files changed, 88 insertions, 42 deletions
diff --git a/gitnoti.py b/gitnoti.py
index 854d1e3..02949fe 100755
--- a/gitnoti.py
+++ b/gitnoti.py
@@ -1,17 +1,18 @@
#!/usr/bin/env python
-import git, os, sys
+import git, os, sys, time
from optparse import OptionParser
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol
from twisted.internet.task import LoopingCall
+import pyinotify
parser = OptionParser()
parser.add_option('-s', '--host')
parser.add_option('-n', '--nick', default = 'git')
parser.add_option('-d', '--dir')
parser.add_option('-c', '--channel')
-parser.add_option('-i', '--interval', type = 'int', default = 30)
+parser.add_option('-i', '--interval', type = 'int', default = 10)
(options, args) = parser.parse_args()
@@ -21,6 +22,9 @@ if not options.host or not options.dir or not options.channel or not options.nic
root = options.dir
+if root[-1] == '/':
+ root = root[:-1]
+
repos = None
def repo_commit_msg(repo, commit):
@@ -36,55 +40,95 @@ def repo_commit_msg(repo, commit):
)
return msg
-def check_repos(bot):
- global repos
- repo_paths = [x for x in os.listdir(root) if x.endswith('.git')]
- if not repos:
- repos = [[git.Repo('%s/%s' % (root, x)), None] for x in repo_paths]
- bot.gitmsg('Repos initialized: %s' % (', '.join([os.path.splitext(os.path.basename(x[0].path))[0] for x in repos])))
- else:
- old_paths = [os.path.basename(x[0].path) for x in repos]
- new_paths = [x for x in repo_paths if not x in old_paths]
- del_paths = [x for x in old_paths if not x in repo_paths]
- del_repos = [x for x in repos if os.path.basename(x[0].path) in del_paths]
- if del_repos:
- bot.gitmsg('Removed repo%s: %s' % ('' if len(del_paths) == 1 else '', ', '.join([os.path.splitext(x)[0] for x in del_paths])))
- for i in del_repos:
- repos.remove(i)
- new_added = []
- for i in new_paths:
- try:
- r = git.Repo('%s/%s' % (root, i))
- repos.append([r, None])
- new_added.append(i)
- except:
- pass
- if new_added:
- bot.gitmsg('New repo%s: %s' % ('' if len(new_added) == 1 else 's', ', '.join([os.path.splitext(x)[0] for x in new_added])))
-
- for i, v in enumerate(repos):
- repo = v[0]
+class ReposNotifyEvent(pyinotify.ProcessEvent):
+ def new_repo(self, event):
+ flags = pyinotify.EventsCodes.ALL_FLAGS
+ heads = '%s/refs/heads' % event.pathname
+
+ i = 0
+ # Wait max 10 seconds for refs/heads/ to appear.
+ while not os.access(heads, os.F_OK) and i < 10:
+ time.sleep(1)
+ i += 1
+ if not os.access(heads, os.F_OK):
+ self.bot.gitmsg('Repo %s was found but couldn''t locate refs/heads/ (repo NOT added).' % os.path.splitext(event.name)[0])
+ return
+
+ wdd = self.bot.wm.add_watch(heads, flags['IN_MODIFY'] | flags['IN_CREATE'])
+ repos[event.pathname] = [None, None, wdd]
+ self.bot.gitmsg('New repo: %s' % os.path.splitext(event.name)[0])
+
+ def updated_repo(self, event):
+ pathname = event.pathname
+ while len(pathname) > 1 and not pathname in repos:
+ pathname = os.path.dirname(pathname)
+ if len(pathname) == 1 or not pathname in repos:
+ return
+ l = repos[pathname]
+ if not l[0]:
+ l[0] = git.Repo(pathname)
+ repo = l[0]
if not repo.heads: # No commits
- continue
- last = v[1]
- if last == None:
- last = repo.heads[0].commit.id
- repos[i][1] = last
+ return
+ last = l[1]
nlast = repo.heads[0].commit
- if last and nlast.id != last:
+ if nlast.id != last:
msg = repo_commit_msg(repo, nlast)
- bot.gitmsg(msg)
- repos[i][1] = nlast.id
+ self.bot.gitmsg(msg)
+ l[1] = nlast.id
+
+ def process_IN_CREATE(self, event):
+ if os.path.dirname(event.pathname) == root:
+ self.new_repo(event)
+ else:
+ self.updated_repo(event)
+
+ def process_IN_DELETE(self, event):
+ if event.pathname in repos:
+ self.bot.wm.rm_watch(repos[event.pathname][2].values())
+ del repos[event.pathname]
+ self.bot.gitmsg('Removed repo: %s' % os.path.splitext(event.name)[0])
+
+ def process_IN_MODIFY(self, event):
+ self.updated_repo(event)
+
+def check_notifies(bot):
+ bot.notifier.process_events()
+ while bot.notifier.check_events():
+ bot.notifier.read_events()
+ bot.notifier.process_events()
class Bot(irc.IRCClient):
nickname = options.nick
+ def initial_add(self):
+ global repos
+ if not repos:
+ repos = {}
+ for path in (x for x in os.listdir(root) if x.endswith('.git')):
+ try:
+ r = git.Repo('%s/%s' % (root, path))
+ except:
+ continue
+ flags = pyinotify.EventsCodes.ALL_FLAGS
+ wdd = self.wm.add_watch('%s/%s/refs/heads' % (root, path), flags['IN_MODIFY'] | flags['IN_CREATE'])
+ repos['%s/%s' % (root, path)] = [r, r.heads[0].commit.id, wdd]
+
def gitmsg(self, msg):
self.say(options.channel, msg)
def signedOn(self):
self.join(options.channel)
- self.repeater = LoopingCall(check_repos, self)
+ self.wm = pyinotify.WatchManager()
+ flags = pyinotify.EventsCodes.ALL_FLAGS
+ self.wm.add_watch(root, flags['IN_DELETE'] | flags['IN_CREATE'] | flags['IN_ONLYDIR'])
+ self.rne = ReposNotifyEvent()
+ self.rne.bot = self
+ self.notifier = pyinotify.Notifier(self.wm, self.rne, timeout = options.interval)
+
+ self.initial_add()
+
+ self.repeater = LoopingCall(check_notifies, self)
self.repeater.start(options.interval)
def privmsg(self, user, channel, message):
@@ -97,16 +141,18 @@ class Bot(irc.IRCClient):
if messagelist[0].startswith(self.nickname):
cmd = messagelist[1].lower()
if cmd == 'list':
- s = 'Repos: %s' % ', '.join([os.path.splitext(os.path.basename(x[0].path))[0] for x in repos])
+ s = 'Repos: %s' % ', '.join([os.path.splitext(os.path.basename(x))[0] for x in repos.keys()])
self.msg(target, s)
elif cmd == 'last':
repo = messagelist[2].lower() if len(messagelist) > 2 else None
if not repo:
self.msg(target, 'Which repo?')
return
- repo = [r for r in repos if os.path.basename(r[0].path).startswith(repo)]
+ repo = [(k, v) for k, v in repos.iteritems() if os.path.basename(k).startswith(repo)]
if len(repo) == 1:
- r = repo[0][0]
+ path = repo[0][0]
+ repo = repo[0][1]
+ r = repo[0] or git.Repo(path)
msg = repo_commit_msg(r, r.heads[0].commit)
self.msg(target, msg)
elif len(repo) == 0: