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
79 def submit_completion_msg(succeeded, values, verbose=True, success=lambda x: x, failure=lambda x: x):
82 values['result'] = success(values['result'])
84 values['result'] = failure(values['result'])
85 msg = """Submission of %(commit)s to be built in %(pocket)s %(result)s.
86 Build submitted by %(principal)s.""" % values
91 POST_BUILD = 'post-build'
92 FAILED_BUILD = 'failed-build'
93 POST_SUBMIT = 'post-submit'
94 FAILED_SUBMIT = 'failed-submit'
96 # Types of communication
101 message_generators = {
102 ZEPHYR : { POST_BUILD : build_completion_msg,
103 FAILED_BUILD : build_completion_msg,
104 POST_SUBMIT : submit_completion_msg,
105 FAILED_SUBMIT : submit_completion_msg },
106 MAIL : { POST_BUILD : build_completion_msg,
107 FAILED_BUILD : build_completion_msg,
108 POST_SUBMIT : submit_completion_msg,
109 FAILED_SUBMIT : submit_completion_msg }
112 def zephyr_escape(m):
113 m = re.sub('@', '@@', m)
114 m = re.sub('}', '@(})', m)
117 def zephyr_success(m):
118 return '}@{@color(green)%s}@{' % zephyr_escape(m)
120 def zephyr_failure(m):
121 return '}@{@color(red)%s}@{' % zephyr_escape(m)
124 parser = optparse.OptionParser('Usage: %prog [options] [arguments]')
125 opts, args = parser.parse_args()
126 prog = os.path.basename(sys.argv[0])
129 if prog == POST_BUILD:
130 hook_config = config.build.hooks.post_build
131 elif prog == FAILED_BUILD:
132 hook_config = config.build.hooks.failed_build
133 elif prog == POST_SUBMIT:
134 hook_config = config.build.hooks.post_submit
135 elif prog == FAILED_SUBMIT:
136 hook_config = config.build.hooks.failed_submit
138 parser.error('hook script invoked with unrecognized name %s' % prog)
140 except common.InvirtConfigError:
141 print >>sys.stderr, 'No hook configuration found for %s.' % prog
144 if prog in [POST_BUILD, FAILED_BUILD]:
146 parser.set_usage('Usage: %prog [options] build_id')
150 build = database.Build.query().get(args[0])
151 short_commit = builder.canonicalize_commit(build.package, build.commit, shorten=True)
152 values = { 'build_id' : build.build_id,
153 'commit' : build.commit,
154 'failed_stage' : build.failed_stage,
155 'inserted_at' : build.inserted_at,
156 'package' : build.package,
157 'pocket' : build.pocket,
158 'principal' : build.principal,
159 'short_commit' : short_commit,
160 'traceback' : build.traceback,
161 'version' : build.version,
162 'default_instance' : 'build_%(build_id)s',
163 'default_subject' : 'XVM build %(result)s: %(package)s %(version)s in %(pocket)s'}
165 assert prog == POST_BUILD
166 values['result'] = 'succeeded'
169 assert prog == FAILED_BUILD
170 values['result'] = 'failed'
172 elif prog in [POST_SUBMIT, FAILED_SUBMIT]:
174 parser.set_usage('Usage: %prog [options] pocket package commit principal')
177 values = { 'pocket' : args[0],
180 'principal' : args[3],
181 'default_instance' : 'submission',
182 'default_subject' : 'Submission %(result)s: %(package)s %(version)s in %(pocket)s'}
183 if prog == POST_SUBMIT:
184 values['result'] = 'succeeded'
187 values['result'] = 'failed'
190 raise AssertionError('Impossible state')
193 zephyr_config = hook_config.zephyr
194 klass = zephyr_config['class'] % values
195 except common.InvirtConfigError:
196 print >>sys.stderr, 'No zephyr configuration specified for %s.' % prog
198 make_msg = message_generators[ZEPHYR][prog]
199 msg = '@{%s}' % make_msg(succeeded, values, verbose=False,
200 success=zephyr_success, failure=zephyr_failure)
201 instance = zephyr_config.get('instance', values['default_instance']) % values
202 zsig = zephyr_config.get('zsig', 'XVM Buildbot') % values
203 common.captureOutput(['zwrite', '-c', klass, '-i', instance, '-s',
204 zsig, '-d', '-m', msg],
205 stdout=None, stderr=None)
208 mail_config = hook_config.mail
209 to = mail_config.to % values
210 sender = mail_config['from'] % values
211 except common.InvirtConfigError:
212 print >>sys.stderr, 'No email configuration specified for %s.' % prog
214 make_msg = message_generators[MAIL][prog]
215 msg = make_msg(succeeded, values)
216 email = text.MIMEText(msg)
217 email['To'] = to % values
218 email['From'] = sender % values
219 email['Subject'] = mail_config.get('subject', values['default_subject']) % values
220 common.captureOutput(['sendmail', '-t'], email.as_string(),
221 stdout=None, stderr=None)
223 if __name__ == '__main__':