diff --git a/runbot/models/build_config.py b/runbot/models/build_config.py
index 469d331f..ec777cb6 100644
--- a/runbot/models/build_config.py
+++ b/runbot/models/build_config.py
@@ -455,7 +455,7 @@ class ConfigStep(models.Model):
test_tags += self.test_tags.replace(' ', '').split(',')
if self.enable_auto_tags and not build.params_id.config_data.get('disable_auto_tags', False):
if grep(config_path, "[/module][:class]"):
- auto_tags = self.env['runbot.build.error']._disabling_tags()
+ auto_tags = self.env['runbot.build.error']._disabling_tags(build)
if auto_tags:
test_tags += auto_tags
diff --git a/runbot/models/build_error.py b/runbot/models/build_error.py
index 24301ee8..b25240e1 100644
--- a/runbot/models/build_error.py
+++ b/runbot/models/build_error.py
@@ -73,6 +73,8 @@ class BuildError(models.Model):
last_seen_build_id = fields.Many2one('runbot.build', compute='_compute_last_seen_build_id', string='Last Seen build', store=True)
last_seen_date = fields.Datetime(string='Last Seen Date', compute='_compute_seen_date', store=True)
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.")
+ tags_max_version_id = fields.Many2one('runbot.version', 'Tags Max version', help="Maximal version where the test tags will be applied.")
@api.constrains('test_tags')
def _check_test_tags(self):
@@ -80,6 +82,12 @@ class BuildError(models.Model):
if build_error.test_tags and '-' in build_error.test_tags:
raise ValidationError('Build error test_tags should not be negated')
+ @api.onchange('test_tags')
+ def _onchange_test_tags(self):
+ for record in self:
+ record.tags_min_version_id = min(record.version_ids, key=lambda rec: rec.number)
+ record.tags_max_version_id = max(record.version_ids, key=lambda rec: rec.number)
+
@api.model_create_multi
def create(self, vals_list):
cleaners = self.env['runbot.error.regex'].search([('re_type', '=', 'cleaning')])
@@ -263,14 +271,22 @@ class BuildError(models.Model):
return window_action
@api.model
- def _test_tags_list(self):
- active_errors = self.search([('test_tags', '!=', False)])
- test_tag_list = active_errors.mapped('test_tags')
+ def _test_tags_list(self, build_id=False):
+ version = build_id.params_id.version_id.number if build_id else False
+
+ def filter_tags(e):
+ if version:
+ min_v = e.tags_min_version_id.number or ''
+ max_v = e.tags_max_version_id.number or '~'
+ return min_v <= version and max_v >= version
+ return True
+
+ test_tag_list = self.search([('test_tags', '!=', False)]).filtered(filter_tags).mapped('test_tags')
return [test_tag for error_tags in test_tag_list for test_tag in (error_tags).split(',')]
@api.model
- def _disabling_tags(self):
- return ['-%s' % tag for tag in self._test_tags_list()]
+ def _disabling_tags(self, build_id=False):
+ return ['-%s' % tag for tag in self._test_tags_list(build_id)]
def _search_version(self, operator, value):
exclude_domain = []
diff --git a/runbot/tests/test_build_error.py b/runbot/tests/test_build_error.py
index 3729778d..d199e041 100644
--- a/runbot/tests/test_build_error.py
+++ b/runbot/tests/test_build_error.py
@@ -29,6 +29,16 @@ class TestBuildError(RunbotCase):
create_vals.update(vals)
return self.Build.create(create_vals)
+ def create_params(self, vals):
+ create_vals = {
+ 'version_id': self.version_13.id,
+ 'project_id': self.project.id,
+ 'config_id': self.default_config.id,
+ 'create_batch_id': self.dev_batch.id,
+ }
+ create_vals.update(vals)
+ return self.BuildParameters.create(create_vals)
+
def create_log(self, vals):
log_vals = {
'level': 'ERROR',
@@ -301,7 +311,7 @@ class TestBuildError(RunbotCase):
self.assertEqual(error_a.build_count, 1)
self.assertEqual(error_b.build_count, 2)
- def test_build_error_test_tags(self):
+ def test_build_error_test_tags_no_version(self):
build_a = self.create_test_build({'local_result': 'ko'})
build_b = self.create_test_build({'local_result': 'ko'})
@@ -309,34 +319,74 @@ class TestBuildError(RunbotCase):
'content': 'foo',
'build_ids': [(6, 0, [build_a.id])],
'random': True,
- 'active': True
+ 'active': True,
+ 'test_tags': 'foo,bar',
})
error_b = self.BuildError.create({
'content': 'bar',
'build_ids': [(6, 0, [build_b.id])],
'random': True,
- 'active': False
+ 'active': False,
+ 'test_tags': 'blah',
})
-
- error_a.test_tags = 'foo,bar'
- error_b.test_tags = 'blah'
- self.assertIn('foo', self.BuildError._test_tags_list())
- self.assertIn('bar', self.BuildError._test_tags_list())
self.assertIn('-foo', self.BuildError._disabling_tags())
self.assertIn('-bar', self.BuildError._disabling_tags())
# test that test tags on fixed errors are not taken into account
- self.assertNotIn('blah', self.BuildError._test_tags_list())
self.assertNotIn('-blah', self.BuildError._disabling_tags())
error_a.test_tags = False
error_b.active = True
error_b.parent_id = error_a.id
self.assertEqual(error_b.test_tags, False)
- self.assertEqual(self.BuildError._disabling_tags(), ['-blah',])
+ self.assertEqual(self.BuildError._disabling_tags(), ['-blah'])
+ 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'})
+
+ 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(
+ [
+ {
+ "content": "foobar",
+ "build_ids": [(6, 0, [build_v13.id])],
+ "test_tags": "every,where",
+ },
+ {
+ "content": "blah",
+ "build_ids": [(6, 0, [build_v17.id])],
+ "test_tags": "tag_17_up_to_master",
+ "tags_min_version_id": version_17.id,
+ },
+ {
+ "content": "spam",
+ "build_ids": [(6, 0, [build_v17.id])],
+ "test_tags": "tag_up_to_17",
+ "tags_max_version_id": version_17.id,
+ },
+ {
+ "content": "eggs",
+ "build_ids": [(6, 0, [build_saas_171.id])],
+ "test_tags": "tag_only_17.1",
+ "tags_min_version_id": version_saas_171.id,
+ "tags_max_version_id": version_saas_171.id,
+ },
+ ]
+ )
+
+ 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)))
def test_build_error_team_wildcards(self):
website_team = self.BuildErrorTeam.create({
diff --git a/runbot/views/build_error_views.xml b/runbot/views/build_error_views.xml
index 570c5818..89ab1a9d 100644
--- a/runbot/views/build_error_views.xml
+++ b/runbot/views/build_error_views.xml
@@ -25,6 +25,8 @@
+
+
@@ -147,8 +149,8 @@
runbot.build.error.tree
runbot.build.error
-
-
-
+
+
+
+
+