X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=irkerhook.py;h=5852b120fe012e0c6eff5dbece5855989a148bf3;hb=96e8dd539ff5316d25c24ece9d5934583bf7d41c;hp=96f7d1f315c3bf9d4ceb2515308f024cd6d29db8;hpb=f6fbbca0f653ca7820f08a7afbba8ba59ebbb08f;p=irker.git diff --git a/irkerhook.py b/irkerhook.py index 96f7d1f..5852b12 100755 --- a/irkerhook.py +++ b/irkerhook.py @@ -37,9 +37,9 @@ default_channels = "irc://chat.freenode.net/#commits" # No user-serviceable parts below this line: # -version = "1.15" +version = "2.1" -import os, sys, commands, socket, urllib, subprocess, locale +import os, sys, commands, socket, urllib, subprocess, locale, datetime from pipes import quote as shellquote try: import simplejson as json # Faster, also makes us Python-2.5-compatible @@ -60,6 +60,8 @@ class Commit: self.files = None self.logmsg = None self.url = None + self.author_date = None + self.commit_date = None self.__dict__.update(extractor.__dict__) def __unicode__(self): "Produce a notification string from this commit." @@ -86,17 +88,19 @@ class Commit: self.url = webview except IOError: self.url = "" - return self.template % self.__dict__ + return unicode(self.template % self.__dict__, "utf-8") class GenericExtractor: "Generic class for encapsulating data from a VCS." booleans = ["tcp"] numerics = ["maxchannels"] + strings = ["email"] def __init__(self, arguments): self.arguments = arguments self.project = None self.repo = None # These aren't really repo data but they belong here anyway... + self.email = None self.tcp = True self.tinyifier = default_tinyifier self.server = None @@ -181,6 +185,8 @@ class GenericExtractor: setattr(self, key, False) elif key in GenericExtractor.numerics: setattr(self, key, int(val)) + elif key in GenericExtractor.strings: + setattr(self, key, val) if not self.project: sys.stderr.write("irkerhook.py: no project name set!\n") raise SystemExit(1) @@ -193,7 +199,11 @@ class GenericExtractor: def has(dirname, paths): "Test for existence of a list of paths." - return all([os.path.exists(os.path.join(dirname, x)) for x in paths]) + # all() is a python2.5 construct + for exists in [os.path.exists(os.path.join(dirname, x)) for x in paths]: + if not exists: + return False + return True # VCS-dependent code begins here @@ -211,6 +221,7 @@ class GitExtractor(GenericExtractor): self.repo = do("git config --get irker.repo") self.server = do("git config --get irker.server") self.channels = do("git config --get irker.channels") + self.email = do("git config --get irker.email") self.tcp = do("git config --bool --get irker.tcp") self.template = '%(bold)s%(project)s:%(reset)s %(green)s%(author)s%(reset)s %(repo)s:%(yellow)s%(branch)s%(reset)s * %(bold)s%(rev)s%(reset)s / %(bold)s%(files)s%(reset)s: %(logmsg)s %(brown)s%(url)s%(reset)s' self.tinyifier = do("git config --get irker.tinyifier") or default_tinyifier @@ -260,17 +271,18 @@ class GitExtractor(GenericExtractor): # Extract the meta-information for the commit commit.files = do("git diff-tree -r --name-only " + shellquote(commit.commit)) commit.files = " ".join(commit.files.strip().split("\n")[1:]) - # Design choice: for git we ship only the first line, which is + # Design choice: for git we ship only the first message line, which is # conventionally supposed to be a summary of the commit. Under # other VCSes a different choice may be appropriate. - metainfo = do("git log -1 '--pretty=format:%an <%ae>|%s' " + shellquote(commit.commit)) - (commit.author, commit.logmsg) = metainfo.split("|") - commit.mail = commit.author.split()[-1].strip("<>") + commit.author_name, commit.mail, commit.logmsg = \ + do("git log -1 '--pretty=format:%an%n%ae%n%s' " + shellquote(commit.commit)).split("\n") # This discards the part of the author's address after @. # Might be be nice to ship the full email address, if not # for spammers' address harvesters - getting this wrong # would make the freenode #commits channel into harvester heaven. commit.author = commit.mail.split("@")[0] + commit.author_date, commit.commit_date = \ + do("git log -1 '--pretty=format:%ai|%ci' " + shellquote(commit.commit)).split("|") return commit class SvnExtractor(GenericExtractor): @@ -299,6 +311,7 @@ class SvnExtractor(GenericExtractor): commit.branch = "" commit.rev = "r%s" % self.id commit.author = self.svnlook("author") + commit.commit_date = self.svnlook("date").partition('(')[0] commit.files = self.svnlook("dirs-changed").strip().replace("\n", " ") commit.logmsg = self.svnlook("log").strip() return commit @@ -341,6 +354,7 @@ class HgExtractor(GenericExtractor): self.repo = ui.config('irker', 'repo') self.server = ui.config('irker', 'server') self.channels = ui.config('irker', 'channels') + self.email = ui.config('irker', 'email') self.tcp = str(ui.configbool('irker', 'tcp')) # converted to bool again in do_overrides self.template = '%(bold)s%(project)s:%(reset)s %(green)s%(author)s%(reset)s %(repo)s:%(yellow)s%(branch)s%(reset)s * %(bold)s%(rev)s%(reset)s / %(bold)s%(files)s%(reset)s: %(logmsg)s %(brown)s%(url)s%(reset)s' self.tinyifier = ui.config('irker', 'tinyifier') or default_tinyifier @@ -369,6 +383,8 @@ class HgExtractor(GenericExtractor): commit.rev = '%d:%s' % (ctx.rev(), commit.commit) commit.branch = ctx.branch() commit.author = person(ctx.user()) + commit.author_date = \ + datetime.datetime.fromtimestamp(ctx.date()[0]).strftime('%Y-%m-%d %H:%M:%S') commit.logmsg = ctx.description() # Extract changed files from status against first parent st = self.repository.status(ctx.p1().node(), ctx.node()) @@ -376,12 +392,20 @@ class HgExtractor(GenericExtractor): return commit def hg_hook(ui, repo, **kwds): - # To be called from a Mercurial "commit" or "incoming" hook. Example - # configuration: + # To be called from a Mercurial "commit", "incoming" or "changegroup" hook. + # Example configuration: # [hooks] # incoming.irker = python:/path/to/irkerhook.py:hg_hook extractor = HgExtractor([(ui, repo)]) - ship(extractor, kwds['node'], False) + start = repo[kwds['node']].rev() + end = len(repo) + if start != end: + # changegroup with multiple commits, so we generate a notification + # for each one + for rev in range(start, end): + ship(extractor, rev, False) + else: + ship(extractor, kwds['node'], False) # The files we use to identify a Subversion repo might occur as content # in a git or hg repo, but the special subdirectories for those are more @@ -440,7 +464,22 @@ def ship(extractor, commit, debug): print message elif channels: try: - if extractor.tcp: + if extractor.email: + # We can't really figure out what our SF username is without + # exploring our environment. The mail pipeline doesn't care + # about who sent the mail, other than being from sourceforge. + # A better way might be to simply call mail(1) + sender = "irker@users.sourceforge.net" + msg = """From: %(sender)s +Subject: irker json + +%(message)s""" % {"sender":sender, "message":message} + import smtplib + smtp = smtplib.SMTP() + smtp.connect() + smtp.sendmail(sender, extractor.email, msg) + smtp.quit() + elif extractor.tcp: try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((extractor.server or default_server, IRKER_PORT))