mirror of
https://github.com/odoo/runbot.git
synced 2025-03-16 16:05:42 +07:00
[IMP] runbot: create a new build error when a fixed one reappear
When a build error appears with the same fingerprint as already known one which was supposedly fixed, the build is simply added to the known build error. In order to keep an eye on such reappearing bugs and keep the fixing history separated, this commit simply creates a new build_error. Old build errors with the same hash (or child_ids 's hashes) appears in a computed field error_history_ids.
This commit is contained in:
parent
54f0488b26
commit
b7df8566e4
@ -33,6 +33,7 @@ class RunbotBuildError(models.Model):
|
||||
parent_id = fields.Many2one('runbot.build.error', 'Linked to')
|
||||
child_ids = fields.One2many('runbot.build.error', 'parent_id', string='Child Errors')
|
||||
children_build_ids = fields.Many2many('runbot.build', compute='_compute_children_build_ids', string='Children builds')
|
||||
error_history_ids = fields.One2many('runbot.build.error', compute='_compute_error_history_ids', string='Old errors')
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
@ -69,6 +70,16 @@ class RunbotBuildError(models.Model):
|
||||
for build_error in self:
|
||||
build_error.children_build_ids = build_error.mapped('child_ids.build_ids')
|
||||
|
||||
@api.depends('fingerprint')
|
||||
def _compute_error_history_ids(self):
|
||||
for error in self:
|
||||
fingerprints = [error.fingerprint] + [rec.fingerprint for rec in error.child_ids]
|
||||
error.error_history_ids = self.search([('fingerprint', 'in', fingerprints), ('active', '=', False), ('id', '!=', error.id)])
|
||||
|
||||
@api.onchange('active')
|
||||
def _onchange_active(self):
|
||||
self.child_ids.write({'active': self.active})
|
||||
|
||||
@api.model
|
||||
def _digest(self, s):
|
||||
"""
|
||||
@ -91,14 +102,15 @@ class RunbotBuildError(models.Model):
|
||||
hash_dict[fingerprint].append(log)
|
||||
|
||||
# add build ids to already detected errors
|
||||
for build_error in self.env['runbot.build.error'].search([('fingerprint', 'in', list(hash_dict.keys()))]):
|
||||
for build_error in self.env['runbot.build.error'].search([('fingerprint', 'in', list(hash_dict.keys())), ('active', '=', True)]):
|
||||
for build in {rec.build_id for rec in hash_dict[build_error.fingerprint]}:
|
||||
build.build_error_ids += build_error
|
||||
del hash_dict[build_error.fingerprint]
|
||||
|
||||
fixed_errors_dict = {rec.fingerprint: rec for rec in self.env['runbot.build.error'].search([('fingerprint', 'in', list(hash_dict.keys())), ('active', '=', False)])}
|
||||
# create an error for the remaining entries
|
||||
for fingerprint, logs in hash_dict.items():
|
||||
self.env['runbot.build.error'].create({
|
||||
build_error = self.env['runbot.build.error'].create({
|
||||
'content': logs[0].message,
|
||||
'module_name': logs[0].name,
|
||||
'function': logs[0].func,
|
||||
|
@ -1,7 +1,9 @@
|
||||
from . import test_repo
|
||||
from . import test_build_error
|
||||
from . import test_branch
|
||||
from . import test_build
|
||||
from . import test_frontend
|
||||
from . import test_schedule
|
||||
from . import test_cron
|
||||
from . import test_build_config_step
|
||||
from . import test_build_error
|
||||
|
93
runbot/tests/test_build_error.py
Normal file
93
runbot/tests/test_build_error.py
Normal file
@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from unittest.mock import patch
|
||||
from odoo.tests import common
|
||||
|
||||
RTE_ERROR = """FAIL: TestUiTranslate.test_admin_tour_rte_translator
|
||||
Traceback (most recent call last):
|
||||
File "/data/build/odoo/addons/website/tests/test_ui.py", line 89, in test_admin_tour_rte_translator
|
||||
self.start_tour("/", 'rte_translator', login='admin', timeout=120)
|
||||
File "/data/build/odoo/odoo/tests/common.py", line 1062, in start_tour
|
||||
res = self.browser_js(url_path=url_path, code=code, ready=ready, **kwargs)
|
||||
File "/data/build/odoo/odoo/tests/common.py", line 1046, in browser_js
|
||||
self.fail('%s\n%s' % (message, error))
|
||||
AssertionError: The test code "odoo.startTour('rte_translator')" failed
|
||||
Tour rte_translator failed at step click language dropdown (trigger: .js_language_selector .dropdown-toggle)
|
||||
"""
|
||||
|
||||
class TestBuildError(common.TransactionCase):
|
||||
|
||||
def create_build(self, vals):
|
||||
create_vals = {
|
||||
'branch_id': self.branch.id,
|
||||
'name': 'deadbeaf0000ffffffffffffffffffffffffffff',
|
||||
'port': '1234',
|
||||
'local_result': 'ok'
|
||||
}
|
||||
create_vals.update(vals)
|
||||
return self.Build.create(create_vals)
|
||||
|
||||
|
||||
@patch('odoo.addons.runbot.models.build.runbot_build._get_params')
|
||||
def setUp(self, mock_get_params):
|
||||
super(TestBuildError, self).setUp()
|
||||
repo = self.env['runbot.repo'].create({'name': 'bla@example.com:foo/bar'})
|
||||
self.branch = self.env['runbot.branch'].create({
|
||||
'repo_id': repo.id,
|
||||
'name': 'refs/heads/master'
|
||||
})
|
||||
|
||||
self.Build = self.env['runbot.build']
|
||||
self.BuildError = self.env['runbot.build.error']
|
||||
|
||||
@patch('odoo.addons.runbot.models.build.runbot_build._get_params')
|
||||
def test_build_scan(self, mock_get_params):
|
||||
IrLog = self.env['ir.logging']
|
||||
ko_build = self.create_build({'local_result': 'ko'})
|
||||
ok_build = self.create_build({'local_result': 'ok'})
|
||||
|
||||
log = {'message': RTE_ERROR,
|
||||
'build_id': ko_build.id,
|
||||
'level': 'ERROR',
|
||||
'type': 'server',
|
||||
'name': 'test-build-error-name',
|
||||
'path': 'test-build-error-path',
|
||||
'func': 'test-build-error-func',
|
||||
'line': 1,
|
||||
}
|
||||
|
||||
# Test the build parse and ensure that an 'ok' build is not parsed
|
||||
IrLog.create(log)
|
||||
log.update({'build_id': ok_build.id})
|
||||
IrLog.create(log)
|
||||
ko_build._parse_logs()
|
||||
ok_build._parse_logs()
|
||||
build_error = self.BuildError.search([('build_ids','in', [ko_build.id])])
|
||||
self.assertIn(ko_build, build_error.build_ids, 'The parsed build should be added to the runbot.build.error')
|
||||
self.assertFalse(self.BuildError.search([('build_ids','in', [ok_build.id])]), 'A successful build should not associated to a runbot.build.error')
|
||||
|
||||
# Test that build with same error is added to the errors
|
||||
ko_build_same_error = self.create_build({'local_result': 'ko'})
|
||||
log.update({'build_id': ko_build_same_error.id})
|
||||
IrLog.create(log)
|
||||
ko_build_same_error._parse_logs()
|
||||
self.assertIn(ko_build_same_error, build_error.build_ids, 'The parsed build should be added to the existing runbot.build.error')
|
||||
|
||||
# Test that line numbers does not interfere with error recognition
|
||||
ko_build_diff_number = self.create_build({'local_result': 'ko'})
|
||||
rte_diff_numbers = RTE_ERROR.replace('89','100').replace('1062','1000').replace('1046', '4610')
|
||||
log.update({'build_id': ko_build_diff_number.id, 'message': rte_diff_numbers})
|
||||
IrLog.create(log)
|
||||
ko_build_diff_number._parse_logs()
|
||||
self.assertIn(ko_build_diff_number, build_error.build_ids, 'The parsed build with different line numbers in error should be added to the runbot.build.error')
|
||||
|
||||
# Test that when an error re-appears after the bug has been fixed,
|
||||
# a new build error is created, with the old one linked
|
||||
build_error.active = False
|
||||
ko_build_new = self.create_build({'local_result': 'ko'})
|
||||
log.update({'build_id': ko_build_new.id})
|
||||
IrLog.create(log)
|
||||
ko_build_new._parse_logs()
|
||||
self.assertNotIn(ko_build_new, build_error.build_ids, 'The parsed build should not be added to a fixed runbot.build.error')
|
||||
new_build_error = self.BuildError.search([('build_ids','in', [ko_build_new.id])])
|
||||
self.assertIn(ko_build_new, new_build_error.build_ids, 'The parsed build with a re-apearing error should generate a new runbot.build.error')
|
||||
self.assertIn(build_error, new_build_error.error_history_ids, 'The old error should appear in history')
|
@ -44,6 +44,18 @@
|
||||
<field name="build_url" widget="url" readonly="1" text="View build"/>
|
||||
</tree>
|
||||
</field>
|
||||
<label for="error_history_ids" string="Error history"/>
|
||||
<field name="error_history_ids" widget="one2many" options="{'not_delete': True, 'no_create': True}">
|
||||
<tree>
|
||||
<field name="create_date"/>
|
||||
<field name="module_name"/>
|
||||
<field name="summary"/>
|
||||
<field name="random"/>
|
||||
<field name="build_count"/>
|
||||
<field name="responsible"/>
|
||||
<field name="fixing_commit"/>
|
||||
</tree>
|
||||
</field>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
|
Loading…
Reference in New Issue
Block a user