3 A script for reporting the results of an invirtibuild. Supports any
4 combination of the following reporting mechanisms.
6 Note that all strings are interpolated with a dictionary containing
9 build_id, commit, failed_stage, inserted_at, package,
10 pocket, principal, result, short_commit, traceback, version
14 To configure zephyr, add something like the following to your invirt config:
19 zephyr: &post_build_zepyhr
20 class: myclass [required]
21 instance: myinstance [optional]
22 zsig: myzsig [optional]
24 zephyr: *post_build_zephyr
28 To configure email notifications, add something like the following to your invirt config:
33 mail: &post_build_mail
34 to: myemail@example.com [required]
35 from: myemail@example.com [required]
36 subject: My Subject [optional]
38 mail: *post_build_mail
40 post_build values will be used when this script is invoked as
41 post-build, while failed_build values will be used when it is invoked
51 from email.mime import text
53 from invirt import common, database, builder
54 from invirt.config import structs as config
56 def build_completion_msg(succeeded, values, verbose=True, success=lambda x: x, failure=lambda x: x):
57 """Format a message reporting the results of a build"""
59 if not verbose and values['traceback'] is not None:
60 # TODO: better heuristic
61 values['traceback'] = textwrap.fill('\n'.join(values['traceback'].split('\n')[-2:]))
64 values['result'] = success(values['result'])
65 msg = """Build of %(package)s %(version)s in %(pocket)s %(result)s.
67 Branch %(pocket)s has been advanced to %(short_commit)s.
69 (Build %(build_id)s was submitted by %(principal)s at %(inserted_at)s.)""" % values
71 values['result'] = failure(values['result'])
72 msg = """Build of %(package)s %(version)s in %(pocket)s %(result)s while %(failed_stage)s.
76 (Build %(build_id)s was submitted by %(principal)s at %(inserted_at)s.)""" % values
80 POST_BUILD = 'post-build'
81 FAILED_BUILD = 'failed-build'
83 # Types of communication
88 message_generators = {
89 ZEPHYR : { POST_BUILD : build_completion_msg,
90 FAILED_BUILD : build_completion_msg },
91 MAIL : { POST_BUILD : build_completion_msg,
92 FAILED_BUILD : build_completion_msg }
96 m = re.sub('@', '@@', m)
97 m = re.sub('}', '@(})', m)
100 def zephyr_success(m):
101 return '}@{@color(green)%s}@{' % zephyr_escape(m)
103 def zephyr_failure(m):
104 return '}@{@color(red)%s}@{' % zephyr_escape(m)
107 parser = optparse.OptionParser('Usage: %prog [options] [arguments]')
108 opts, args = parser.parse_args()
109 prog = os.path.basename(sys.argv[0])
112 if prog == POST_BUILD:
113 hook_config = config.build.hooks.post_build
114 elif prog == FAILED_BUILD:
115 hook_config = config.build.hooks.failed_build
117 parser.error('hook script invoked with unrecognized name %s' % prog)
119 except common.InvirtConfigError:
120 print >>sys.stderr, 'No hook configuration found for %s.' % prog
123 if prog in [POST_BUILD, FAILED_BUILD]:
125 parser.set_usage('Usage: %prog [options] build_id')
129 build = database.Build.query().get(args[0])
130 short_commit = builder.canonicalize_commit(build.package, build.commit, shorten=True)
131 values = { 'build_id' : build.build_id,
132 'commit' : build.commit,
133 'failed_stage' : build.failed_stage,
134 'inserted_at' : build.inserted_at,
135 'package' : build.package,
136 'pocket' : build.pocket,
137 'principal' : build.principal,
138 'short_commit' : short_commit,
139 'traceback' : build.traceback,
140 'version' : build.version,
141 'default_instance' : 'build_%(build_id)s',
142 'default_subject' : 'XVM build %(result)s: %(package)s %(version)s in %(pocket)s'}
144 assert prog == POST_BUILD
145 values['result'] = 'succeeded'
148 assert prog == FAILED_BUILD
149 values['result'] = 'failed'
152 raise AssertionError('Impossible state')
155 zephyr_config = hook_config.zephyr
156 klass = zephyr_config['class'] % values
157 except common.InvirtConfigError:
158 print >>sys.stderr, 'No zephyr configuration specified for %s.' % prog
160 make_msg = message_generators[ZEPHYR][prog]
161 msg = '@{%s}' % make_msg(succeeded, values, verbose=False,
162 success=zephyr_success, failure=zephyr_failure)
163 instance = zephyr_config.get('instance', values['default_instance']) % values
164 zsig = zephyr_config.get('zsig', 'XVM Buildbot') % values
165 common.captureOutput(['zwrite', '-c', klass, '-i', instance, '-s',
166 zsig, '-d', '-m', msg],
167 stdout=None, stderr=None)
170 mail_config = hook_config.mail
171 to = mail_config.to % values
172 sender = mail_config['from'] % values
173 except common.InvirtConfigError:
174 print >>sys.stderr, 'No email configuration specified for %s.' % prog
176 make_msg = message_generators[MAIL][prog]
177 msg = make_msg(succeeded, values)
178 email = text.MIMEText(msg)
179 email['To'] = to % values
180 email['From'] = sender % values
181 email['Subject'] = mail_config.get('subject', values['default_subject']) % values
182 common.captureOutput(['sendmail', '-t'], email.as_string(),
183 stdout=None, stderr=None)
185 if __name__ == '__main__':