Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Unified Diff: chromite/bin/cros_changelog

Issue 4102013: Added option for including priority/milestone to cros_changelog. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/crosutils.git
Patch Set: Fixes from David James code review. Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | chromite/lib/tracker_access.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chromite/bin/cros_changelog
diff --git a/chromite/bin/cros_changelog b/chromite/bin/cros_changelog
index c79c2e8c561b91e8309c0cab1a5285a7782c7cb2..73b54354515b852e01d72ec70cc78443ed6ef754 100755
--- a/chromite/bin/cros_changelog
+++ b/chromite/bin/cros_changelog
@@ -17,6 +17,30 @@ import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../lib'))
from cros_build_lib import RunCommand
+
+# TODO(dianders):
+# We use GData to access the tracker on code.google.com. Eventually, we
+# want to create an ebuild and add the ebuild to hard-host-depends
+# For now, we'll just include instructions for installing it.
+INSTRS_FOR_GDATA = """
+To access the tracker you need the GData library. To install in your home dir:
+
+ GDATA_INSTALL_DIR=~/gdatalib
+ mkdir -p "$GDATA_INSTALL_DIR"
+
+ TMP_DIR=`mktemp -d`
+ pushd $TMP_DIR
+ wget http://gdata-python-client.googlecode.com/files/gdata-2.0.12.zip
+ unzip gdata-2.0.12.zip
+ cd gdata-2.0.12/
+ python setup.py install --home="$GDATA_INSTALL_DIR"
+ popd
+
+ export PYTHONPATH="$GDATA_INSTALL_DIR/lib/python:$PYTHONPATH"
+
+You should add the PYTHONPATH line to your .bashrc file (or equivalent)."""
+
+
DEFAULT_TRACKER = 'chromium-os'
@@ -38,11 +62,73 @@ def _GrabDirs():
return _GrabOutput('repo forall -c "pwd"').split()
+class Issue(object):
+ """Class for holding info about issues (aka bugs)."""
+
+ def __init__(self, project_name, issue_id, tracker_acc):
+ """Constructor for Issue object.
+
+ Args:
+ project_name: The tracker project to query.
+ issue_id: The ID of the issue to query
+ tracker_acc: A TrackerAccess object, or None.
+ """
+ self.project_name = project_name
+ self.issue_id = issue_id
+ self.milestone = ''
+ self.priority = ''
+
+ if tracker_acc is not None:
+ keyed_labels = tracker_acc.GetKeyedLabels(project_name, issue_id)
+ if 'Mstone' in keyed_labels:
+ self.milestone = keyed_labels['Mstone']
+ if 'Pri' in keyed_labels:
+ self.priority = keyed_labels['Pri']
+
+ def GetUrl(self):
+ """Returns the URL to access the issue."""
+ bug_url_fmt = 'http://code.google.com/p/%s/issues/detail?id=%s'
+
+ # Get bug URL. We use short URLs to make the URLs a bit more readable.
+ if self.project_name == 'chromium-os':
+ bug_url = 'http://crosbug.com/%s' % self.issue_id
+ elif self.project_name == 'chrome-os-partner':
+ bug_url = 'http://crosbug.com/p/%s' % self.issue_id
+ else:
+ bug_url = bug_url_fmt % (self.project_name, self.issue_id)
+
+ return bug_url
+
+ def __str__(self):
+ """Provides a string representation of the issue.
+
+ Returns:
+ A string that looks something like:
+
+ project:id (milestone, priority)
+ """
+ if self.milestone and self.priority:
+ info_str = ' (%s, P%s)' % (self.milestone, self.priority)
+ elif self.milestone:
+ info_str = ' (%s)' % self.milestone
+ elif self.priority:
+ info_str = ' (P%s)' % self.priority
+ else:
+ info_str = ''
+
+ return '%s:%s%s' % (self.project_name, self.issue_id, info_str)
+
+ def __cmp__(self, other):
+ """Compare two Issue objects."""
+ return cmp((self.project_name.lower(), self.issue_id),
+ (other.project_name.lower(), other.issue_id))
+
+
class Commit(object):
"""Class for tracking git commits."""
def __init__(self, commit, projectname, commit_email, commit_date, subject,
- body):
+ body, tracker_acc):
"""Create commit logs."""
self.commit = commit
self.projectname = projectname
@@ -51,10 +137,18 @@ class Commit(object):
self.commit_date = datetime.strptime(commit_date, fmt)
self.subject = subject
self.body = body
- self.bug_ids = self._GetBugIDs()
+ self._tracker_acc = tracker_acc
+ self._issues = self._GetIssues()
+
+ def _GetIssues(self):
+ """Get bug info from commit logs and issue tracker.
- def _GetBugIDs(self):
- """Get bug ID from commit logs."""
+ This should be called as the last step of __init__, since it
+ assumes that our member variables are already setup.
+
+ Returns:
+ A list of Issue objects, each of which holds info about a bug.
+ """
entries = []
for line in self.body.split('\n'):
@@ -63,7 +157,7 @@ class Commit(object):
for i in match.group(1).split(','):
entries.extend(filter(None, [x.strip() for x in i.split()]))
- bug_ids = []
+ issues = []
last_tracker = DEFAULT_TRACKER
regex = (r'http://code.google.com/p/(\S+)/issues/detail\?id=([0-9]+)'
r'|(\S+):([0-9]+)|(\b[0-9]+\b)')
@@ -72,38 +166,32 @@ class Commit(object):
bug_numbers = re.findall(regex, new_item)
for bug_tuple in bug_numbers:
if bug_tuple[0] and bug_tuple[1]:
- bug_ids.append('%s:%s' % (bug_tuple[0], bug_tuple[1]))
+ issues.append(Issue(bug_tuple[0], bug_tuple[1], self._tracker_acc))
last_tracker = bug_tuple[0]
elif bug_tuple[2] and bug_tuple[3]:
- bug_ids.append('%s:%s' % (bug_tuple[2], bug_tuple[3]))
+ issues.append(Issue(bug_tuple[2], bug_tuple[3], self._tracker_acc))
last_tracker = bug_tuple[2]
elif bug_tuple[4]:
- bug_ids.append('%s:%s' % (last_tracker, bug_tuple[4]))
+ issues.append(Issue(last_tracker, bug_tuple[4], self._tracker_acc))
- bug_ids.sort(key=str.lower)
- return bug_ids
+ issues.sort()
+ return issues
def AsHTMLTableRow(self):
"""Returns HTML for this change, for printing as part of a table.
Columns: Project, Date, Commit, Committer, Bugs, Subject.
+
+ Returns:
+ A string usable as an HTML table row, like:
+
+ <tr><td>Blah</td><td>Blah blah</td></tr>
"""
bugs = []
- bug_url_fmt = 'http://code.google.com/p/%s/issues/detail?id=%s'
link_fmt = '<a href="%s">%s</a>'
- for bug in self.bug_ids:
- tracker, bug_id = bug.split(':')
-
- # Get bug URL. We use short URLs to make the URLs a bit more readable.
- if tracker == 'chromium-os':
- bug_url = 'http://crosbug.com/%s' % bug_id
- elif tracker == 'chrome-os-partner':
- bug_url = 'http://crosbug.com/p/%s' % bug_id
- else:
- bug_url = bug_url_fmt % (tracker, bug_id)
-
- bugs.append(link_fmt % (bug_url, bug))
+ for issue in self._issues:
+ bugs.append(link_fmt % (issue.GetUrl(), str(issue)))
url_fmt = 'http://chromiumos-git/git/?p=%s.git;a=commitdiff;h=%s'
url = url_fmt % (self.projectname, self.commit)
@@ -132,7 +220,7 @@ class Commit(object):
cmp(self.commit_date, other.commit_date))
-def _GrabChanges(path, tag1, tag2):
+def _GrabChanges(path, tag1, tag2, tracker_acc):
"""Return list of commits to path between tag1 and tag2."""
cmd = 'cd %s && git config --get remote.cros.projectname' % path
@@ -145,14 +233,25 @@ def _GrabChanges(path, tag1, tag2):
for log_data in output.split('\0')[1:]:
commit, commit_email, commit_date, subject, body = log_data.split('\t', 4)
change = Commit(commit, projectname, commit_email, commit_date, subject,
- body)
+ body, tracker_acc)
commits.append(change)
return commits
+
def _ParseArgs():
parser = optparse.OptionParser()
- parser.add_option("--sort-by-date", dest="sort_by_date", default=False,
- action='store_true', help="Sort commits by date.")
+ parser.add_option(
+ "--sort-by-date", dest="sort_by_date", default=False,
+ action='store_true', help="Sort commits by date.")
+ parser.add_option(
+ "--tracker-user", dest="tracker_user", default=None,
+ help="Specify a username to login to code.google.com.")
+ parser.add_option(
+ "--tracker-pass", dest="tracker_pass", default=None,
+ help="Specify a password to go w/ user.")
+ parser.add_option(
+ "--tracker-passfile", dest="tracker_passfile", default=None,
+ help="Specify a file containing a password to go w/ user.")
return parser.parse_args()
@@ -178,11 +277,27 @@ def main():
print >>sys.stderr, 'E.g. %s %s cros/master' % (sys.argv[0], tags[0])
sys.exit(1)
+ if options.tracker_user is not None:
+ # TODO(dianders): Once we install GData automatically, move the import
+ # to the top of the file where it belongs. It's only here to allow
+ # people to run the script without GData.
+ try:
+ import tracker_access
+ except ImportError:
+ print >>sys.stderr, INSTRS_FOR_GDATA
+ sys.exit(1)
+ if options.tracker_passfile is not None:
+ options.tracker_pass = open(options.tracker_passfile, "r").read().strip()
+ tracker_acc = tracker_access.TrackerAccess(options.tracker_user,
+ options.tracker_pass)
+ else:
+ tracker_acc = None
+
print >>sys.stderr, 'Finding differences between %s and %s' % (tag1, tag2)
paths = _GrabDirs()
changes = []
for path in paths:
- changes.extend(_GrabChanges(path, tag1, tag2))
+ changes.extend(_GrabChanges(path, tag1, tag2, tracker_acc))
title = 'Changelog for %s to %s' % (tag1, tag2)
print '<html>'
« no previous file with comments | « no previous file | chromite/lib/tracker_access.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698