Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion runbot/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
'author': "Odoo SA",
'website': "http://runbot.odoo.com",
'category': 'Website',
'version': '5.12',
'version': '5.13',
'application': True,
'depends': ['base', 'base_automation', 'website'],
'data': [
Expand Down
9 changes: 9 additions & 0 deletions runbot/migrations/18.0.5.13/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def migrate(cr, version):
cr.execute('ALTER TABLE runbot_build_error_link ADD COLUMN batch_id INT')
cr.execute('ALTER TABLE runbot_build_error_link ADD COLUMN batch_date TIMESTAMP WITHOUT TIME ZONE')
cr.execute('''
UPDATE runbot_build_error_link SET batch_id = batch.id, batch_date = batch.create_date
FROM runbot_batch as batch
JOIN runbot_build build on build.create_batch_id = batch.id
JOIN runbot_build_error_link as link on link.build_id = build.id
''')
6 changes: 4 additions & 2 deletions runbot/models/build_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class BuildErrorLink(models.Model):
build_id = fields.Many2one('runbot.build', required=True, index=True)
error_content_id = fields.Many2one('runbot.build.error.content', required=True, index=True, ondelete='cascade')
log_date = fields.Datetime(string='Log date')
batch_id = fields.Many2one('runbot.batch', related='build_id.create_batch_id', string='Batch', store=True, readonly=True)
batch_date = fields.Datetime(related='batch_id.create_date', string='Batch date', store=True, readonly=True)
host = fields.Char(related='build_id.host')
dest = fields.Char(related='build_id.dest')
version_id = fields.Many2one(related='build_id.version_id')
Expand Down Expand Up @@ -75,8 +77,8 @@ def _compute_seen(self):
if error_link_ids:
first_error_link = error_link_ids[0]
last_error_link = error_link_ids[-1]
record.first_seen_date = first_error_link.log_date
record.last_seen_date = last_error_link.log_date
record.first_seen_date = first_error_link.batch_date
record.last_seen_date = last_error_link.batch_date
record.first_seen_build_id = first_error_link.build_id
record.last_seen_build_id = last_error_link.build_id
record.build_count = len(error_link_ids.build_id)
Expand Down
70 changes: 37 additions & 33 deletions runbot/tests/test_build_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ def setUpClass(cls):
cls.x_test_class = cls.env['ir.model.fields'].search([('name', '=', 'x_test_class'), ('model_id', '=', model)], limit=1)
cls.x_test_method = cls.env['ir.model.fields'].search([('name', '=', 'x_test_method'), ('model_id', '=', model)], limit=1)

def create_test_build(self, vals):
def create_test_build(self, vals, params_vals=None):
params = self.create_params(params_vals or {})
create_vals = {
'params_id': self.base_params.id,
'params_id': params.id,
'port': '1234',
'local_result': 'ok'
}
Expand Down Expand Up @@ -380,48 +381,51 @@ def test_build_scan(self):

def test_seen_date(self):
# create all the records before the tests to evaluate compute dependencies
build_a = self.create_test_build({'local_result': 'ok', 'local_state': 'testing'})
first_seen_date = fields.Datetime.from_string('2023-08-29 00:46:21')
self.create_log({'create_date': first_seen_date, 'message': RTE_ERROR, 'build_id': build_a.id})

build_b = self.create_test_build({'local_result': 'ok', 'local_state': 'testing'})
new_seen_date = fields.Datetime.from_string('2023-08-29 02:46:21')
self.create_log({'create_date': new_seen_date, 'message': RTE_ERROR, 'build_id': build_b.id})

build_c = self.create_test_build({'local_result': 'ok', 'local_state': 'testing'})
child_seen_date = fields.Datetime.from_string('2023-09-01 12:00:00')
self.create_log({'create_date': child_seen_date, 'message': 'Fail: foo bar error', 'build_id': build_c.id})
trigger = self.Trigger.create({
'name': 'test-trigger',
'batch_dependent': True,
'project_id': self.project.id,
'config_id': self.default_config.id,
})
batch_date = fields.Datetime.from_string('2023-08-28 19:00:01')
log_date = fields.Datetime.from_string('2023-08-29 00:46:21')
self.fist_batch = self.Batch.create({
'bundle_id': self.dev_bundle.id,
})
self.env.cr.execute('UPDATE runbot_batch SET create_date = %s WHERE id = %s', (batch_date, self.fist_batch.id)) # Update in sql to avoid getting removed
self.fist_batch.invalidate_recordset()
self.assertEqual(self.fist_batch.create_date, batch_date, 'Batch create date should be set')

build_d = self.create_test_build({'local_result': 'ok', 'local_state': 'testing'})
new_child_seen_date = fields.Datetime.from_string('2023-09-02 12:00:00')
self.create_log({'create_date': new_child_seen_date, 'message': 'Fail: foo bar error', 'build_id': build_d.id})
build_a = self.create_test_build({'local_result': 'ok', 'local_state': 'testing'}, {'create_batch_id': self.fist_batch.id, 'trigger_id': trigger.id})
self.assertEqual(build_a.create_batch_id, self.fist_batch, 'Build should be linked to the batch')
self.create_log({'create_date': log_date, 'message': RTE_ERROR, 'build_id': build_a.id})

build_a._parse_logs()
build_error_a = build_a.build_error_ids
self.assertEqual(build_error_a.first_seen_date, first_seen_date)
self.assertEqual(build_error_a.first_seen_build_id, build_a)
self.assertEqual(build_error_a.last_seen_date, first_seen_date)
self.assertEqual(build_error_a.first_seen_build_id.create_batch_id, self.fist_batch)
self.assertEqual(build_error_a.first_seen_date, batch_date)
self.assertEqual(build_error_a.last_seen_build_id, build_a)
self.assertEqual(build_error_a.last_seen_build_id.create_batch_id, self.fist_batch)
self.assertEqual(build_error_a.last_seen_date, batch_date)

new_batch_date = fields.Datetime.from_string('2023-08-29 19:00:01')
new_log_date = fields.Datetime.from_string('2023-08-30 00:48:21')
self.new_batch = self.Batch.create({
'bundle_id': self.dev_bundle.id,
})
self.env.cr.execute('UPDATE runbot_batch SET create_date = %s WHERE id = %s', (new_batch_date, self.new_batch.id)) # Update in sql to avoid getting removed
self.new_batch.invalidate_recordset()
self.assertEqual(self.new_batch.create_date, new_batch_date, 'New batch create date should be set')

build_b = self.create_test_build({'local_result': 'ok', 'local_state': 'testing'}, {'create_batch_id': self.new_batch.id, 'trigger_id': trigger.id})
self.create_log({'create_date': new_log_date, 'message': RTE_ERROR, 'build_id': build_b.id})

# a new build with the same error should be the last seen
build_b._parse_logs()
self.assertEqual(build_error_a.last_seen_date, new_seen_date)
self.assertEqual(build_error_a.last_seen_date, new_batch_date)
self.assertEqual(build_error_a.last_seen_build_id, build_b)

# a new build error is linked to the current one
build_c._parse_logs()
build_error_c = build_c.build_error_ids
self.assertNotIn(build_c, build_error_a.build_ids)
build_error_a._merge(build_error_c)
self.assertIn(build_c, build_error_a.build_ids)
self.assertEqual(build_error_a.last_seen_date, child_seen_date)
self.assertEqual(build_error_a.last_seen_build_id, build_c)

# a new build appears in the linked error
build_d._parse_logs()
self.assertEqual(build_error_a.last_seen_date, new_child_seen_date)
self.assertEqual(build_error_a.last_seen_build_id, build_d)

def test_build_error_links(self):
build_a = self.create_test_build({'local_result': 'ko'})
build_b = self.create_test_build({'local_result': 'ko'})
Expand Down
2 changes: 1 addition & 1 deletion runbot/views/build_error_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
</group>
<group>
<group name="fixer_info" string="Fixing" col="2">
<field name="breaking_bundle_url" invisible="not breaking_pr_id"/>
<field name="responsible"/>
<field name="customer"/>
<field name="team_id"/>
Expand All @@ -50,6 +49,7 @@
<field name="random"/>
<field name="breaking_pr_id"/>
<field name="breaking_pr_url" widget="pull_request_url" invisible="not breaking_pr_id"/>
<field name="breaking_bundle_url" invisible="not breaking_pr_id"/>
<field name="first_seen_date" widget="frontend_url" options="{'link_field': 'first_seen_build_id'}"/>
<field name="last_seen_date" widget="frontend_url" options="{'link_field': 'last_seen_build_id'}"/>
<field name="first_seen_build_id" invisible="True"/>
Expand Down