blob: 5392fa54bb225e6ad50394be2faa4e74cd729d49 [file] [log] [blame]
#!/usr/bin/env python
"""%prog
Iterates over a series of commit logs generated from hg merge operations,
collating bug numbers, and generating links to all commits. Formats the
output for insertion into the Maintenance wiki:
https://wiki.mozilla.org/ReleaseEngineering/Maintenance
"""
import glob
import os
import re
import requests
found_commits = {}
unique_bugs = {}
def get_revision_url_for_tag(repo_basedir, repo_name, tag='production'):
"""Find the corresponding revision for a given tag in a local repo,
and return a web URL for that revision.
"""
base_hg_url = "https://hg.mozilla.org/build/%s/rev/" % repo_name
try:
from mercurial import hg, ui
repo = hg.repository(ui.ui(), os.path.join(repo_basedir, repo_name))
return base_hg_url + str(repo[tag])
except:
return base_hg_url + tag
def clean_commit_entry(entry):
"""Remove common preceding/trailing characters from commit messages.
"""
return entry.rstrip(' \.\,\-\:\;').lstrip(' \.\,\-\:\;')
def collate_merge_previews(logdir):
"""Iterate over commit logs to create a data structure containing
the commit information.
"""
repo_p = re.compile('^(.*)_preview_changes\.txt$')
changeset_p = re.compile('changeset:\s+\d+:(.*)$')
summary_p = re.compile('summary:\s+(.*)$')
bug_p = re.compile('[Bb]ug\s*(\d+)[\s\-\.\,]*(.*)$')
review_p = re.compile('(.*)[\s\-\.\,]*(r=.*)$')
for preview_file in glob.glob(os.path.join(logdir, '*_preview_changes.txt')):
repo = ""
m = repo_p.match(os.path.basename(preview_file))
if m:
repo = m.group(1)
else:
print "Error determining repo"
revision = ""
with open(preview_file) as f:
for line in f:
m = changeset_p.match(line)
if m:
revision = m.group(1)
continue
m = summary_p.match(line)
bug_number = 'None'
summary = 'None'
review = 'None'
if m:
commit_message = m.group(1)
bug_m = bug_p.match(commit_message)
if bug_m:
bug_number = bug_m.group(1)
if not unique_bugs.has_key(bug_number):
unique_bugs[bug_number] = 1
summary = bug_m.group(2)
else:
summary = commit_message
review_m = review_p.match(summary)
if review_m:
summary = review_m.group(1)
review = review_m.group(2)
if not found_commits.has_key(repo):
found_commits[repo] = {}
if not found_commits[repo].has_key(bug_number):
found_commits[repo][bug_number] = []
found_commits[repo][bug_number].append({'revision': revision,
'summary': clean_commit_entry(summary),
'review': clean_commit_entry(review)})
def process_results(update_bugzilla, wiki_markup_file, logdir):
"""Write our formatted commit data out to a file.
"""
if not found_commits:
print " * No commits found; nothing to do"
return
if wiki_markup_file:
f = open(wiki_markup_file,"w")
f.write('<div style="border: thin grey solid; background-color: lightgrey; float: right; text-align: right; text-size: 80%; padding-left: 5px; padding-right: 5px;">\n')
f.write('[https://bugzilla.mozilla.org/buglist.cgi?bug_id=' + \
','.join(sorted(unique_bugs)) + \
'&query_format=advanced&order=bug_status%2Cbug_id&tweak=1 ' +\
'View list in Bugzilla]\n')
f.write('</div>\n')
for repo in sorted(found_commits):
if wiki_markup_file:
f.write('[https://hg.mozilla.org/build/%s %s]\n' % (repo, repo))
for bug_number in sorted(found_commits[repo]):
bug_link = '{{bug|%s}}' % bug_number
if bug_number == 'None':
bug_link = 'No bug'
for commit in found_commits[repo][bug_number]:
hg_changeset_url = 'https://hg.mozilla.org/build/%s/rev/%s' % (repo,
commit['revision'])
if wiki_markup_file:
review_display = ' - %s' % commit['review']
if commit['review'] == 'None':
review_display = ''
f.write('* %s - %s%s ([%s %s])\n' % (bug_link,
commit['summary'],
review_display,
hg_changeset_url,
commit['revision']))
if args.update_bugzilla:
comment = 'In production: %s' % hg_changeset_url
if repo == "mozharness":
comment = 'mozharness production tag moved to: %s' % get_revision_url_for_tag(logdir, 'mozharness', tag='production')
payload = {'comment': comment,
'login': os.environ['BUGZILLA_USERNAME'],
'password': os.environ['BUGZILLA_PASSWORD']}
requests.post('https://bugzilla.mozilla.org/rest/bug/%s/comment' % bug_number, data=payload)
print " * curl --data-urlencode 'comment=In production: %s' --data-urlencode 'login=%s' --data-urlencode 'password=XXXXX' 'https://bugzilla.mozilla.org/rest/bug/%s/comment'" % (hg_changeset_url, os.environ['BUGZILLA_USERNAME'], bug_number)
if wiki_markup_file:
f.close()
if __name__ == '__main__':
import argparse
import sys
parser = argparse.ArgumentParser(description='Collate and format commit messages for Maintenance wiki.')
parser.add_argument("-l", "--logdir", dest="logdir", default='.',
help="directory containing merge preview output")
parser.add_argument("-b", "--update-bugzilla", action="store_true", dest="update_bugzilla", help="post comments on landed bugs in Bugzilla")
parser.add_argument("-w", "--wiki-markup-file", action="store", dest="wiki_markup_file", help="file to write wiki markup to", default=None)
args = parser.parse_args()
if not os.path.isdir(args.logdir):
print "ERROR: Log directory specified ('%s') does not exist. Exiting..." % args.logdir
sys.exit(1)
collate_merge_previews(args.logdir)
process_results(update_bugzilla=args.update_bugzilla,
wiki_markup_file=args.wiki_markup_file,
logdir=args.logdir)