[IMP] runbot: allow a parent to ignore some child builds

In a create config, a parent result is computed based on children
results

In some situations, it could be handy to ignore the result of some
sub-builds.

Example: the nightly tests are just the children of one nightly build
with a create config. The external tests are failing randomly and as a
consequence, the nightly result is always red. On the other hand,
keeping the test running, just to have logs is a good idea.

With this commit, a config_step of type create can be marked as
orphan_result, that way, the result is not taken into account in the
parent build result.
This commit is contained in:
Christophe Monniez 2019-05-21 18:05:21 +02:00
parent c24df5fac2
commit c49e4d21ad
7 changed files with 101 additions and 9 deletions

View File

@ -98,6 +98,7 @@ class runbot_build(models.Model):
config_id = fields.Many2one('runbot.build.config', 'Run Config', required=True, default=lambda self: self.env.ref('runbot.runbot_build_config_default', raise_if_not_found=False))
real_build = fields.Many2one('runbot.build', 'Real Build', help="duplicate_id or self", compute='_compute_real_build')
log_list = fields.Char('Comma separted list of step_ids names with logs', compute="_compute_log_list", store=True)
orphan_result = fields.Boolean('No effect on the parent result', default=False)
@api.depends('config_id')
def _compute_log_list(self): # storing this field because it will be access trhoug repo viewn and keep track of the list at create
@ -131,20 +132,23 @@ class runbot_build(models.Model):
# random note: need to count hidden in pending and testing build displayed in frontend
@api.depends('children_ids.global_result', 'local_result', 'duplicate_id.global_result')
@api.depends('children_ids.global_result', 'local_result', 'duplicate_id.global_result', 'children_ids.orphan_result')
def _compute_global_result(self):
for record in self: # looks like it's computed twice for children
if record.duplicate_id: # would like to avoid to add state as a depends only for this.
record.global_result = record.duplicate_id.global_result
elif not record.local_result:
elif record.local_result and record._get_result_score(record.local_result) >= record._get_result_score('ko'):
record.global_result = record.local_result
elif record._get_result_score(record.local_result) >= record._get_result_score('ko'):
record.global_result = record.local_result
elif record.children_ids:
children_result = record._get_worst_result([child.global_result for child in record.children_ids], max_res='ko')
record.global_result = record._get_worst_result([record.local_result, children_result])
else:
record.global_result = record.local_result
children_ids = [child for child in record.children_ids if not child.orphan_result]
if children_ids:
children_result = record._get_worst_result([child.global_result for child in children_ids], max_res='ko')
if record.local_result:
record.global_result = record._get_worst_result([record.local_result, children_result])
else:
record.global_result = children_result
else:
record.global_result = record.local_result
def _get_worst_result(self, results, max_res=False):
results = [result for result in results if result] # filter Falsy values

View File

@ -111,6 +111,7 @@ class ConfigStep(models.Model):
hide_build = fields.Boolean('Hide created build in frontend', default=True, tracking=True)
force_build = fields.Boolean("As a forced rebuild, don't use duplicate detection", default=False, tracking=True)
force_host = fields.Boolean('Use same host as parent for children', default=False, tracking=True) # future
make_orphan = fields.Boolean('No effect on the parent result', help='Created build result will not affect parent build result', default=False, tracking=True)
@api.constrains('python_code')
def _check_python_code(self):
@ -205,6 +206,7 @@ class ConfigStep(models.Model):
'subject': build.subject,
'modules': build.modules,
'hidden': self.hide_build,
'orphan_result': self.make_orphan,
})
build._log('create_build', 'created with config %s' % create_config.name, log_type='subbuild', path=str(children.id))

View File

@ -215,6 +215,7 @@
<tr><td t-attf-class="{{rowclass}}">
<a t-attf-href="/runbot/build/{{child.id}}" >Build <t t-esc="child.id"/></a>
with config <a t-attf-href="/web#id={{child.config_id.id}}&amp;view_type=form&amp;model=runbot.build.config"><t t-esc="child.config_id.name"/></a>
<t t-if="child.orphan_result"><i class="fa fa-chain-broken" title="Build result ignored for parent" /></t>
<t t-if="child.job"> Running step: <t t-esc="child.job"/></t>
<t t-if="child.global_state in ['testing', 'waiting']">
<i class="fa fa-spinner fa-spin"/>
@ -228,7 +229,8 @@
</td>
</tr>
</table>
<p t-if="build.parent_id">Child of <a t-attf-href="/runbot/build/#{build.parent_id.id}"><t t-esc="build.parent_id.dest"/></a></p>
<p t-if="build.parent_id">Child of <a t-attf-href="/runbot/build/#{build.parent_id.id}"><t t-esc="build.parent_id.dest"/></a>
<t t-if="build.orphan_result">&amp;nbsp;<i class="fa fa-chain-broken" title="Build result ignored for parent" />&amp;nbsp;Orphaned build, the result does not affect parent build result</t></p>
<p t-if="build.duplicate_id">Duplicate of <a t-attf-href="/runbot/build/#{build.duplicate_id.id}"><t t-esc="build.duplicate_id.dest"/></a></p>
<table class="table table-condensed table-striped">
<tr>

View File

@ -4,3 +4,4 @@ from . import test_build
from . import test_frontend
from . import test_schedule
from . import test_cron
from . import test_build_config_step

View File

@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
from odoo.tests import common
class TestBuildConfigStep(common.TransactionCase):
def setUp(self):
super(TestBuildConfigStep, self).setUp()
self.Repo = self.env['runbot.repo']
self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar'})
self.Branch = self.env['runbot.branch']
self.branch = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/heads/master'
})
self.branch_10 = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/heads/10.0'
})
self.branch_11 = self.Branch.create({
'repo_id': self.repo.id,
'name': 'refs/heads/11.0'
})
self.Build = self.env['runbot.build']
self.ConfigStep = self.env['runbot.build.config.step']
self.Config = self.env['runbot.build.config']
self.parent_build = self.Build.create({
'branch_id': self.branch.id,
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
'port': '1234',
})
def test_config_step_create_results(self):
""" Test child builds are taken into account"""
config_step = self.ConfigStep.create({
'name': 'test_step',
'job_type': 'create_build',
'number_builds': 2,
'make_orphan': False,
'force_build': True,
})
config = self.Config.create({'name': 'test_config'})
config_step.create_config_ids = [config.id]
config_step._create_build(self.parent_build, '/tmp/essai')
self.assertEqual(len(self.parent_build.children_ids), 2, 'Two sub-builds should have been generated')
# check that the result will be ignored by parent build
for child_build in self.parent_build.children_ids:
self.assertFalse(child_build.orphan_result)
child_build.local_result = 'ko'
self.assertEqual(child_build.global_result, 'ko')
self.assertEqual(self.parent_build.global_result, 'ko')
def test_config_step_create(self):
""" Test the config step of type create """
config_step = self.ConfigStep.create({
'name': 'test_step',
'job_type': 'create_build',
'number_builds': 2,
'make_orphan': True,
'force_build': True,
})
config = self.Config.create({'name': 'test_config'})
config_step.create_config_ids = [config.id]
config_step._create_build(self.parent_build, '/tmp/essai')
self.assertEqual(len(self.parent_build.children_ids), 2, 'Two sub-builds should have been generated')
# check that the result will be ignored by parent build
for child_build in self.parent_build.children_ids:
self.assertTrue(child_build.orphan_result, 'An orphan result config step should mark the build as orphan_result')
child_build.local_result = 'ko'
self.assertFalse(self.parent_build.global_result)

View File

@ -36,6 +36,7 @@
<field name="build_type" groups="base.group_no_one"/>
<field name="config_id" readonly="1"/>
<field name="config_id" groups="base.group_no_one"/>
<field name="orphan_result" readonly="1"/>
</group>
</sheet>
</form>

View File

@ -61,6 +61,7 @@
<field name="number_builds"/>
<field name="hide_build" groups="base.group_no_one"/>
<field name="force_build"/>
<field name="make_orphan"/>
</group>
</sheet>
</form>