autoland/metrics: Script to report on autoland usage (
bug 1340227); r?smacleod
This is a simple script that calculates and displays the number of commits to
the autoland branch and to the mozilla-central branch, via the Mercurial
pushlog.
MozReview-Commit-ID: ACAMm1iK84o
new file mode 100644
--- /dev/null
+++ b/autoland/metrics/autoland_stats.py
@@ -0,0 +1,131 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+import datetime
+import json
+import re
+from urllib.request import urlopen
+
+
+pushlog_url = 'https://hg.mozilla.org/{}/json-pushes'
+pushlog_url += '?startdate={}&version=2&full=1'
+enddate_arg = '&enddate={}'
+
+merge_desc = re.compile('^[M,m]erge')
+servo_merge_desc = re.compile('^servo: Merge')
+backout_desc = re.compile('^.*[b,B]acked out.*')
+
+
+def count_changesets(pushes, ignore_merges):
+ """Count changesets in a dict of pushes.
+
+ 'pushes' should be a dict as returned by the pushlog JSON API.
+ If 'ignore_merges' is True, does not count any changesets from any push
+ that has a changeset with a description matching the 'merge_desc'
+ regular expression above.
+
+ Never counts changesets from pushes containing a changeset with the
+ description matching 'servo_merge_desc', since this was a special
+ set of commits that were pushed directly to the autoland branch.
+
+ Returns a dictionary containing the following keys:
+ 'changesets': total number of changesets in the given pushes (ignoring
+ merges if 'ignore_merges' is True)
+ 'backouts': number of changesets matching 'backout_desc'
+ 'nonbackouts': changesets - backouts
+ """
+ changesets = 0
+ backouts = 0
+
+ for push_id, p in pushes.items():
+ push_changesets = set([c['node'] for c in p['changesets']])
+
+ for c in p['changesets']:
+ m = servo_merge_desc.match(c['desc'])
+
+ if m:
+ break
+
+ if ignore_merges:
+ m = merge_desc.match(c['desc'])
+
+ if m:
+ break
+
+ m = backout_desc.match(c['desc'])
+
+ if m:
+ backouts += 1
+ else:
+ changesets += len(push_changesets)
+
+ return {
+ 'changesets': changesets,
+ 'backouts': backouts,
+ 'nonbackouts': changesets - backouts
+ }
+
+
+def date_from_arg(arg):
+ """Convert an ISO date string into a datetime.date object."""
+ return datetime.datetime.strptime(arg, '%Y-%m-%d').date()
+
+
+if __name__ == '__main__':
+ import argparse
+ import sys
+
+ parser = argparse.ArgumentParser(
+ description='Print statistics on Autoland usage.')
+ parser.add_argument('startdate')
+ parser.add_argument('--enddate')
+ args = parser.parse_args()
+
+ startdate = date_from_arg(args.startdate)
+
+ if args.enddate:
+ enddate = date_from_arg(args.enddate)
+
+ if startdate >= enddate:
+ print('enddate must occur after startdate')
+ sys.exit(1)
+ else:
+ enddate = None
+
+ mc_pushlog_url = pushlog_url.format(
+ 'mozilla-central', startdate.isoformat())
+ autoland_pushlog_url = pushlog_url.format(
+ 'integration/autoland', startdate.isoformat())
+
+ if enddate:
+ mc_pushlog_url += enddate_arg.format(enddate)
+ autoland_pushlog_url += enddate_arg.format(enddate)
+
+ print('Getting m-c pushes: {}'.format(mc_pushlog_url))
+ mc_pushes = json.loads(urlopen(mc_pushlog_url).read())['pushes']
+ print('Done.')
+
+ print('Getting autoland pushes: {}'.format(autoland_pushlog_url))
+ autoland_pushes = json.loads(urlopen(
+ autoland_pushlog_url).read())['pushes']
+ print('Done.')
+ print()
+
+ if enddate:
+ print('Changesets from {} to {}:'.format(startdate, enddate))
+ else:
+ print('Changesets from {} to now.'.format(startdate))
+
+ mc_stats = count_changesets(mc_pushes, False)
+ autoland_stats = count_changesets(autoland_pushes, True)
+
+ print(' mozilla-central: {} (plus {} backouts)'.format(
+ mc_stats['nonbackouts'],
+ mc_stats['backouts']))
+ print(' autoland: {} (plus {} backouts)'.format(
+ autoland_stats['nonbackouts'],
+ autoland_stats['backouts']))
+
+ print('Percentage of changesets coming from autoland, '
+ 'ignoring backouts: {:.1%}'.format(
+ float(autoland_stats['nonbackouts']) / mc_stats['nonbackouts']))