[FIX] runbot: allow build parsing with same error

When a build that contains the same error that appears two times is
parsed, it crashes because of the unique constraint on build error link.

With this commit, only one link with the same error is created.
Two tests are added for the two cases:
- a new error appearing two times in a same build
- an existing error appearing two times in a same build
This commit is contained in:
Christophe Monniez 2024-01-25 15:10:59 +01:00 committed by xdo
parent 0b20a834c7
commit 3ff4787788
2 changed files with 25 additions and 13 deletions

View File

@ -204,6 +204,8 @@ class BuildError(models.Model):
@api.model
def _parse_logs(self, ir_logs):
if not ir_logs:
return
regexes = self.env['runbot.error.regex'].search([])
search_regs = regexes.filtered(lambda r: r.re_type == 'filter')
cleaning_regs = regexes.filtered(lambda r: r.re_type == 'cleaning')
@ -218,24 +220,19 @@ class BuildError(models.Model):
build_errors = self.env['runbot.build.error']
# add build ids to already detected errors
existing_errors = self.env['runbot.build.error'].search([('fingerprint', 'in', list(hash_dict.keys())), ('active', '=', True)])
existing_fingerprints = existing_errors.mapped('fingerprint')
build_errors |= existing_errors
for build_error in existing_errors:
logs = hash_dict[build_error.fingerprint]
self.env['runbot.build.error.link'].create([{
'build_id': rec.build_id.id,
'build_error_id': build_error.id,
'log_date': rec.create_date}
for rec in logs if rec.build_id not in build_error.build_ids])
# update filepath if it changed. This is optionnal and mainly there in case we adapt the OdooRunner log
if logs[0].path != build_error.file_path:
build_error.file_path = logs[0].path
build_error.function = logs[0].func
del hash_dict[build_error.fingerprint]
# create an error for the remaining entries
for fingerprint, logs in hash_dict.items():
if fingerprint in existing_fingerprints:
continue
new_build_error = self.env['runbot.build.error'].create({
'content': logs[0].message,
'module_name': logs[0].name.removeprefix('odoo.').removeprefix('addons.'),
@ -243,12 +240,17 @@ class BuildError(models.Model):
'function': logs[0].func,
})
build_errors |= new_build_error
self.env['runbot.build.error.link'].create([{
'build_id': rec.build_id.id,
'build_error_id': new_build_error.id,
'log_date': rec.create_date}
for rec in logs])
existing_fingerprints.append(fingerprint)
for build_error in build_errors:
logs = hash_dict[build_error.fingerprint]
for rec in logs:
if rec.build_id not in build_error.build_error_link_ids.build_id:
self.env['runbot.build.error.link'].create({
'build_id': rec.build_id.id,
'build_error_id': build_error.id,
'log_date': rec.create_date
})
if build_errors:
window_action = {

View File

@ -118,6 +118,7 @@ class TestBuildError(RunbotCase):
def test_build_scan(self):
IrLog = self.env['ir.logging']
ko_build = self.create_test_build({'local_result': 'ok', 'local_state': 'testing'})
ko_build_b = self.create_test_build({'local_result': 'ok', 'local_state': 'testing'})
ok_build = self.create_test_build({'local_result': 'ok', 'local_state': 'running'})
cleaner = self.env['runbot.error.regex'].create({
@ -144,6 +145,14 @@ class TestBuildError(RunbotCase):
# Test the build parse and ensure that an 'ok' build is not parsed
IrLog.create(log)
# As it happens that a same error could appear again in the same build, ensure that the parsing adds only one link
log.update({'create_date': fields.Datetime.from_string('2023-08-29 00:48:21')})
IrLog.create(log)
# now simulate another build with the same errors
log.update({'build_id': ko_build_b.id, 'create_date': fields.Datetime.from_string('2023-08-29 01:46:21')})
log.update({'build_id': ko_build_b.id, 'create_date': fields.Datetime.from_string('2023-08-29 01:48:21')})
log.update({'build_id': ok_build.id})
IrLog.create(log)
@ -151,6 +160,7 @@ class TestBuildError(RunbotCase):
self.assertEqual(ok_build.local_result, 'ok', 'Running build should not have gone ko after error log')
ko_build._parse_logs()
ko_build_b._parse_logs()
ok_build._parse_logs()
# build_error = self.BuildError.search([('build_ids', 'in', [ko_build.id])])
build_error = self.BuildErrorLink.search([('build_id.id', '=', ko_build.id)]).mapped('build_error_id')