use buildstore function to get the cached status
[build-farm.git] / import-and-analyse.py
1 #!/usr/bin/python
2 # Write sqlite entries for test reports in the build farm
3 # Copyright (C) 2007-2010 Jelmer Vernooij <jelmer@samba.org>
4 # Copyright (C) 2007-2010 Andrew Bartlett <abartlet@samba.org>
5 # Published under the GNU GPL
6
7 """Script to parse build farm log files from the data directory, import
8 them into the database, add links to the oldrevs/ directory and send
9 some mail chastising the possible culprits when the build fails, based
10 on recent commits.
11 """
12
13 from buildfarm import (
14     BuildFarm,
15     hostdb,
16     )
17 import commands
18 from email.mime.text import MIMEText
19 import logging
20 import optparse
21 import os
22 import re
23 import smtplib
24
25 parser = optparse.OptionParser("import-and-analyse [options]")
26 parser.add_option("--dry-run", help="Will cause the script to send output to stdout instead of to sendmail.", action="store_true")
27 parser.add_option("--verbose", help="Be verbose", action="count")
28
29 (opts, args) = parser.parse_args()
30
31 UNPACKED_DIR = "/home/ftp/pub/unpacked"
32
33 # we open readonly here as only apache(www-run) has write access
34 buildfarm = BuildFarm()
35 db = buildfarm.builds
36 hostsdb = buildfarm.hostdb
37
38 smtp = smtplib.SMTP()
39 smtp.connect()
40
41 class Log(object):
42
43     def __init__(self):
44         self.change_log = None
45         self.committers = set()
46         self.authors = set()
47         self.recipients = None
48
49
50 def get_log_git(tree, cur, old):
51     cmd = "cd %s/%s && git log --pretty=full %s..%s ./" % (UNPACKED_DIR, tree, old, cur)
52
53     log = Log()
54
55     log.change_log = commands.getoutput(cmd)
56     #print log.change_log
57
58     # get the list of possible culprits
59     log2 = log.change_log
60
61     for m in re.findall("[\n]*Author: [^<]*<([^>]+)>\nCommit: [^<]*<([^>]+)>\n(.*)$", log.change_log):
62         author = m.group(1)
63         committer = m.group(2)
64
65         # handle cherry-picks from svnmirror repo
66         author = author.replace("0c0555d6-39d7-0310-84fc-f1cc0bd64818", "samba.org")
67
68         # for now only send reports to samba.org addresses.
69         if not "@samba.org" in author:
70             author = None
71
72         if author:
73             log.authors.add(author)
74         if committer:
75             log.committers.add(committer)
76
77     # Add a URL to the diffs for each change
78     log.change_log = re.sub("([\n]*commit ([0-9a-f]+))", "\\1\nhttp:\/\/build.samba.org\/?function=diff;tree=%s;revision=\\2" % tree, log.change_log)
79
80     all = set()
81     all.update(log.authors)
82     all.update(log.committers)
83     log.recipients = all
84     return log
85
86
87 def get_log(tree, cur, old):
88     treedir = os.path.join(UNPACKED_DIR, tree)
89
90     if os.path.exists(os.path.join(treedir, ".git")):
91         return get_log_git(tree, cur, old)
92     else:
93         raise Exception("Unknown vcs for %s" % treedir)
94
95
96 def check_and_send_mails(tree, host, compiler, cur, old):
97     t = buildfarm.trees[tree]
98
99     (cur_rev, cur_rev_timestamp) = cur.revision_details()
100     cur_status = cur.status()
101
102     (old_rev, old_rev_timestamp) = old.revision_details()
103     old_status = old.status()
104
105     if opts.dry_run:
106         print "rev=%s status=%s" % (cur_rev, cur_status)
107         print "old rev=%s status=%s" % (old_rev, old_status)
108
109     if not cur_status.regressed_since(old_status):
110         if opts.dry_run:
111             print "the build didn't get worse since %r" % old_status
112         return
113
114     log = get_log(tree, cur, old)
115     if not log:
116         if opts.dry_run:
117             print "no log"
118         return
119
120     recipients = ",".join(log.recipients.keys())
121
122     body = """
123 Broken build for tree %(tree)s on host %(host)s with compiler %(compiler)s
124
125 Tree %(tree)s is %(scm)s branch %(branch)s.
126
127 Build status for new revision %(cur_rev)s is %(cur_status)s
128 Build status for old revision %(old_rev)s was %(old_status)s
129
130 See http://build.samba.org/?function=View+Build;host=%(host)s;tree=%(tree)s;compiler=%(compiler)s
131
132 The build may have been broken by one of the following commits:
133
134 %(change_log)s
135     """ % {"tree": tree, "host": host, "compiler": compiler, "change_log": log.change_log, "scm": t.scm, "branch": t.branch,
136             "cur_rev": cur_rev, "old_rev": old_rev, "cur_status": cur_status, "old_status": old_status }
137
138     msg = MIMEText(body)
139     msg["Subject"] = "BUILD of %s:%s BROKEN on %s with %s AT REVISION %s" % (tree, t.branch, host, compiler, cur_rev)
140     msg["From"] = "\"Build Farm\" <build@samba.org>"
141     msg["To"] = recipients
142     smtp.send(msg["From"], [msg["To"]], msg.as_string())
143
144
145 for build in buildfarm.get_new_builds():
146     if opts.verbose >= 2:
147         print "Processing %s..." % build
148
149     db.upload_build(build)
150
151     (rev, commit_rev, rev_timestamp) = build.revision_details()
152
153     try:
154         prev_rev = db.get_previous_revision(build.tree, build.host, build.compiler, rev)
155     except hostdb.NoSuchBuild:
156         # Can't send a nastygram until there are 2 builds..
157         continue
158     else:
159         prev_build = db.get_build(build.tree, build.host, build.compiler, prev_rev)
160         check_and_send_mails(build.tree, build.host, build.compiler, build, prev_build)
161
162
163 smtp.quit()