<pre style='margin:0'>
Rainer Müller (raimue) pushed a commit to branch master
in repository trac.macports.org.
</pre>
<p><a href="https://github.com/macports/trac.macports.org/commit/b87ae642ad751eeb87169682e8bd162a0da6ca56">https://github.com/macports/trac.macports.org/commit/b87ae642ad751eeb87169682e8bd162a0da6ca56</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit b87ae642ad751eeb87169682e8bd162a0da6ca56
</span>Author: Rainer Müller <raimue@macports.org>
AuthorDate: Wed Nov 9 00:31:00 2016 +0100
<span style='display:block; white-space:pre;color:#404040;'> trac-github-update: get user data from Trac
</span><span style='display:block; white-space:pre;color:#404040;'>
</span><span style='display:block; white-space:pre;color:#404040;'> If GitHub supplied us with a username, try to find corresponding
</span><span style='display:block; white-space:pre;color:#404040;'> metadata in Trac. If no data exists in Trac, query GitHub to expand the
</span><span style='display:block; white-space:pre;color:#404040;'> real name.
</span><span style='display:block; white-space:pre;color:#404040;'>
</span><span style='display:block; white-space:pre;color:#404040;'> Closes: https://trac.macports.org/ticket/52826
</span>---
plugins/hooks/trac-github-update.config | 1 -
plugins/hooks/trac-github-update.py | 172 ++++++++++++++++++++++++++++----
2 files changed, 152 insertions(+), 21 deletions(-)
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/plugins/hooks/trac-github-update.config b/plugins/hooks/trac-github-update.config
</span><span style='display:block; white-space:pre;color:#808080;'>index d0ce957..20461a0 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/plugins/hooks/trac-github-update.config
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/plugins/hooks/trac-github-update.config
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -4,6 +4,5 @@
</span> administrator = admin@macports.org
mailingList = macports-changes@lists.macports.org
replyTo = macports-dev@lists.macports.org
<span style='display:block; white-space:pre;background:#ffe0e0;'>- from = pusher
</span> commitBrowseURL = https://github.com/macports/%(repo_shortname)s/commit/%(id)s
commitEmailFormat = html
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/plugins/hooks/trac-github-update.py b/plugins/hooks/trac-github-update.py
</span><span style='display:block; white-space:pre;color:#808080;'>index f455541..95001a0 100755
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/plugins/hooks/trac-github-update.py
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/plugins/hooks/trac-github-update.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -8,8 +8,9 @@ import json
</span>
import git_multimail
from git_multimail import GenericEnvironment, Config, ConfigurationException, \
<span style='display:block; white-space:pre;background:#ffe0e0;'>- OutputMailer, ReferenceChange, Push
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ OutputMailer, ReferenceChange, Revision, Push
</span> from github import Github
<span style='display:block; white-space:pre;background:#e0ffe0;'>+import trac.env
</span>
### TEMPLATES ###
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -50,10 +51,64 @@ git_multimail.LINK_HTML_TEMPLATE = """\
</span>
### MAIN ###
<span style='display:block; white-space:pre;background:#e0ffe0;'>+class TracDB(object):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def __init__(self, tracenv):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.env = tracenv
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.cache = {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def get_user(self, username):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if username in self.cache:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self.cache[username]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ row = self.env.db_query("""
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ SELECT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s1.value,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s2.value
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ FROM
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ session_attribute s1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ LEFT JOIN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ session_attribute s2
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ON
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s1.sid = s2.sid AND
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s1.authenticated = s2.authenticated AND
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s1.name = 'name' AND
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s2.name = 'email'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ WHERE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s1.authenticated = 1 AND s2.authenticated = 1 AND
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s1.sid = %s AND s2.sid = %s AND
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s1.name = 'name' AND
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ s2.name = 'email'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ """, (username, username))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ for name, email in row:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.cache[username] = (name, email)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self.cache[username]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return (None, None)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+class GitHubAPI(object):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def __init__(self, github):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.github = github
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.cache = {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def get_user(self, username):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if username in self.cache:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self.cache[username]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ name = self.github.get_user(username).name
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # GitHub only lists the primary email address in the payload. We do not
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # want to expose it to the public. The profile may not have any public
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # address. Use a static sender email instead.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ email = "%s@users.noreply.github.com" % (username,)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if name:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.cache[username] = (name, email)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self.cache[username]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return (None, None)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>
class GitHubWebhookEnvironment(GenericEnvironment):
<span style='display:block; white-space:pre;background:#ffe0e0;'>- def __init__(self, github, **kw):
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- self._github = github
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def __init__(self, github, tracenv, **kw):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self._github = GitHubAPI(github)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self._tracdb = TracDB(tracenv)
</span> self._data = None
self._pusher = None
self._pusher_email = None
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -61,35 +116,102 @@ class GitHubWebhookEnvironment(GenericEnvironment):
</span>
def load_payload(self, payload):
self._data = json.loads(payload)
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ self._commits = {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ for commit in self._data['commits']:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self._commits[commit['id']] = commit
</span> return self._data
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ def _get_username(self, username):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ result = None
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Get name from Trac DB
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ name, _ = self._tracdb.get_user(username)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if name:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ result = "%s (%s)" % (name, username)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # If user was not in Trac DB, ask GitHub API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if not result:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ name, _ = self._github.get_user(username)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if name:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ result = "%s (%s)" % (name, username)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ result = username
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return result
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def _get_username_email(self, username):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ result = None
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Get name and email from Trac DB
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ name, email = self._tracdb.get_user(username)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if name and email:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ result = "%s <%s>" % (name, email)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # If user was not in Trac DB, ask GitHub API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if not result:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ name, email = self._github.get_user(username)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if name:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ result = "%s <%s>" % (name, email)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ result = "%s <%s@users.noreply.github.com>" % (username, username)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return result
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def get_fromaddr(self, change=None):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if change:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if isinstance(change, ReferenceChange):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self.get_pusher_email()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ elif isinstance(change, Revision):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ author = None
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ commit = self._commits[change.rev.sha1]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if 'username' in commit['author']:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ username = commit['author']['username']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ author = self._get_username_email(username)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if not author:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ author = "%s <%s>" % (commit['author']['name'], commit['author']['email'])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return author.encode('utf-8')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return super(GitHubWebhookEnvironment, self).get_fromaddr(change)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> def get_pusher(self):
if self._pusher:
<span style='display:block; white-space:pre;background:#ffe0e0;'>- return self._pusher
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self._pusher.encode('utf-8')
</span> if not self._data:
return super(GitHubWebhookEnvironment, self).get_pusher()
<span style='display:block; white-space:pre;background:#ffe0e0;'>- login = self._data['pusher']['name']
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- realname = self._github.get_user(login).name
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- if realname:
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- self._pusher = "%s (%s)" % (realname, login)
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- else:
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- self._pusher = login
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ username = self._data['pusher']['name']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self._pusher = self._get_username(username)
</span> return self._pusher.encode('utf-8')
def get_pusher_email(self):
if self._pusher_email:
<span style='display:block; white-space:pre;background:#ffe0e0;'>- return self._pusher_email
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self._pusher_email.encode('utf-8')
</span> if not self._data:
return super(GitHubWebhookEnvironment, self).get_pusher_email()
<span style='display:block; white-space:pre;background:#ffe0e0;'>- login = self._data['pusher']['name']
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- name = self._github.get_user(login).name or login
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- # GitHub only lists the primary email address in the payload. We do not
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- # want to expose it to the public, and sending with these addresses
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- # would also violate DMARC. Use a static sender email instead.
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- email = "%s@users.noreply.github.com" % (login,)
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- self._pusher_email = "%s <%s>" % (name, email)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ username = self._data['pusher']['name']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self._pusher_email = self._get_username_email(username)
</span> return self._pusher_email.encode('utf-8')
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ def get_reply_to_refchange(self, refchange):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to = []
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to_extra = self.config.get('replyTo')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if reply_to_extra:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to.append(reply_to_extra)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return ", ".join(reply_to).encode('utf-8')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def get_reply_to_commit(self, revision):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to = []
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to_extra = self.config.get('replyTo')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to_committer = None
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ commit = self._commits[revision.rev.sha1]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ author = commit['author']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ committer = commit['committer']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if 'username' in author and 'username' in committer:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if author['username'] != committer['username']:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ username = commit['committer']['username']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to_committer = self._get_username_email(username)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if author['email'] != committer['email']:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ name = commit['committer']['name']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ email = commit['committer']['email']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to_committer = "%s <%s>" % (name, email)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if reply_to_committer:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to.append(reply_to_committer)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if reply_to_extra:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ reply_to.append(reply_to_extra)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return ", ".join(reply_to).encode('utf-8')
</span>
def run_as_github_webhook(environment, mailer):
payload = environment.load_payload(sys.stdin.read())
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -123,6 +245,7 @@ def main(args):
</span> # git-multimail:
config = Config('multimailhook')
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ # GitHub API
</span> token = os.getenv("GITHUB_ACCESS_TOKEN")
if not token:
token = config.get("githubAccessToken")
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -130,12 +253,21 @@ def main(args):
</span> sys.stderr.write("Set GITHUB_ACCESS_TOKEN in environment or " +
"'git config multimailhook.githubAccessToken <token>'!\n")
sys.exit(1)
<span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span> github = Github(token)
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Trac Environment
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ tracenvpath = os.getenv("TRAC_ENV")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if not tracenvpath:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ tracenvpath = config.get("tracEnv")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if not tracenvpath:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ sys.stderr.write("Set TRAC_ENV in environment or " +
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "'git config multimailhook.tracEnv <path>'!\n")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ sys.exit(1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ tracenv = trac.env.Environment(path=tracenvpath, create=False)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> # Select the type of environment:
try:
<span style='display:block; white-space:pre;background:#ffe0e0;'>- environment = GitHubWebhookEnvironment(github, config=config)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ environment = GitHubWebhookEnvironment(github, tracenv, config=config)
</span> except ConfigurationException:
sys.stderr.write("%s\n" % sys.exc_info()[1])
sys.exit(1)
</pre><pre style='margin:0'>
</pre>