Mercurial > bitten > bitten-test
changeset 245:a22ec8fce6c9
Store the reason(s) for build step failure in the database.
Requires DB upgrade.
author | cmlenz |
---|---|
date | Mon, 03 Oct 2005 21:15:31 +0000 |
parents | 1aa624af9ebb |
children | ebfe660f7cb1 |
files | bitten/build/javatools.py bitten/build/pythontools.py bitten/master.py bitten/model.py bitten/tests/model.py bitten/trac_ext/templates/bitten_build.cs bitten/trac_ext/web_ui.py bitten/upgrades.py scripts/build.py |
diffstat | 9 files changed, 87 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/bitten/build/javatools.py +++ b/bitten/build/javatools.py @@ -41,6 +41,7 @@ if err is not None: log.error(err) + error_logged = False log_elem = xmlio.Fragment() try: xml_log = xmlio.parse(logfile) @@ -57,9 +58,14 @@ else: collect_log_messages(child) collect_log_messages(xml_log) + + if 'error' in xml_log.attr: + ctxt.error(xml_log.attr['error']) + error_logged = True + except xmlio.ParseError, e: log.warning('Error parsing Ant XML log file (%s)', e) ctxt.log(log_elem) - if cmdline.returncode != 0: + if not error_logged and cmdline.returncode != 0: ctxt.error('Ant failed (%s)' % cmdline.returncode)
--- a/bitten/build/pythontools.py +++ b/bitten/build/pythontools.py @@ -38,6 +38,7 @@ cmdline = CommandLine(_python_path(ctxt), [ctxt.resolve(file_), command], cwd=ctxt.basedir) log_elem = xmlio.Fragment() + error_logged = False for out, err in cmdline.execute(): if out is not None: log.info(out) @@ -48,11 +49,15 @@ err = err[9:] level = 'warning' log.warning(err) + elif err.startswith('error: '): + ctxt.error(err[7:]) + error_logged = True else: log.error(err) log_elem.append(xmlio.Element('message', level=level)[err]) ctxt.log(log_elem) - if cmdline.returncode != 0: + + if not error_logged and cmdline.returncode != 0: ctxt.error('distutils failed (%s)' % cmdline.returncode) def exec_(ctxt, file_=None, module=None, function=None, output=None, args=None):
--- a/bitten/master.py +++ b/bitten/master.py @@ -267,6 +267,7 @@ step.status = BuildStep.FAILURE else: step.status = BuildStep.SUCCESS + step.errors += [err.gettext() for err in elem.children('error')] step.insert(db=db) for idx, log_elem in enumerate(elem.children('log')):
--- a/bitten/model.py +++ b/bitten/model.py @@ -501,6 +501,10 @@ Column('build', type='int'), Column('name'), Column('description'), Column('status', size=1), Column('started', type='int'), Column('stopped', type='int') + ], + Table('bitten_error', key=('build', 'step', 'orderno'))[ + Column('build', type='int'), Column('step'), Column('message'), + Column('orderno', type='int') ] ] @@ -509,7 +513,7 @@ FAILURE = 'F' def __init__(self, env, build=None, name=None, description=None, - status=None, started=None, stopped=None): + status=None, started=None, stopped=None, errors=None): """Initialize a new build step with the specified attributes. To actually create this build step in the database, the `insert` method @@ -522,8 +526,10 @@ self.status = status self.started = started self.stopped = stopped + self.errors = errors or [] + self._exists = False - exists = property(fget=lambda self: self.build is not None) + exists = property(fget=lambda self: self._exists) successful = property(fget=lambda self: self.status == BuildStep.SUCCESS) def delete(self, db=None): @@ -544,8 +550,12 @@ cursor = db.cursor() cursor.execute("DELETE FROM bitten_step WHERE build=%s AND name=%s", (self.build, self.name)) + cursor.execute("DELETE FROM bitten_error WHERE build=%s AND step=%s", + (self.build, self.name)) + if handle_ta: db.commit() + self._exists = False def insert(self, db=None): """Insert a new build step into the database.""" @@ -563,8 +573,16 @@ "started,stopped) VALUES (%s,%s,%s,%s,%s,%s)", (self.build, self.name, self.description or '', self.status, self.started or 0, self.stopped or 0)) + step_id = db.get_last_id(cursor, 'bitten_step') + if self.errors: + cursor.executemany("INSERT INTO bitten_error (build,step,message," + "orderno) VALUES (%s,%s,%s,%s)", + [(self.build, self.name, message, idx) + for idx, message in enumerate(self.errors)]) + if handle_ta: db.commit() + self._exists = True def fetch(cls, env, build, name, db=None): """Retrieve an existing build from the database by build ID and step @@ -579,9 +597,16 @@ row = cursor.fetchone() if not row: return None + step = BuildStep(env, build, name, row[0] or '', row[1], + row[2] and int(row[2]), row[3] and int(row[3])) + step._exists = True - return BuildStep(env, build, name, row[0] or '', row[1], - row[2] and int(row[2]), row[3] and int(row[3])) + cursor.execute("SELECT message FROM bitten_error WHERE build=%s " + "AND step=%s ORDER BY orderno", (build, name)) + for row in cursor: + step.errors.append(row[0] or '') + return step + fetch = classmethod(fetch) def select(cls, env, build=None, name=None, db=None): @@ -602,12 +627,11 @@ where = "" cursor = db.cursor() - cursor.execute("SELECT build,name,description,status,started,stopped " - "FROM bitten_step %s ORDER BY stopped" + cursor.execute("SELECT build,name FROM bitten_step %s ORDER BY stopped" % where, [wc[1] for wc in where_clauses]) - for build, name, description, status, started, stopped in cursor: - yield BuildStep(env, build, name, description or '', status, - started and int(started), stopped and int(stopped)) + for build, name in cursor: + yield BuildStep.fetch(env, build, name, db=db) + select = classmethod(select) @@ -883,4 +907,4 @@ schema = BuildConfig._schema + TargetPlatform._schema + Build._schema + \ BuildStep._schema + BuildLog._schema + Report._schema -schema_version = 6 +schema_version = 7
--- a/bitten/tests/model.py +++ b/bitten/tests/model.py @@ -340,6 +340,21 @@ self.assertEqual('Foo bar', step.description) self.assertEqual(BuildStep.SUCCESS, step.status) + def test_fetch_with_errors(self): + db = self.env.get_db_cnx() + cursor = db.cursor() + cursor.execute("INSERT INTO bitten_step VALUES (%s,%s,%s,%s,%s,%s)", + (1, 'test', 'Foo bar', BuildStep.SUCCESS, 0, 0)) + cursor.executemany("INSERT INTO bitten_error VALUES (%s,%s,%s,%s)", + [(1, 'test', 'Foo', 0), (1, 'test', 'Bar', 1)]) + + step = BuildStep.fetch(self.env, build=1, name='test') + self.assertEqual(1, step.build) + self.assertEqual('test', step.name) + self.assertEqual('Foo bar', step.description) + self.assertEqual(BuildStep.SUCCESS, step.status) + self.assertEqual(['Foo', 'Bar'], step.errors) + class BuildLogTestCase(unittest.TestCase):
--- a/bitten/trac_ext/templates/bitten_build.cs +++ b/bitten/trac_ext/templates/bitten_build.cs @@ -25,7 +25,14 @@ each:step = build.steps ?> <h2 id="<?cs var:step.name ?>"><?cs var:step.name ?> (<?cs var:step.duration ?>)</h2> - <p><?cs var:step.description ?></p> + <p><?cs var:step.description ?></p><?cs + if:len(step.errors) ?> + <div class="errors"> + <h3>Errors:</h3><ul class="errors"><?cs + each:error = step.errors ?><li><?cs var:error ?></li><?cs + /each ?></ul> + </div><?cs + /if ?> <div id="<?cs var:step.name ?>_tabs"> <div class="tab"><h3>Log</h3><div class="log"><?cs each:item = step.log ?><code class="<?cs var:item.level ?>"><?cs
--- a/bitten/trac_ext/web_ui.py +++ b/bitten/trac_ext/web_ui.py @@ -483,6 +483,7 @@ 'name': step.name, 'description': step.description, 'duration': pretty_timedelta(step.started, step.stopped), 'failed': step.status == BuildStep.FAILURE, + 'errors': step.errors, 'log': self._render_log(req, build, step), 'reports': self._render_reports(req, config, build, step) })
--- a/bitten/upgrades.py +++ b/bitten/upgrades.py @@ -252,10 +252,23 @@ cursor.execute("UPDATE bitten_report SET generator=%s " "WHERE id=%s", (mapping[generator], report_id)) +def add_error_table(env, db): + """Add the bitten_error table for recording step failure reasons.""" + from trac.db import Table, Column + + table = Table('bitten_error', key=('build', 'step', 'orderno'))[ + Column('build', type='int'), Column('step'), Column('message'), + Column('orderno', type='int') + ] + cursor = db.cursor() + for stmt in db.to_sql(table): + cursor.execute(stmt) + map = { 2: [add_log_table], 3: [add_recipe_to_config], 4: [add_config_to_reports], 5: [add_order_to_log, add_report_tables, xmldb_to_db], - 6: [normalize_file_paths, fixup_generators] + 6: [normalize_file_paths, fixup_generators], + 7: [add_error_table] }
--- a/scripts/build.py +++ b/scripts/build.py @@ -56,7 +56,7 @@ print '-->', step.id for type, category, generator, output in step.execute(recipe.ctxt): if type == Recipe.ERROR: - log.error('Failure in step "%s": %s', step.id, output) + log.error(output) elif type == Recipe.LOG and options.print_logs: output.write(sys.stdout, newlines=True) elif type == Recipe.REPORT and options.print_reports: