Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: sitescripts/hg/bin/update_issues.py

Issue 29336790: Issue 3674 - Add a hg hook that references commits in issues (Closed)
Left Patch Set: Created Feb. 22, 2016, 10:40 a.m.
Right Patch Set: Don't use RegexObject.match Created Feb. 23, 2016, 9:39 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « .sitescripts.example ('k') | sitescripts/hg/template/issue_commit_comment.tmpl » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 2
3 # This file is part of Adblock Plus <https://adblockplus.org/>, 3 # This file is part of Adblock Plus <https://adblockplus.org/>,
4 # Copyright (C) 2006-2016 Eyeo GmbH 4 # Copyright (C) 2006-2016 Eyeo GmbH
5 # 5 #
6 # Adblock Plus is free software: you can redistribute it and/or modify 6 # Adblock Plus is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License version 3 as 7 # it under the terms of the GNU General Public License version 3 as
8 # published by the Free Software Foundation. 8 # published by the Free Software Foundation.
9 # 9 #
10 # Adblock Plus is distributed in the hope that it will be useful, 10 # Adblock Plus is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details. 13 # GNU General Public License for more details.
14 # 14 #
15 # You should have received a copy of the GNU General Public License 15 # You should have received a copy of the GNU General Public License
16 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 16 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
17 17
18 """ 18 """
19 This module implements a changegroup hook that inspects all commit messages and 19 This module implements a changegroup (or pretxnchangegroup) hook that inspects
20 checks if issues from the Adblock Plus issue tracker are being referenced. If 20 all commit messages and checks if issues from the Adblock Plus issue tracker are
21 there are, it updates them with the respective changeset URLs. 21 being referenced. If there are, it updates them with the respective changeset
22 URLs.
22 """ 23 """
23 24
24 import posixpath 25 import posixpath
25 import re 26 import re
26 import xmlrpclib 27 import xmlrpclib
27 28
28 from sitescripts.utils import get_config 29 from sitescripts.utils import get_config, get_template
29
30 def _generate_changeset_url(repository_name, changeset_id):
31 template = get_config().get("hg", "changeset_url_template")
32 return template.format(repo=repository_name, id=changeset_id[:12])
33 30
34 def _generate_comments(repository_name, changes_by_issue): 31 def _generate_comments(repository_name, changes_by_issue):
35 comments = {} 32 comments = {}
33 template = get_template("hg/template/issue_commit_comment.tmpl",
34 autoescape=False)
36 for issue_id, changes in changes_by_issue.iteritems(): 35 for issue_id, changes in changes_by_issue.iteritems():
37 multiple_changes = len(changes) > 1 36 comments[issue_id] = template.render({"repository_name": repository_name,
38 if multiple_changes: 37 "changes": changes})
39 comment = "Some commits referencing this issue have landed:\n"
40 else:
41 comment = "A commit referencing this issue has landed:\n"
42 for change in changes:
43 if multiple_changes:
44 comment += "- "
45 comment += _generate_changeset_url(repository_name, change.hex())
46 comment += "\n"
47 comments[issue_id] = comment
Wladimir Palant 2016/02/22 11:32:33 I'd rather have this logic in a template. This sho
Felix Dahlke 2016/02/22 20:09:49 Done.
48 return comments 38 return comments
49 39
50 def _post_comment(issue_id, comment): 40 def _post_comment(issue_id, comment):
51 issue_id = int(issue_id) 41 issue_id = int(issue_id)
52 url = get_config().get("hg", "trac_xmlrpc_url") 42 url = get_config().get("hg", "trac_xmlrpc_url")
53 server = xmlrpclib.ServerProxy(url) 43 server = xmlrpclib.ServerProxy(url)
54 attributes = server.ticket.get(issue_id)[3] 44 attributes = server.ticket.get(issue_id)[3]
55 server.ticket.update(issue_id, comment, 45 server.ticket.update(issue_id, comment,
56 {"_ts": attributes["_ts"], "action": "leave"}) 46 {"_ts": attributes["_ts"], "action": "leave"}, True)
Wladimir Palant 2016/02/22 12:02:51 The ridiculous part about this is: there is a race
Wladimir Palant 2016/02/22 12:06:01 Sorry about so many mails, I've only noticed some
Felix Dahlke 2016/02/22 20:09:49 Done.
Felix Dahlke 2016/02/22 20:09:50 That seems to be quite the hassle for what I think
57 47
58 def hook(ui, repo, node, **kwargs): 48 def hook(ui, repo, node, **kwargs):
59 first_change = repo[node] 49 first_change = repo[node]
60 issue_number_regex = re.compile(r"Issue (\d+) - ") 50 issue_number_regex = re.compile(r"\bissue\s+(\d+)\b", re.I)
Wladimir Palant 2016/02/22 11:32:33 This is too restrictive, see https://hg.adblockplu
Felix Dahlke 2016/02/22 20:09:49 Done.
51 noissue_regex = re.compile(r"^noissue\b", re.I)
61 changes_by_issue = {} 52 changes_by_issue = {}
62 for revision in xrange(first_change.rev(), len(repo)): 53 for revision in xrange(first_change.rev(), len(repo)):
63 change = repo[revision] 54 change = repo[revision]
64 description = change.description() 55 description = change.description()
65 match = issue_number_regex.search(description) 56 issue_ids = issue_number_regex.findall(description)
Wladimir Palant 2016/02/22 11:32:33 What if there multiple issues referenced in the co
Felix Dahlke 2016/02/22 20:09:49 Never seen that before, but since you've shown a r
66 if match: 57 if issue_ids:
67 issue_number = match.group(1) 58 for issue_id in issue_ids:
68 changes_by_issue.setdefault(issue_number, []).append(change) 59 changes_by_issue.setdefault(issue_id, []).append(change)
60 elif not noissue_regex.search(description):
61 # We should just reject all changes when one of them has an invalid
62 # commit message format, see: https://issues.adblockplus.org/ticket/3679
63 ui.warn("warning: invalid commit message format in changeset %s\n" %
64 change)
69 65
70 repository_name = posixpath.split(repo.url())[1] 66 repository_name = posixpath.split(repo.url())[1]
71 comments = _generate_comments(repository_name, changes_by_issue) 67 comments = _generate_comments(repository_name, changes_by_issue)
72 68
73 issue_url_template = get_config().get("hg", "issue_url_template") 69 issue_url_template = get_config().get("hg", "issue_url_template")
74 for issue_id, comment in comments.iteritems(): 70 for issue_id, comment in comments.iteritems():
75 try: 71 try:
76 _post_comment(issue_id, comment) 72 _post_comment(issue_id, comment)
77 ui.status("updating %s\n" % issue_url_template.format(id=issue_id)) 73 ui.status("updating %s\n" % issue_url_template.format(id=issue_id))
78 except: 74 except:
79 ui.warn("failed to update %s - does it exist?\n" % \ 75 ui.warn("warning: failed to update %s\n" %
Wladimir Palant 2016/02/22 11:32:33 This backslash is unnecessary.
Felix Dahlke 2016/02/22 20:09:49 Done, also changed the message a bit.
80 issue_url_template.format(id=issue_id)) 76 issue_url_template.format(id=issue_id))
LEFTRIGHT

Powered by Google App Engine
This is Rietveld