[IMP] runbot: add trigger cross dependency

This commit is contained in:
Xavier-Do 2024-03-29 11:12:27 +01:00 committed by Christophe Monniez
parent 86b88d86c8
commit 9eb7f0d577
16 changed files with 237 additions and 47 deletions

View File

@ -6,7 +6,7 @@
'author': "Odoo SA", 'author': "Odoo SA",
'website': "http://runbot.odoo.com", 'website': "http://runbot.odoo.com",
'category': 'Website', 'category': 'Website',
'version': '5.5', 'version': '5.6',
'application': True, 'application': True,
'depends': ['base', 'base_automation', 'website'], 'depends': ['base', 'base_automation', 'website'],
'data': [ 'data': [

View File

@ -232,6 +232,7 @@ class Runbot(Controller):
batch = bundle.sudo()._force() batch = bundle.sudo()._force()
batch._log('Batch forced by %s', request.env.user.name) batch._log('Batch forced by %s', request.env.user.name)
batch._prepare(auto_rebase) batch._prepare(auto_rebase)
batch._process()
return werkzeug.utils.redirect('/runbot/batch/%s' % batch.id) return werkzeug.utils.redirect('/runbot/batch/%s' % batch.id)
@route(['/runbot/batch/<int:batch_id>'], website=True, auth='public', type='http', sitemap=False) @route(['/runbot/batch/<int:batch_id>'], website=True, auth='public', type='http', sitemap=False)

View File

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
import logging
def migrate(cr, version):
cr.execute('ALTER TABLE runbot_build ADD COLUMN create_batch_id INT')

View File

@ -109,14 +109,16 @@ class Batch(models.Model):
def _process(self): def _process(self):
processed = self.browse() processed = self.browse()
for batch in self: for batch in self:
if batch.state == 'preparing' and batch.last_update < fields.Datetime.now() - datetime.timedelta(seconds=60): if batch.state == 'preparing' and batch.last_update <= fields.Datetime.now() - datetime.timedelta(seconds=60):
batch._prepare() batch._prepare()
processed |= batch processed |= batch
elif batch.state == 'ready' and all(slot.build_id.global_state in (False, 'running', 'done') for slot in batch.slot_ids): if batch.state == 'ready':
_logger.info('Batch %s is done', self.id) self._start_builds()
batch._log('Batch done') if all(slot.build_id.global_state in (False, 'running', 'done') for slot in batch.slot_ids):
batch.state = 'done' _logger.info('Batch %s is done', self.id)
processed |= batch batch._log('Batch done')
batch.state = 'done'
processed |= batch
return processed return processed
def _create_build(self, params): def _create_build(self, params):
@ -326,7 +328,6 @@ class Batch(models.Model):
for commit_link in self.commit_link_ids: for commit_link in self.commit_link_ids:
commit_link.commit_id = commit_link.commit_id._rebase_on(commit_link.base_commit_id) commit_link.commit_id = commit_link.commit_id._rebase_on(commit_link.base_commit_id)
commit_link_by_repos = {commit_link.commit_id.repo_id.id: commit_link for commit_link in self.commit_link_ids} commit_link_by_repos = {commit_link.commit_id.repo_id.id: commit_link for commit_link in self.commit_link_ids}
bundle_repos = bundle.branch_ids.mapped('remote_id.repo_id')
version_id = self.bundle_id.version_id.id version_id = self.bundle_id.version_id.id
project_id = self.bundle_id.project_id.id project_id = self.bundle_id.project_id.id
trigger_customs = {} trigger_customs = {}
@ -360,19 +361,11 @@ class Batch(models.Model):
params = self.env['runbot.build.params'].create(params_value) params = self.env['runbot.build.params'].create(params_value)
build = self.env['runbot.build']
link_type = 'created'
force_trigger = trigger_custom and trigger_custom.start_mode == 'force'
skip_trigger = (trigger_custom and trigger_custom.start_mode == 'disabled') or trigger.manual
should_start = ((trigger.repo_ids & bundle_repos) or bundle.build_all or bundle.sticky)
if force_trigger or (should_start and not skip_trigger): # only auto link build if bundle has a branch for this trigger
link_type, build = self._create_build(params)
self.env['runbot.batch.slot'].create({ self.env['runbot.batch.slot'].create({
'batch_id': self.id, 'batch_id': self.id,
'trigger_id': trigger.id, 'trigger_id': trigger.id,
'build_id': build.id,
'params_id': params.id, 'params_id': params.id,
'link_type': link_type, 'link_type': 'created',
}) })
###################################### ######################################
@ -388,6 +381,29 @@ class Batch(models.Model):
]) ])
skippable._skip() skippable._skip()
def _start_builds(self):
self.ensure_one()
bundle = self.bundle_id
bundle_repos = bundle.branch_ids.mapped('remote_id.repo_id')
success_trigger = self.slot_ids.filtered(lambda s: s.build_id.global_state in ('running', 'done') and s.build_id.global_result == "ok").trigger_id
trigger_customs = {}
for trigger_custom in self.bundle_id.trigger_custom_ids:
trigger_customs[trigger_custom.trigger_id] = trigger_custom
for slot in self.slot_ids:
if slot.build_id:
continue
trigger = slot.trigger_id
if trigger.starts_after_ids - success_trigger: # some required triggers are missing
continue
trigger_custom = trigger_customs.get(trigger, self.env['runbot.bundle.trigger.custom'])
force_trigger = trigger_custom and trigger_custom.start_mode == 'force'
skip_trigger = (trigger_custom and trigger_custom.start_mode == 'disabled') or trigger.manual
should_start = ((trigger.repo_ids & bundle_repos) or bundle.build_all or bundle.sticky)
if force_trigger or (should_start and not skip_trigger):
slot.link_type, slot.build_id = self._create_build(slot.params_id)
def _update_commits_infos(self, base_head_per_repo): def _update_commits_infos(self, base_head_per_repo):
for link_commit in self.commit_link_ids: for link_commit in self.commit_link_ids:
commit = link_commit.commit_id commit = link_commit.commit_id

View File

@ -158,6 +158,7 @@ class BuildResult(models.Model):
version_id = fields.Many2one('runbot.version', related='params_id.version_id', store=True, index=True) version_id = fields.Many2one('runbot.version', related='params_id.version_id', store=True, index=True)
config_id = fields.Many2one('runbot.build.config', related='params_id.config_id', store=True, index=True) config_id = fields.Many2one('runbot.build.config', related='params_id.config_id', store=True, index=True)
trigger_id = fields.Many2one('runbot.trigger', related='params_id.trigger_id', store=True, index=True) trigger_id = fields.Many2one('runbot.trigger', related='params_id.trigger_id', store=True, index=True)
create_batch_id = fields.Many2one('runbot.batch', related='params_id.create_batch_id', store=True, index=True)
# state machine # state machine
global_state = fields.Selection(make_selection(state_order), string='Status', compute='_compute_global_state', store=True, recursive=True) global_state = fields.Selection(make_selection(state_order), string='Status', compute='_compute_global_state', store=True, recursive=True)

View File

@ -29,6 +29,13 @@ class ModuleFilter(models.Model):
description = fields.Char(string="Description") description = fields.Char(string="Description")
class TriggerDependency(models.Model):
_name = 'runbot.trigger.dependency'
_description = 'Trigger Dependency'
dependency_id = fields.Many2one('runbot.trigger', string="Dependency", required=True)
dependant_id = fields.Many2one('runbot.trigger', string="Dependant", required=True)
class Trigger(models.Model): class Trigger(models.Model):
""" """
List of repo parts that must be part of the same bundle List of repo parts that must be part of the same bundle
@ -46,6 +53,21 @@ class Trigger(models.Model):
project_id = fields.Many2one('runbot.project', string="Project id", required=True) project_id = fields.Many2one('runbot.project', string="Project id", required=True)
repo_ids = fields.Many2many('runbot.repo', relation='runbot_trigger_triggers', string="Triggers", domain="[('project_id', '=', project_id)]") repo_ids = fields.Many2many('runbot.repo', relation='runbot_trigger_triggers', string="Triggers", domain="[('project_id', '=', project_id)]")
dependency_ids = fields.Many2many('runbot.repo', relation='runbot_trigger_dependencies', string="Dependencies") dependency_ids = fields.Many2many('runbot.repo', relation='runbot_trigger_dependencies', string="Dependencies")
starts_before_ids = fields.Many2many(
'runbot.trigger',
string="Start before",
relation='runbot_trigger_dependency',
column1='dependency_id',
column2='dependant_id',
)
starts_after_ids = fields.Many2many(
'runbot.trigger',
string="Start after",
relation='runbot_trigger_dependency',
column2='dependency_id',
column1='dependant_id',
)
module_filters = fields.One2many('runbot.module.filter', 'trigger_id', string="Module filters", help='Will be combined with repo module filters when used with this trigger') module_filters = fields.One2many('runbot.module.filter', 'trigger_id', string="Module filters", help='Will be combined with repo module filters when used with this trigger')
config_id = fields.Many2one('runbot.build.config', string="Config", required=True) config_id = fields.Many2one('runbot.build.config', string="Config", required=True)
batch_dependent = fields.Boolean('Batch Dependent', help="Force adding batch in build parameters to make it unique and give access to bundle") batch_dependent = fields.Boolean('Batch Dependent', help="Force adding batch in build parameters to make it unique and give access to bundle")

View File

@ -139,7 +139,7 @@ class Runbot(models.AbstractModel):
non_allocated_domain = expression.AND([non_allocated_domain, domain]) non_allocated_domain = expression.AND([non_allocated_domain, domain])
e = expression.expression(non_allocated_domain, self.env['runbot.build']) e = expression.expression(non_allocated_domain, self.env['runbot.build'])
query = e.query query = e.query
query.order = '"runbot_build".parent_path' query.order = 'runbot_build.create_batch_id'
select_query, select_params = query.select() select_query, select_params = query.select()
# self-assign to be sure that another runbot batch cannot self assign the same builds # self-assign to be sure that another runbot batch cannot self assign the same builds
query = """UPDATE query = """UPDATE

View File

@ -67,6 +67,9 @@ access_runbot_build_stat_regex_admin,access_runbot_build_stat_regex_admin,runbot
access_runbot_trigger_user,access_runbot_trigger_user,runbot.model_runbot_trigger,runbot.group_user,1,0,0,0 access_runbot_trigger_user,access_runbot_trigger_user,runbot.model_runbot_trigger,runbot.group_user,1,0,0,0
access_runbot_trigger_runbot_admin,access_runbot_trigger_runbot_admin,runbot.model_runbot_trigger,runbot.group_runbot_admin,1,1,1,1 access_runbot_trigger_runbot_admin,access_runbot_trigger_runbot_admin,runbot.model_runbot_trigger,runbot.group_runbot_admin,1,1,1,1
access_runbot_trigger_dependency_user,access_runbot_trigger_dependency_user,runbot.model_runbot_trigger_dependency,runbot.group_user,1,0,0,0
access_runbot_trigger_dependency_runbot_admin,access_runbot_trigger_dependency_runbot_admin,runbot.model_runbot_trigger_dependency,runbot.group_runbot_admin,1,1,1,1
access_runbot_module_filter_user,access_runbot_module_filter_user,runbot.model_runbot_module_filter,runbot.group_user,1,0,0,0 access_runbot_module_filter_user,access_runbot_module_filter_user,runbot.model_runbot_module_filter,runbot.group_user,1,0,0,0
access_runbot_module_filter_runbot_admin,access_runbot_module_filter_runbot_admin,runbot.model_runbot_module_filter,runbot.group_runbot_admin,1,1,1,1 access_runbot_module_filter_runbot_admin,access_runbot_module_filter_runbot_admin,runbot.model_runbot_module_filter,runbot.group_runbot_admin,1,1,1,1
@ -135,3 +138,5 @@ access_runbot_trigger_custom_wizard,access_runbot_trigger_custom_wizard,model_ru
access_runbot_build_stat_regex_wizard,access_runbot_build_stat_regex_wizard,model_runbot_build_stat_regex_wizard,runbot.group_runbot_admin,1,1,1,1 access_runbot_build_stat_regex_wizard,access_runbot_build_stat_regex_wizard,model_runbot_build_stat_regex_wizard,runbot.group_runbot_admin,1,1,1,1
access_runbot_host_message,access_runbot_host_message,runbot.model_runbot_host_message,runbot.group_runbot_admin,1,0,0,0 access_runbot_host_message,access_runbot_host_message,runbot.model_runbot_host_message,runbot.group_runbot_admin,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
67 access_runbot_project_runbot_admin access_runbot_version_runbot_admin access_runbot_project_runbot_admin access_runbot_version_runbot_admin runbot.model_runbot_project runbot.model_runbot_version runbot.group_runbot_admin 1 1 1 1
68 access_runbot_bundle_user access_runbot_project_user access_runbot_bundle_user access_runbot_project_user runbot.model_runbot_bundle runbot.model_runbot_project runbot.group_user 1 0 0 0
69 access_runbot_bundle_runbot_admin access_runbot_project_runbot_admin access_runbot_bundle_runbot_admin access_runbot_project_runbot_admin runbot.model_runbot_bundle runbot.model_runbot_project runbot.group_runbot_admin 1 1 1 1
70 access_runbot_bundle_user access_runbot_bundle_user runbot.model_runbot_bundle runbot.group_user 1 0 0 0
71 access_runbot_bundle_runbot_admin access_runbot_bundle_runbot_admin runbot.model_runbot_bundle runbot.group_runbot_admin 1 1 1 1
72 access_runbot_batch_user access_runbot_batch_user runbot.model_runbot_batch runbot.group_user 1 0 0 0
73 access_runbot_batch_user access_runbot_batch_runbot_admin access_runbot_batch_user access_runbot_batch_runbot_admin runbot.model_runbot_batch runbot.group_user runbot.group_runbot_admin 1 0 1 0 1 0 1
74 access_runbot_batch_runbot_admin access_runbot_batch_slot_user access_runbot_batch_runbot_admin access_runbot_batch_slot_user runbot.model_runbot_batch runbot.model_runbot_batch_slot runbot.group_runbot_admin runbot.group_user 1 1 0 1 0 1 0
75 access_runbot_batch_slot_user access_runbot_batch_slot_runbot_admin access_runbot_batch_slot_user access_runbot_batch_slot_runbot_admin runbot.model_runbot_batch_slot runbot.group_user runbot.group_runbot_admin 1 0 1 0 1 0 1
138
139
140
141
142

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import datetime import datetime
import time import time
from odoo import fields
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
from unittest.mock import patch, DEFAULT from unittest.mock import patch, DEFAULT
@ -249,7 +250,8 @@ class RunbotCase(TransactionCase):
self.assertEqual(triggers.repo_ids + triggers.dependency_ids, self.remote_addons.repo_id + self.remote_server.repo_id) self.assertEqual(triggers.repo_ids + triggers.dependency_ids, self.remote_addons.repo_id + self.remote_server.repo_id)
batch = self.branch_addons.bundle_id._force() batch = self.branch_addons.bundle_id._force()
batch._prepare() batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch._process()
class RunbotCaseMinimalSetup(RunbotCase): class RunbotCaseMinimalSetup(RunbotCase):

View File

@ -90,7 +90,8 @@ class TestBuildParams(RunbotCaseMinimalSetup):
# prepare last_batch # prepare last_batch
bundle = self.env['runbot.bundle'].search([('name', '=', branch_a_name), ('project_id', '=', self.project.id)]) bundle = self.env['runbot.bundle'].search([('name', '=', branch_a_name), ('project_id', '=', self.project.id)])
bundle.last_batch._prepare() bundle.last_batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
bundle.last_batch._process()
build_slot = bundle.last_batch.slot_ids.filtered(lambda rec: rec.trigger_id == self.trigger_server) build_slot = bundle.last_batch.slot_ids.filtered(lambda rec: rec.trigger_id == self.trigger_server)
self.assertEqual(build_slot.build_id.params_id.config_id, self.trigger_server.config_id) self.assertEqual(build_slot.build_id.params_id.config_id, self.trigger_server.config_id)
self.assertEqual(build_slot.build_id.description, expected_description, "A build description should reflect the trigger description") self.assertEqual(build_slot.build_id.description, expected_description, "A build description should reflect the trigger description")
@ -118,11 +119,68 @@ class TestBuildParams(RunbotCaseMinimalSetup):
'bundle_id': bundle.id, 'bundle_id': bundle.id,
'config_id': custom_config.id 'config_id': custom_config.id
}) })
bundle.last_batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
bundle.last_batch._prepare() bundle.last_batch._process()
build_slot = bundle.last_batch.slot_ids.filtered(lambda rec: rec.trigger_id == self.trigger_server) build_slot = bundle.last_batch.slot_ids.filtered(lambda rec: rec.trigger_id == self.trigger_server)
self.assertEqual(build_slot.build_id.params_id.config_id, custom_config) self.assertEqual(build_slot.build_id.params_id.config_id, custom_config)
def test_trigger_dependency(self):
self.start_patchers()
self.additionnal_setup()
self.assertEqual(self.project.trigger_ids, self.trigger_server | self.trigger_addons)
minimal_config = self.env['runbot.build.config'].create({'name': 'Minimal Check'})
other_triggers = self.project.trigger_ids
self.assertEqual(other_triggers.mapped('name'), ['Server trigger', 'Addons trigger'])
trigger_minimal_check = self.Trigger.create({
'sequence': 0,
'name': 'minimal_check',
'repo_ids': [(4, self.repo_addons.id), (4, self.repo_server.id)],
'config_id': minimal_config.id,
'project_id': self.project.id,
'starts_before_ids': other_triggers.ids,
})
self.assertEqual(self.trigger_server.starts_after_ids, trigger_minimal_check)
self.assertEqual(self.trigger_addons.starts_after_ids, trigger_minimal_check)
branch_a_name = 'master-test-something'
self.push_commit(self.remote_server_dev, branch_a_name, 'nice subject', sha='d0d0caca')
self.repo_server._update_batches()
bundle = self.Bundle.search([('name', '=', branch_a_name), ('project_id', '=', self.project.id)])
batch = bundle.last_batch
batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch._process()
self.assertEqual(batch.slot_ids.mapped('trigger_id.name'), ['minimal_check', 'Server trigger', 'Addons trigger'], 'All three slot should have been created')
minimal_check_build = batch.slot_ids.build_id
self.assertEqual(len(minimal_check_build), 1, 'Only minimal check should have started')
self.assertEqual(minimal_check_build.trigger_id.name, 'minimal_check', 'Only minimal check should have started')
with self.env.cr.savepoint() as sp:
minimal_check_build.local_result = 'ko'
minimal_check_build.local_state = 'done'
batch._process()
all_builds = batch.slot_ids.build_id
self.assertEqual(len(all_builds), 1, 'Only minimal check should have started')
self.assertEqual(batch.state, 'done')
sp.rollback()
minimal_check_build.local_result = 'ok'
minimal_check_build.local_state = 'done'
batch._process()
all_builds = batch.slot_ids.build_id
self.assertEqual(
all_builds.trigger_id.mapped('name'),
['minimal_check', 'Server trigger'],
'Other builds should have started (server only since addons was not updated)',
)
self.assertEqual(batch.state, 'ready')
self.assertEqual(all_builds.mapped('local_state'), ['done', 'pending'])
server_builds = all_builds[1]
server_builds.local_result = 'ok'
server_builds.local_state = 'done'
batch._process()
self.assertEqual(batch.state, 'done')
class TestBuildResult(RunbotCase): class TestBuildResult(RunbotCase):
@ -558,11 +616,14 @@ class TestGc(RunbotCaseMinimalSetup):
# prepare last_batch # prepare last_batch
bundle_a = self.env['runbot.bundle'].search([('name', '=', branch_a_name)]) bundle_a = self.env['runbot.bundle'].search([('name', '=', branch_a_name)])
bundle_a.last_batch._prepare() bundle_a.last_batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
bundle_a.last_batch._process()
# now we should have a build in pending state in the bundle # now we should have a build in pending state in the bundle
self.assertEqual(len(bundle_a.last_batch.slot_ids), 2) self.assertEqual(len(bundle_a.last_batch.slot_ids), 2)
build_a = bundle_a.last_batch.slot_ids[0].build_id build_a = bundle_a.last_batch.slot_ids[0].build_id
self.assertEqual(build_a.local_state, 'pending')
self.assertEqual(build_a.local_state, 'pending')
self.assertEqual(build_a.global_state, 'pending') self.assertEqual(build_a.global_state, 'pending')
# now another commit is found in another branch # now another commit is found in another branch
@ -570,7 +631,9 @@ class TestGc(RunbotCaseMinimalSetup):
self.push_commit(self.remote_server_dev, branch_b_name, 'other subject', sha='cacad0d0') self.push_commit(self.remote_server_dev, branch_b_name, 'other subject', sha='cacad0d0')
self.repo_server._update_batches() self.repo_server._update_batches()
bundle_b = self.env['runbot.bundle'].search([('name', '=', branch_b_name)]) bundle_b = self.env['runbot.bundle'].search([('name', '=', branch_b_name)])
bundle_b.last_batch._prepare() bundle_b.last_batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
bundle_b.last_batch._process()
build_b = bundle_b.last_batch.slot_ids[0].build_id build_b = bundle_b.last_batch.slot_ids[0].build_id
@ -588,7 +651,8 @@ class TestGc(RunbotCaseMinimalSetup):
self.push_commit(self.remote_server_dev, branch_a_name, 'new subject', sha='d0cad0ca') self.push_commit(self.remote_server_dev, branch_a_name, 'new subject', sha='d0cad0ca')
self.repo_server._update_batches() self.repo_server._update_batches()
bundle_a = self.env['runbot.bundle'].search([('name', '=', branch_a_name)]) bundle_a = self.env['runbot.bundle'].search([('name', '=', branch_a_name)])
bundle_a.last_batch._prepare() bundle_a.last_batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
bundle_a.last_batch._process()
build_a_last = bundle_a.last_batch.slot_ids[0].build_id build_a_last = bundle_a.last_batch.slot_ids[0].build_id
self.assertEqual(build_a_last.local_state, 'pending') self.assertEqual(build_a_last.local_state, 'pending')
self.assertTrue(build_a.killable, 'The previous build in the batch should be killable') self.assertTrue(build_a.killable, 'The previous build in the batch should be killable')

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import datetime
from unittest.mock import patch, mock_open from unittest.mock import patch, mock_open
from odoo import Command from odoo import Command, fields
from odoo.tools import mute_logger from odoo.tools import mute_logger
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.addons.runbot.common import RunbotException from odoo.addons.runbot.common import RunbotException
@ -219,7 +220,8 @@ class TestBuildConfigStepRestore(TestBuildConfigStepCommon):
# setup master branch # setup master branch
master_batch = self.master_bundle._force() master_batch = self.master_bundle._force()
with mute_logger('odoo.addons.runbot.models.batch'): with mute_logger('odoo.addons.runbot.models.batch'):
master_batch._prepare() master_batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
master_batch._process()
reference_slot = master_batch.slot_ids reference_slot = master_batch.slot_ids
trigger = reference_slot.trigger_id trigger = reference_slot.trigger_id
self.assertEqual(trigger.name, 'Server trigger', 'Just checking that we have a single slot') self.assertEqual(trigger.name, 'Server trigger', 'Just checking that we have a single slot')
@ -246,7 +248,8 @@ class TestBuildConfigStepRestore(TestBuildConfigStepCommon):
# create dev build # create dev build
dev_batch = self.dev_bundle._force() dev_batch = self.dev_bundle._force()
with mute_logger('odoo.addons.runbot.models.batch'): with mute_logger('odoo.addons.runbot.models.batch'):
dev_batch._prepare() dev_batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
dev_batch._process()
dev_batch.base_reference_batch_id = master_batch # not tested, this is not the purpose of this test dev_batch.base_reference_batch_id = master_batch # not tested, this is not the purpose of this test
dev_build = dev_batch.slot_ids.build_id dev_build = dev_batch.slot_ids.build_id
self.assertEqual(dev_build.params_id.config_data, config_data) self.assertEqual(dev_build.params_id.config_data, config_data)

View File

@ -1,12 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import datetime
import logging
import time
import re import re
from unittest import skip from unittest import skip
from unittest.mock import patch, Mock from unittest.mock import patch, Mock
from subprocess import CalledProcessError from subprocess import CalledProcessError
from odoo import fields
from odoo.tests import common, TransactionCase from odoo.tests import common, TransactionCase
from odoo.tools import mute_logger from odoo.tools import mute_logger
import logging
import time
from .common import RunbotCase, RunbotCaseMinimalSetup from .common import RunbotCase, RunbotCaseMinimalSetup
@ -248,7 +252,8 @@ class TestRepo(RunbotCaseMinimalSetup):
return {} return {}
self.patchers['github_patcher'].side_effect = github2 self.patchers['github_patcher'].side_effect = github2
last_batch._prepare() bundle.last_batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
bundle.last_batch._process()
self.assertEqual(last_batch.commit_link_ids.commit_id.mapped('subject'), ['Server subject', 'Addons subject']) self.assertEqual(last_batch.commit_link_ids.commit_id.mapped('subject'), ['Server subject', 'Addons subject'])
self.assertEqual(last_batch.state, 'ready') self.assertEqual(last_batch.state, 'ready')

View File

@ -1,5 +1,7 @@
import datetime
import getpass import getpass
import logging import logging
from odoo import fields
from unittest.mock import patch, mock_open from unittest.mock import patch, mock_open
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tools import mute_logger from odoo.tools import mute_logger
@ -206,7 +208,8 @@ class TestUpgradeFlow(RunbotCase):
# create nightly # create nightly
batch_nigthly = bundle._force(self.nightly_category.id) batch_nigthly = bundle._force(self.nightly_category.id)
batch_nigthly._prepare() batch_nigthly.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch_nigthly._process()
self.assertEqual(batch_nigthly.category_id, self.nightly_category) self.assertEqual(batch_nigthly.category_id, self.nightly_category)
builds_nigthly = {} builds_nigthly = {}
host = self.env['runbot.host']._get_current() host = self.env['runbot.host']._get_current()
@ -232,7 +235,8 @@ class TestUpgradeFlow(RunbotCase):
batch_nigthly.state = 'done' batch_nigthly.state = 'done'
batch_weekly = bundle._force(self.weekly_category.id) batch_weekly = bundle._force(self.weekly_category.id)
batch_weekly._prepare() batch_weekly.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch_weekly._process()
self.assertEqual(batch_weekly.category_id, self.weekly_category) self.assertEqual(batch_weekly.category_id, self.weekly_category)
builds_weekly = {} builds_weekly = {}
build = batch_weekly.slot_ids.filtered(lambda s: s.trigger_id == self.trigger_addons_weekly).build_id build = batch_weekly.slot_ids.filtered(lambda s: s.trigger_id == self.trigger_addons_weekly).build_id
@ -249,7 +253,8 @@ class TestUpgradeFlow(RunbotCase):
batch_weekly.state = 'done' batch_weekly.state = 'done'
batch_default = bundle._force() batch_default = bundle._force()
batch_default._prepare() batch_default.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch_default._process()
build = batch_default.slot_ids.filtered(lambda s: s.trigger_id == self.trigger_server).build_id build = batch_default.slot_ids.filtered(lambda s: s.trigger_id == self.trigger_server).build_id
build.local_state = 'done' build.local_state = 'done'
batch_default.state = 'done' batch_default.state = 'done'
@ -268,7 +273,8 @@ class TestUpgradeFlow(RunbotCase):
self.trigger_upgrade_server.flush_recordset(['upgrade_step_id']) self.trigger_upgrade_server.flush_recordset(['upgrade_step_id'])
batch = self.master_bundle._force() batch = self.master_bundle._force()
batch._prepare() batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch._process()
upgrade_current_build = batch.slot_ids.filtered(lambda slot: slot.trigger_id == self.trigger_upgrade_server).build_id upgrade_current_build = batch.slot_ids.filtered(lambda slot: slot.trigger_id == self.trigger_upgrade_server).build_id
#host = self.env['runbot.host']._get_current() #host = self.env['runbot.host']._get_current()
#upgrade_current_build.host = host.name #upgrade_current_build.host = host.name
@ -334,7 +340,8 @@ class TestUpgradeFlow(RunbotCase):
}) })
batch = self.master_bundle._force(self.nightly_category.id) batch = self.master_bundle._force(self.nightly_category.id)
batch._prepare() batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch._process()
upgrade_nightly = batch.slot_ids.filtered(lambda slot: slot.trigger_id == trigger_upgrade_addons_nightly).build_id upgrade_nightly = batch.slot_ids.filtered(lambda slot: slot.trigger_id == trigger_upgrade_addons_nightly).build_id
#upgrade_nightly.host = host.name #upgrade_nightly.host = host.name
upgrade_nightly._schedule() upgrade_nightly._schedule()
@ -484,7 +491,8 @@ class TestUpgradeFlow(RunbotCase):
# test_build_references # test_build_references
batch = self.master_bundle._force() batch = self.master_bundle._force()
batch._prepare() batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch._process()
upgrade_slot = batch.slot_ids.filtered(lambda slot: slot.trigger_id == self.trigger_upgrade_server) upgrade_slot = batch.slot_ids.filtered(lambda slot: slot.trigger_id == self.trigger_upgrade_server)
self.assertTrue(upgrade_slot) self.assertTrue(upgrade_slot)
upgrade_build = upgrade_slot.build_id upgrade_build = upgrade_slot.build_id
@ -501,7 +509,8 @@ class TestUpgradeFlow(RunbotCase):
self.trigger_upgrade_server.upgrade_step_id.upgrade_from_all_intermediate_version = True self.trigger_upgrade_server.upgrade_step_id.upgrade_from_all_intermediate_version = True
batch = self.master_bundle._force() batch = self.master_bundle._force()
batch._prepare() batch.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch._process()
upgrade_build = batch.slot_ids.filtered(lambda slot: slot.trigger_id == self.trigger_upgrade_server).build_id upgrade_build = batch.slot_ids.filtered(lambda slot: slot.trigger_id == self.trigger_upgrade_server).build_id
self.assertEqual( self.assertEqual(
upgrade_build.params_id.builds_reference_ids, upgrade_build.params_id.builds_reference_ids,
@ -539,7 +548,8 @@ class TestUpgradeFlow(RunbotCase):
self.assertEqual(bundle_133.name, 'saas-13.3') self.assertEqual(bundle_133.name, 'saas-13.3')
batch13 = bundle_13._force() batch13 = bundle_13._force()
batch13._prepare() batch13.last_update = fields.Datetime.now() - datetime.timedelta(seconds=60)
batch13._process()
upgrade_complement_build_13 = batch13.slot_ids.filtered(lambda slot: slot.trigger_id == trigger_upgrade_complement).build_id upgrade_complement_build_13 = batch13.slot_ids.filtered(lambda slot: slot.trigger_id == trigger_upgrade_complement).build_id
# upgrade_complement_build_13.host = host.name # upgrade_complement_build_13.host = host.name
self.assertEqual(upgrade_complement_build_13.params_id.config_id, config_upgrade_complement) self.assertEqual(upgrade_complement_build_13.params_id.config_id, config_upgrade_complement)

View File

@ -14,8 +14,14 @@
<menuitem name="Hosts" id="runbot_menu_host_tree" parent="runbot_menu_root" sequence="300" action="open_view_host_tree" groups="runbot.group_runbot_admin"/> <menuitem name="Hosts" id="runbot_menu_host_tree" parent="runbot_menu_root" sequence="300" action="open_view_host_tree" groups="runbot.group_runbot_admin"/>
<menuitem id="runbot_menu_trigger" parent="runbot_menu_root" sequence="500" action="runbot_triggers_action" groups="runbot.group_runbot_admin"/> <menuitem id="runbot_menu_trigger_root" parent="runbot_menu_root" sequence="500" name="Triggers" groups="runbot.group_runbot_admin"/>
<menuitem id="runbot_menu_trigger" parent="runbot_menu_trigger_root" sequence="501" action="runbot_triggers_action" groups="runbot.group_runbot_admin"/>
<menuitem id="runbot_menu_trigger_dependency" parent="runbot_menu_trigger_root" sequence="502" action="runbot_triggers_dependency_action" groups="runbot.group_runbot_admin"/>
<menuitem name="Configs" id="runbot_menu_configs" parent="runbot_menu_root" sequence="600" groups="runbot.group_build_config_user"/> <menuitem name="Configs" id="runbot_menu_configs" parent="runbot_menu_root" sequence="600" groups="runbot.group_build_config_user"/>
<menuitem id="runbot_menu_job_config_tree" parent="runbot_menu_configs" sequence="10" action="open_view_job_config_tree"/> <menuitem id="runbot_menu_job_config_tree" parent="runbot_menu_configs" sequence="10" action="open_view_job_config_tree"/>
<menuitem id="runbot_menu_job_tree" parent="runbot_menu_configs" sequence="20" action="open_view_job_tree"/> <menuitem id="runbot_menu_job_tree" parent="runbot_menu_configs" sequence="20" action="open_view_job_tree"/>

View File

@ -33,6 +33,20 @@
<group string="Dependencies"> <group string="Dependencies">
<field name="dependency_ids" nolabel="1" colspan="2"/> <field name="dependency_ids" nolabel="1" colspan="2"/>
</group> </group>
<group string="Starts after" invisible="manual" colspan="2">
<field
name="starts_after_ids"
domain="[('category_id', '=', category_id), ('manual', '=', False), ('project_id', '=', project_id), ('id', '!=', id), ('id', 'not in', starts_before_ids)]"
widget="many2many_tags">
</field>
</group>
<group string="Starts before" invisible="manual" colspan="2">
<field
name="starts_before_ids"
domain="[('category_id', '=', category_id), ('manual', '=', False), ('project_id', '=', project_id), ('id', '!=', id), ('id', 'not in', starts_after_ids)]"
widget="many2many_tags">
</field>
</group>
<group string="Module filters"> <group string="Module filters">
<field name="module_filters" nolabel="1" colspan="4"> <field name="module_filters" nolabel="1" colspan="4">
<tree string="Module filters" editable="bottom"> <tree string="Module filters" editable="bottom">
@ -85,12 +99,40 @@
<field name="ci_context"/> <field name="ci_context"/>
<field name="repo_ids" widget="many2many_tags"/> <field name="repo_ids" widget="many2many_tags"/>
<field name="dependency_ids" widget="many2many_tags"/> <field name="dependency_ids" widget="many2many_tags"/>
<field name="starts_after_ids" widget="many2many_tags"/>
<field name="manual"/> <field name="manual"/>
</tree> </tree>
</field> </field>
</record> </record>
<record id="repo_trigger_dependency_form" model="ir.ui.view">
<field name="name">runbot.trigger.dependency.form</field>
<field name="model">runbot.trigger.dependency</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="dependency_id"/>
<field name="dependant_id"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="repo_trigger_catgory_form" model="ir.ui.view"> <record id="repo_trigger_dependency_tree" model="ir.ui.view">
<field name="name">runbot.trigger.dependency.tree</field>
<field name="model">runbot.trigger.dependency</field>
<field name="arch" type="xml">
<tree>
<field name="dependency_id"/>
<field name="dependant_id"/>
</tree>
</field>
</record>
<record id="repo_trigger_category_form" model="ir.ui.view">
<field name="name">runbot.category.form</field> <field name="name">runbot.category.form</field>
<field name="model">runbot.category</field> <field name="model">runbot.category</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
@ -224,10 +266,17 @@
</record> </record>
<record id="runbot_triggers_action" model="ir.actions.act_window"> <record id="runbot_triggers_action" model="ir.actions.act_window">
<field name="name">Triggers</field> <field name="name">Triggers</field>
<field name="res_model">runbot.trigger</field> <field name="res_model">runbot.trigger</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
</record> </record>
<record id="runbot_triggers_dependency_action" model="ir.actions.act_window">
<field name="name">Triggers dependency</field>
<field name="res_model">runbot.trigger.dependency</field>
<field name="view_mode">tree,form</field>
</record>
<record id="runbot_remotes_action" model="ir.actions.act_window"> <record id="runbot_remotes_action" model="ir.actions.act_window">
<field name="name">Remotes</field> <field name="name">Remotes</field>
<field name="res_model">runbot.remote</field> <field name="res_model">runbot.remote</field>

View File

@ -115,8 +115,7 @@ class Runbot(models.AbstractModel):
mock_git.side_effect = git mock_git.side_effect = git
with mute_logger('odoo.addons.runbot.models.batch'): with mute_logger('odoo.addons.runbot.models.batch'):
batch._prepare() batch._process()
if i != nb_batch - 1: if i != nb_batch - 1:
for slot in batch.slot_ids: for slot in batch.slot_ids:
if slot.build_id: if slot.build_id: