From 9377df5dd0c5cd1535c826c6760fc9e62de6244e Mon Sep 17 00:00:00 2001 From: Xavier-Do Date: Tue, 24 Dec 2024 14:30:40 +0100 Subject: [PATCH] [IMP] runbot: improve min version management --- runbot/__manifest__.py | 2 +- runbot/migrations/18.0.5.9/post-migration.py | 21 +++++++++++++ runbot/models/build_error.py | 24 ++++++++++++++- runbot/tests/test_build_error.py | 32 +++++++++++++++++--- 4 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 runbot/migrations/18.0.5.9/post-migration.py diff --git a/runbot/__manifest__.py b/runbot/__manifest__.py index ad37f5c73..25a07b90a 100644 --- a/runbot/__manifest__.py +++ b/runbot/__manifest__.py @@ -6,7 +6,7 @@ 'author': "Odoo SA", 'website': "http://runbot.odoo.com", 'category': 'Website', - 'version': '5.8', + 'version': '5.9', 'application': True, 'depends': ['base', 'base_automation', 'website'], 'data': [ diff --git a/runbot/migrations/18.0.5.9/post-migration.py b/runbot/migrations/18.0.5.9/post-migration.py new file mode 100644 index 000000000..3ac2307a0 --- /dev/null +++ b/runbot/migrations/18.0.5.9/post-migration.py @@ -0,0 +1,21 @@ +def migrate(cr, version): + cr.execute(""" + WITH helper AS + ( + SELECT v.id, + ( + SELECT v2.id + FROM runbot_version v2 + WHERE Coalesce(v2.SEQUENCE, 9999) <= Coalesce(v.SEQUENCE, 9999) + AND v2.number < v.number + ORDER BY v2.SEQUENCE DESC, + v2.number DESC limit 1 ) AS v_excluded + FROM runbot_version v + ORDER BY v.SEQUENCE DESC, + v.NUMBER DESC ) + UPDATE runbot_build_error + SET tags_min_version_excluded_id = h.v_excluded + FROM helper h + WHERE h.id = tags_min_version_id; + """) + cr.execute("""ALTER TABLE runbot_build_error DROP COLUMN tags_min_version_id;""") diff --git a/runbot/models/build_error.py b/runbot/models/build_error.py index 301272276..9b544c8eb 100644 --- a/runbot/models/build_error.py +++ b/runbot/models/build_error.py @@ -100,7 +100,8 @@ class BuildError(models.Model): fixing_pr_url = fields.Char('Fixing PR url', related='fixing_pr_id.branch_url') test_tags = fields.Char(string='Test tags', help="Comma separated list of test_tags to use to reproduce/remove this error", tracking=True) - tags_min_version_id = fields.Many2one('runbot.version', 'Tags Min version', help="Minimal version where the test tags will be applied.", tracking=True) + tags_min_version_excluded_id = fields.Many2one('runbot.version', 'Tag min version (excluded)') + tags_min_version_id = fields.Many2one('runbot.version', 'Tags Min version', compute="_compute_tags_min_version_id", inverse="_inverse_tags_min_version_id", help="Minimal version where the test tags will be applied.", tracking=True) tags_max_version_id = fields.Many2one('runbot.version', 'Tags Max version', help="Maximal version where the test tags will be applied.", tracking=True) common_qualifiers = JsonDictField('Common Qualifiers', compute='_compute_common_qualifiers', store=True, help="Minimal qualifiers in common needed to link error content.") @@ -121,6 +122,27 @@ class BuildError(models.Model): random = fields.Boolean('Random', compute="_compute_random", store=True) + @api.constrains('tags_min_version_id', 'tags_max_version_id') + def _check_min_max_version(self): + for build_error in self: + if build_error.tags_min_version_id and build_error.tags_max_version_id and build_error.tags_min_version_id.number >= build_error.tags_max_version_id.number: + raise ValidationError('Tags Min version should be lower than Tags Max version') + + def _inverse_tags_min_version_id(self): + all_versions = self.env['runbot.version'].search([]).sorted(lambda rec: (rec.sequence, rec.number), reverse=True) + for records in self: + records.tags_min_version_excluded_id = False + if records.tags_min_version_id: + records.tags_min_version_excluded_id = next((version for version in all_versions if version.number < records.tags_min_version_id.number), False) + + @api.depends('tags_min_version_id') + def _compute_tags_min_version_id(self): + all_versions = self.env['runbot.version'].search([]).sorted(lambda rec: (rec.sequence, rec.number)) + for records in self: + records.tags_min_version_id = False + if records.tags_min_version_excluded_id: + records.tags_min_version_id = next((version for version in all_versions if version.number > records.tags_min_version_excluded_id.number), False) + @api.depends('build_error_link_ids') def _compute_unique_build_error_link_ids(self): for record in self: diff --git a/runbot/tests/test_build_error.py b/runbot/tests/test_build_error.py index 6f55fd9f9..ee2e2c3f0 100644 --- a/runbot/tests/test_build_error.py +++ b/runbot/tests/test_build_error.py @@ -456,16 +456,16 @@ def test_build_error_test_tags_no_version(self): self.assertNotIn('-blah', self.BuildError._disabling_tags()) def test_build_error_test_tags_min_max_version(self): - version_17 = self.Version.create({'name': '17.0'}) - version_saas_171 = self.Version.create({'name': 'saas-17.1'}) - version_master = self.Version.create({'name': 'master'}) + version_17 = self.env['runbot.version']._get('17.0') + version_saas_171 = self.env['runbot.version']._get('saas-17.1') + version_master = self.env['runbot.version']._get('master') build_v13 = self.create_test_build({'local_result': 'ko'}) build_v17 = self.create_test_build({'local_result': 'ko', 'params_id': self.create_params({'version_id': version_17.id}).id}) build_saas_171 = self.create_test_build({'local_result': 'ko', 'params_id': self.create_params({'version_id': version_saas_171.id}).id}) build_master = self.create_test_build({'local_result': 'ko', 'params_id': self.create_params({'version_id': version_master.id}).id}) - self.BuildError.create( + errors = self.BuildError.create( [ { "content": "foobar", @@ -494,11 +494,35 @@ def test_build_error_test_tags_min_max_version(self): ] ) + self.assertEqual(errors[1].tags_min_version_id, version_17) + self.assertEqual(errors[3].tags_min_version_id, version_saas_171) + + self.assertEqual(errors[3].tags_min_version_excluded_id, version_17, f'{errors[3].tags_min_version_excluded_id.name} does not match version_17') + self.assertEqual(sorted(['-every', '-where', '-tag_17_up_to_master', '-tag_up_to_17', '-tag_only_17.1']), sorted(self.BuildError._disabling_tags()), "Should return the whole list without parameters") self.assertEqual(sorted(['-every', '-where', '-tag_up_to_17']), sorted(self.BuildError._disabling_tags(build_v13))) self.assertEqual(sorted(['-every', '-where', '-tag_up_to_17', '-tag_17_up_to_master']), sorted(self.BuildError._disabling_tags(build_v17))) self.assertEqual(sorted(['-every', '-where', '-tag_17_up_to_master', '-tag_only_17.1']), sorted(self.BuildError._disabling_tags(build_saas_171))) self.assertEqual(sorted(['-every', '-where', '-tag_17_up_to_master']), sorted(self.BuildError._disabling_tags(build_master))) + error_master = self.BuildError.create([{ + "content": "blah", + "build_ids": [(6, 0, [build_v17.id])], + "test_tags": "tag_master_only", + "tags_max_version_id": version_master.id, + "tags_min_version_id": version_master.id, + }]) + + self.assertEqual(error_master.tags_min_version_id, version_master) + self.assertEqual(error_master.tags_max_version_id, version_master) + self.assertEqual(error_master.tags_min_version_excluded_id, version_saas_171) + + version_99 = self.env['runbot.version']._get('99.0') + self.env.flush_all() + self.env.cache.invalidate() + build_v99 = self.create_test_build({'local_result': 'ko', 'params_id': self.create_params({'version_id': version_99.id}).id}) + self.assertEqual(error_master.tags_min_version_excluded_id, version_saas_171, "excluded id shouldn't have changed") + self.assertEqual(error_master.tags_min_version_id, version_99) + self.assertEqual(sorted(['-every', '-where', '-tag_17_up_to_master', '-tag_master_only']), sorted(self.BuildError._disabling_tags(build_v99))) def test_build_error_test_tags_fixing_pr(self): fix_commit = self.env['runbot.commit'].create({