#!/usr/bin/python import sys import os import re LIBDIR = '<%= @gitolite_commonhooksdir %>' sys.path.insert(0, LIBDIR) import git_multimail import xmlrpclib from cookielib import LWPCookieJar from bugz.bugzilla import BugzillaProxy import urllib2 # When editing this list, remember to edit the same list in # modules/cgit/templates/filter.commit-links.sh BUG_REFS = { 'Mageia': { 're': re.compile('mga#([0-9]+)'), 'replace': 'https://bugs.mageia.org/show_bug.cgi?id=%s' }, 'Red Hat': { 're': re.compile('rhbz#([0-9]+)'), 'replace': 'https://bugzilla.redhat.com/show_bug.cgi?id=%s' }, 'Free Desktop': { 're': re.compile('fdo#([0-9]+)'), 'replace': 'https://bugs.freedesktop.org/show_bug.cgi?id=%s' }, 'KDE': { 're': re.compile('(?:bko|kde)#([0-9]+)'), 'replace': 'https://bugs.kde.org/show_bug.cgi?id=%s' }, 'GNOME': { 're': re.compile('(?:bgo|gnome)#([0-9]+)'), 'replace': 'https://bugzilla.gnome.org/show_bug.cgi?id=%s' }, 'Launchpad': { 're': re.compile('lp#([0-9]+)'), 'replace': 'https://launchpad.net/bugs/%s' }, } COMMIT_RE = re.compile('^commit ([a-f0-9]{40})') COMMIT_REPLACE = 'http://gitweb.mageia.org/%s/commit/?id=%s' MAGEIA_BUGZILLA_URL = 'https://bugs.mageia.org/xmlrpc.cgi' MAGEIA_BUGZILLA_PASSWORD_FILE = '.gitzilla-password' MAGEIA_BUGZILLA_COOKIE_FILE = '.gitzilla-cookie' git_multimail.FOOTER_TEMPLATE = """\ -- \n\ Mageia Git Monkeys. """ git_multimail.REVISION_FOOTER_TEMPLATE = git_multimail.FOOTER_TEMPLATE I18N_REVISION_HEADER_TEMPLATE = """\ Date: %(send_date)s To: %(recipients)s Subject: %(emailprefix)s%(oneline)s MIME-Version: 1.0 Content-Type: text/plain; charset=%(charset)s Content-Transfer-Encoding: 8bit From: %(fromaddr)s Reply-To: %(reply_to)s In-Reply-To: %(reply_to_msgid)s References: %(reply_to_msgid)s X-Git-Host: %(fqdn)s X-Git-Repo: %(repo_shortname)s X-Git-Refname: %(refname)s X-Git-Reftype: %(refname_type)s X-Git-Rev: %(rev)s Auto-Submitted: auto-generated """ REPO_NAME_RE = re.compile(r'^/git/(?P.+?)(?:\.git)?$') def repo_shortname(): basename = os.path.abspath(git_multimail.get_git_dir()) m = REPO_NAME_RE.match(basename) if m: return m.group('name') else: return basename # Override the Environment class to generate an apporpriate short name which is # used in git links and as an email prefix class MageiaEnvironment(git_multimail.Environment): def get_repo_shortname(self): return repo_shortname() git_multimail.Environment = MageiaEnvironment # Override the Reviesion class to inject gitweb/cgit links and any referenced # bug URLs class MageiaLinksRevision(git_multimail.Revision): bz = None def bugzilla_init(self): if self.bz is None: cookie_file = os.path.join(os.environ['HOME'], MAGEIA_BUGZILLA_COOKIE_FILE) self.cookiejar = LWPCookieJar(cookie_file) try: self.cookiejar.load() except IOError: pass self.bz = BugzillaProxy(MAGEIA_BUGZILLA_URL, cookiejar=self.cookiejar) return self.bz def bugzilla_login(self): params = { 'login': 'bot', 'password': open(os.path.join(os.environ['HOME'], MAGEIA_BUGZILLA_PASSWORD_FILE), 'r').readline().rstrip(), 'remember': True } self.bz.User.login(params) self.cookiejar.save() os.chmod(self.cookiejar.filename, 0600) def bugzilla_call(self, method, *args): """Attempt to call method with args. Log in if authentication is required. """ try: return method(*args) except xmlrpclib.Fault, fault: # Fault code 410 means login required if fault.faultCode == 410: self.bugzilla_login() return method(*args) raise def generate_email_body(self, push): """Show this revision.""" output = git_multimail.read_git_lines( ['log'] + self.environment.commitlogopts + ['-1', self.rev.sha1], keepends=True, ) bugs = {} commit = None idx = 0 for line in output: idx += 1 if line == "---\n": if commit and COMMIT_REPLACE: output.insert(idx, "\n") output.insert(idx, " %s\n" % (COMMIT_REPLACE % (self.environment.get_repo_shortname(), commit))) output.insert(idx, " Commit Link:\n") idx += 3 if bugs: output.insert(idx, " Bug links:\n") idx += 1 for tracker, bugnos in bugs.items(): output.insert(idx, " %s\n" % tracker) idx += 1 for bugno in bugnos: output.insert(idx, " %s\n" % (BUG_REFS[tracker]['replace'] % bugno)) idx += 1 output.insert(idx, "\n") idx += 1 # Attempt to modify bugzilla if "Mageia" in bugs: try: bz = self.bugzilla_init() # Mask email address comment = None # Suppress the "Bug links:" section if only one bug # is referenced if len(bugs) == 1 and len(bugs['Mageia']) == 1: comment = output[0:idx-4] else: comment = output[0:idx] comment[1] = re.sub(r'^(Author: [^@]*)@.*(>)?', r'\1@...>', comment[1]) comment = "".join(comment) params = {} params['ids'] = bugs['Mageia'] params['comment'] = { 'body': comment } self.bugzilla_call(bz.Bug.update, params) print "Updated bugzilla bugs: %s" % ", ".join(bugs['Mageia']) except: print "Unable to post to bugzilla bugs: %s :(" % ", ".join(bugs['Mageia']) pass break m = COMMIT_RE.search(line) if m: commit = m.group(1) for tracker in BUG_REFS.keys(): foundbugs = BUG_REFS[tracker]['re'].findall(line) if len(foundbugs): if not tracker in bugs: bugs[tracker] = foundbugs else: bugs[tracker] = list(set(bugs[tracker] + foundbugs)) return output # Override the Revision class to inject gitweb/cgit links and any referenced # bug URLs class MageiaI18NRevision(git_multimail.Revision): """A Change consisting of a single git commit.""" def __init__(self, reference_change, rev, num, tot): git_multimail.Change.__init__(self, reference_change.environment) self.reference_change = reference_change self.rev = rev self.change_type = self.reference_change.change_type self.refname = self.reference_change.refname self.num = num self.tot = tot self.author = git_multimail.read_git_output(['log', '--no-walk', '--format=%aN <%aE>', self.rev.sha1]) self.recipients = False self.output = [] i18n_folders = [] # Check files and find i18n folders for line in git_multimail.read_git_lines(['ls-tree', '-rd', self.rev.sha1]): (modetypesha1, name) = line.split("\t", 1) if name.endswith("/.tx"): i18n_folders.append(os.path.dirname(name)) if len(i18n_folders): self.output = git_multimail.read_git_lines( ['log', '-C', '--stat', '-p', '--no-walk', self.rev.sha1, '--'] + i18n_folders, keepends=True, ) if len(self.output): # We have some output so lets send the mail... self.recipients = 'i18n-reports@ml.mageia.org' def generate_email_body(self, push): """Show this revision.""" return self.output if __name__ == '__main__': # Attempt to write a last-updated file for cgit cosmetics try: git_dir = git_multimail.get_git_dir() infowebdir = os.path.join(git_dir, 'info', 'web') if not os.path.exists(infowebdir): os.makedirs(infowebdir) lastupdated = git_multimail.read_git_output( ['for-each-ref', '--sort=-committerdate', "--format=%(committerdate:iso8601)", '--count=1', 'refs/heads'], ) modfile = open(os.path.join(infowebdir, 'last-modified'), 'w') modfile.write(lastupdated) modfile.close() except Exception: pass try: req = urllib2.Request('http://alamut.mageia.org:8000', repo_shortname() + '.git') req.add_header('Content-Type', 'x-git/repo') fp = urllib2.urlopen(req, timeout=5) if (fp): fp.close() except Exception: pass config = git_multimail.Config('multimailhook') try: environment = git_multimail.choose_environment( config, osenv=os.environ, ) mailer = git_multimail.choose_mailer(config, environment) # For testing... #mailer = git_multimail.OutputMailer(sys.stdout) changes = [] for line in sys.stdin: (oldrev, newrev, refname) = line.strip().split(' ', 2) changes.append( git_multimail.ReferenceChange.create(environment, oldrev, newrev, refname) ) push = git_multimail.Push(changes) # First pass - regular commit mails git_multimail.Revision = MageiaLinksRevision push.send_emails(mailer, body_filter=environment.filter_body) # Second pass - i18n commit mails git_multimail.REVISION_HEADER_TEMPLATE = I18N_REVISION_HEADER_TEMPLATE git_multimail.Revision = MageiaI18NRevision # Don't send the summary email, so nuke the change recipients for change in push.changes: change.recipients = False push.send_emails(mailer, body_filter=environment.filter_body) except git_multimail.ConfigurationException, e: sys.exit(str(e))