From 3ff4787788fdb9f37bec50a9a44ecd687115e328 Mon Sep 17 00:00:00 2001 From: Christophe Monniez Date: Thu, 25 Jan 2024 15:10:59 +0100 Subject: [PATCH] [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 --- runbot/models/build_error.py | 28 +++++++++++++++------------- runbot/tests/test_build_error.py | 10 ++++++++++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/runbot/models/build_error.py b/runbot/models/build_error.py index d37179cd..a7909333 100644 --- a/runbot/models/build_error.py +++ b/runbot/models/build_error.py @@ -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 = { diff --git a/runbot/tests/test_build_error.py b/runbot/tests/test_build_error.py index e0df3860..009462fa 100644 --- a/runbot/tests/test_build_error.py +++ b/runbot/tests/test_build_error.py @@ -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')