Allow /build/<checksum>
[amitay/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.build import (
14     MissingRevisionInfo,
15     NoSuchBuildError,
16     )
17 from buildfarm import BuildFarm
18 from buildfarm.web import build_uri
19 from email.mime.text import MIMEText
20 import logging
21 import optparse
22 import resource
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 resource.setrlimit(resource.RLIMIT_RSS, (300000, 300000))
32 resource.setrlimit(resource.RLIMIT_DATA, (300000, 300000))
33
34 buildfarm = BuildFarm(timeout=40.0)
35
36 smtp = smtplib.SMTP()
37 smtp.connect()
38
39 def check_and_send_mails(cur, old):
40     t = buildfarm.trees[cur.tree]
41
42     cur_rev = cur.revision_details()
43     cur_status = cur.status()
44
45     old_rev = old.revision_details()
46     old_status = old.status()
47
48     if not cur_status.regressed_since(old_status):
49         if opts.verbose >= 3:
50             print "... hasn't regressed since %s: %s" % (old_rev, old_status)
51         return
52
53     branch = t.get_branch()
54     recipients = set()
55     change_log = ""
56
57     for rev in branch.log(from_rev=cur.revision, exclude_revs=set([old.revision])):
58         recipients.add(rev.author)
59         recipients.add(rev.committer)
60         change_log += """
61 revision: %s
62 author: %s
63 committer: %s
64 message:
65     %s
66 """ % (rev.revision, rev.author, rev.committer, rev.message)
67
68     body = """
69 Broken build for tree %(tree)s on host %(host)s with compiler %(compiler)s
70
71 Tree %(tree)s is %(scm)s branch %(branch)s.
72
73 Build status for new revision %(cur_rev)s is %(cur_status)s
74 Build status for old revision %(old_rev)s was %(old_status)s
75
76 See %(build_link)s
77
78 The build may have been broken by one of the following commits:
79
80 %(change_log)s
81     """ % {
82         "tree": cur.tree, "host": cur.host, "compiler": cur.compiler,
83         "change_log": change_log,
84         "scm": t.scm,
85         "branch": t.branch,
86         "cur_rev": cur_rev,
87         "old_rev": old_rev,
88         "cur_status": cur_status,
89         "old_status": old_status,
90         "build_link": build_uri("http://build.samba.org/build.cgi", cur)
91         }
92
93     msg = MIMEText(body)
94     msg["Subject"] = "BUILD of %s:%s BROKEN on %s with %s AT REVISION %s" % (cur.tree, t.branch, cur.host, cur.compiler, cur_rev)
95     msg["From"] = "\"Build Farm\" <build@samba.org>"
96     msg["To"] = ",".join(recipients)
97     if not opts.dry_run:
98         smtp.sendmail(msg["From"], [msg["To"]], msg.as_string())
99     else:
100         print msg.as_string()
101
102
103 for build in buildfarm.get_new_builds():
104     if build in buildfarm.builds:
105         continue
106
107     if not opts.dry_run:
108         old_build = build
109         try:
110             build = buildfarm.builds.upload_build(old_build)
111         except MissingRevisionInfo:
112             print "No revision info in %r, skipping" % build
113             continue
114
115     try:
116         rev = build.revision_details()
117     except MissingRevisionInfo:
118         print "No revision info in %r, skipping" % build
119         continue
120
121     if opts.verbose >= 2:
122         print "%s... " % build,
123         print str(build.status())
124
125     try:
126         if opts.dry_run:
127             # Perhaps this is a dry run and rev is not in the database yet?
128             prev_rev = buildfarm.builds.get_latest_revision(build.tree, build.host, build.compiler)
129         else:
130             prev_rev = buildfarm.builds.get_previous_revision(build.tree, build.host, build.compiler, rev)
131     except NoSuchBuildError:
132         if opts.verbose >= 1:
133             print "Unable to find previous build for %s,%s,%s" % (build.tree, build.host, build.compiler)
134         # Can't send a nastygram until there are 2 builds..
135     else:
136         try:
137             assert prev_rev is not None
138             prev_build = buildfarm.builds.get_build(build.tree, build.host, build.compiler, prev_rev)
139         except NoSuchBuildError:
140             if opts.verbose >= 1:
141                 print "Previous build %s has disappeared" % prev_build
142         else:
143             check_and_send_mails(build, prev_build)
144
145     if not opts.dry_run:
146         old_build.remove()
147         buildfarm.commit()
148
149 smtp.quit()