aboutsummaryrefslogtreecommitdiffstats
path: root/deployment/mgagit/templates/git-post-receive-hook
diff options
context:
space:
mode:
Diffstat (limited to 'deployment/mgagit/templates/git-post-receive-hook')
-rwxr-xr-xdeployment/mgagit/templates/git-post-receive-hook314
1 files changed, 314 insertions, 0 deletions
diff --git a/deployment/mgagit/templates/git-post-receive-hook b/deployment/mgagit/templates/git-post-receive-hook
new file mode 100755
index 00000000..68da3200
--- /dev/null
+++ b/deployment/mgagit/templates/git-post-receive-hook
@@ -0,0 +1,314 @@
+#!/usr/bin/python3
+
+import configparser
+import os
+import re
+import sys
+import urllib.error
+import urllib.parse
+import urllib.request
+import xmlrpc.client
+from dataclasses import dataclass
+
+LIBDIR = '<%= @gitolite_commonhooksdir %>'
+sys.path.insert(0, LIBDIR)
+
+import bugz.settings
+
+import git_multimail
+
+# 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/%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 = 'https://gitweb.mageia.org/%s/commit/?id=%s'
+
+MAGEIA_BUGZILLA_URL = 'https://bugs.mageia.org/xmlrpc.cgi'
+MAGEIA_BUGZILLA_PASSWORD_FILE = '.gitzilla-password'
+MAGEIA_BUGZILLA_APIKEY_FILE = '.gitzilla-apikey' # this holds a Bugzilla API key
+GITWEB_UPDATE_URL = 'http://gitweb.mageia.org:8000'
+
+# Bugzilla user
+USER_LOGIN = 'bot'
+
+# Recipient of i18n notifications
+I18N_MAIL = 'i18n-reports@ml.mageia.org'
+
+# Set to 1..3 for debug logging (WARNING: this will show passwords to git committers!)
+DEBUG = 0
+
+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<name>.+?)(?:\.git)?$')
+
+
+# Log a debug message when logging is enabled
+def debug(s, *a):
+ if DEBUG > 0:
+ print(s % a)
+
+
+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 appropriate 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 Revision class to inject gitweb/cgit links and any referenced
+# bug URLs
+class MageiaLinksRevision(git_multimail.Revision):
+ def __init__(self, reference_change, rev, num, tot):
+ super().__init__(reference_change, rev, num, tot)
+ self.bz = None
+
+ def bugzilla_init(self):
+ if not self.bz:
+ tokenfile = os.path.join(os.environ['HOME'], MAGEIA_BUGZILLA_APIKEY_FILE)
+ token = None
+ try:
+ token = open(tokenfile, 'r').readline().rstrip()
+ except IOError:
+ # Assume username/password will be used instead
+ pass
+
+ passwordfile = os.path.join(os.environ['HOME'], MAGEIA_BUGZILLA_PASSWORD_FILE)
+ pword = None
+ try:
+ pword = open(passwordfile, 'r').readline().rstrip()
+ except IOError:
+ print('Error: no Bugzilla credentials available; trying anyway')
+ # There's no real point in continuing, but why not
+
+ class ConfigSettings:
+ pass
+
+ cfg = ConfigSettings()
+ cfg.connection = 'Mageia'
+ if token:
+ # If an API key is found, that will be used instead of user/password
+ cfg.key = token
+ cfg.user = USER_LOGIN
+ cfg.password = pword
+ cfg.base = MAGEIA_BUGZILLA_URL
+ cfg.debug = DEBUG
+
+ cfile = configparser.ConfigParser()
+ cfile.add_section(cfg.connection)
+ self.bz = bugz.settings.Settings(cfg, cfile)
+
+ 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
+ # Modify the mail output on-the-fly to add links; this is sensitive to
+ # the mail format produced by git_multimail. Also, update Mageia
+ # Bugzilla if a bug reference is found.
+ 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:
+ 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.bz.call_bz(self.bz.bz.Bug.update, params)
+ print("Updated bugzilla bugs: %s" % ", ".join(bugs['Mageia']))
+ except:
+ print("Unable to post to bugzilla bugs: %s :(" % ", ".join(bugs['Mageia']))
+ print(sys.exc_info()[1])
+
+ 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 foundbugs:
+ if tracker not 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):
+ super().__init__(reference_change, rev, num, tot)
+
+ # Don't send to any of the normal recipients
+ self.recipients = False
+ self.cc_recipients = ''
+
+ 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 i18n_folders:
+ self.output = git_multimail.read_git_lines(
+ ['log', '-C', '--stat', '-p', '--no-walk', self.rev.sha1, '--'] + i18n_folders,
+ keepends=True,
+ )
+ if self.output:
+ # We have some output so let's send the mail...
+ self.recipients = I18N_MAIL
+ print(f'Sending i8n notification to {self.recipients}')
+
+
+ def generate_email_body(self, push):
+ """Show this revision."""
+
+ return self.output
+
+
+def main():
+ # Attempt to write a last-updated file for cgit cosmetics
+ git_dir = git_multimail.get_git_dir()
+ infowebdir = os.path.join(git_dir, 'info', 'web')
+ lastupdated = git_multimail.read_git_output(
+ ['for-each-ref', '--sort=-committerdate', "--format=%(committerdate:iso8601)", '--count=1', 'refs/heads'],
+ )
+ try:
+ if not os.path.exists(infowebdir):
+ os.makedirs(infowebdir)
+ with open(os.path.join(infowebdir, 'last-modified'), 'w') as modfile:
+ modfile.write(lastupdated)
+ except Exception:
+ debug('Warning: could not update git last-modified date: %s', sys.exc_info()[1])
+
+ # Contact the on-the-pull service on the gitweb server with the updated repo
+ try:
+ req = urllib.request.Request(GITWEB_UPDATE_URL, (repo_shortname() + '.git').encode('utf-8'))
+ req.add_header('Content-Type', 'x-git/repo')
+ fp = urllib.request.urlopen(req, timeout=5)
+ fp.close()
+ except Exception:
+ debug('Warning: could not contact gitweb server: %s', sys.exc_info()[1])
+
+ config = git_multimail.Config('multimailhook')
+
+ try:
+ environment = git_multimail.choose_environment(
+ config, osenv=os.environ,
+ )
+
+ mailer = git_multimail.choose_mailer(config, environment)
+ # For testing...send mail to stdout only
+ #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(environment, 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 as e:
+ sys.exit(str(e))
+
+
+if __name__ == '__main__':
+ main()