autoland/metrics: Script to report on autoland usage (bug 1340227); r?smacleod draft
authorMark Cote <mcote@mozilla.com>
Thu, 16 Feb 2017 13:03:51 -0500
changeset 163 563f34490256c2c5ce0a644c306893cdbf847f0e
parent 162 b04d73001d3d9f898ce7b0ec1143f8b7cc2ac0e9
push id100
push usermcote@mozilla.com
push dateThu, 16 Feb 2017 18:04:58 +0000
reviewerssmacleod
bugs1340227
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
autoland/metrics/autoland_stats.py
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']))