]> scripts.mit.edu Git - wizard.git/blob - wizard/command/research.py
Convert ad hoc shell calls to singleton instance; fix upgrade bug.
[wizard.git] / wizard / command / research.py
1 import logging
2 import traceback
3 import itertools
4 import random
5
6 from wizard import app, command, deploy, shell, util
7
8 def main(argv, baton):
9     options, show = parse_args(argv, baton)
10     appname = show[0]
11     application = app.applications()[appname]
12     deploys = deploy.parse_install_lines(show, options.versions_path)
13     stats = {}
14     iffy = 0
15     clean = 0
16     total = 0
17     deploys = itertools.islice(deploys, options.limit)
18     if options.sample:
19         deploys = random.sample(list(deploys), options.sample)
20     try:
21         for d in deploys:
22             logging.info("Processing " + d.location)
23             try:
24                 d.verify()
25                 d.verifyTag(options.srv_path)
26                 d.verifyGit(options.srv_path)
27                 d.verifyConfigured()
28                 with util.ChangeDirectory(d.location):
29                     results = []
30                     out = shell.safeCall('git', 'diff', '--numstat', d.app_version.scripts_tag, strip=True)
31                     total += 1
32                     for line in out.split("\n"):
33                         added, deleted, filename = line.split(None, 3)
34                         if filename.endswith("php.ini"): continue
35                         if added == '-': continue
36                         if deleted == '-': continue
37                         added = int(added)
38                         deleted = int(deleted)
39                         if not added and not deleted or application.researchFilter(filename, added, deleted):
40                             continue
41                         results.append((added,deleted,filename))
42                     if len(results) > options.filter:
43                         print "-       -       " +  d.location
44                         iffy += 1
45                         continue
46                     if not results:
47                         clean += 1
48                     for added,deleted,filename in results:
49                         stats.setdefault(filename, 0)
50                         stats[filename] += 1
51                         if application.researchVerbose(filename) and not options.verbose:
52                             continue
53                         print "%-7d %-7d %s/%s" % (added,deleted,d.location,filename)
54             except (deploy.NotConfiguredError, deploy.NotMigratedError):
55                 # XXX: These should error, but for now don't
56                 pass
57             except (deploy.Error, shell.CallError):
58                 # XXX: Maybe should also do app.Error
59                 logging.error("%s in %s" % (traceback.format_exc(), d.location))
60             except KeyboardInterrupt:
61                 raise
62             except:
63                 logging.critical("%s in %s" % (traceback.format_exc(), d.location))
64     except KeyboardInterrupt:
65         print
66         print "Caught signal..."
67         pass
68     print '-' * 50
69     for filename in sorted(stats.keys()):
70         count = stats[filename]
71         if not count: continue
72         print "%-7d %s" % (count, filename)
73     print '-' * 50
74     print "%d out of %d (%.1f%%) had large diffstats" % (iffy, total, float(iffy)/total*100)
75     print "%d out of %d (%.1f%%) had clean diffstats" % (clean, total, float(clean)/total*100)
76
77 def parse_args(argv, baton):
78     usage = """usage: %prog research APP
79
80 Tells you how spectacularly an upgrade here will explode."""
81     parser = command.WizardOptionParser(usage)
82     parser.add_option("--limit", dest="limit", type="int",
83             default=None, help="Limit the number of autoinstalls to look at.")
84     parser.add_option("--sample", dest="sample", type="int", metavar="N",
85             default=None, help="Instead of researching all installs, research a random sample of N size.")
86     parser.add_option("--filter", dest="filter", type="int", metavar="N",
87             default=4, help="How many files are permitted in a diffstat before treating the install as having a 'large diffstat'")
88     baton.push(parser, "srv_path")
89     baton.push(parser, "versions_path")
90     options, args = parser.parse_all(argv)
91     if len(args) > 1:
92         parser.error("too many arguments")
93     if not args:
94         parser.error("must specify application to research")
95     return options, args
96