From eaee010b25fe65562d05b7bf4f5e0bd046c0fcca Mon Sep 17 00:00:00 2001 From: Xavier-Do Date: Thu, 19 Dec 2019 13:56:25 +0100 Subject: [PATCH] [IMP] runbot: add relation between branches Migration tests comming on runbot, it will be usefull to have quick way to obtain branches related to current build. This commit adds a field for the colsest sticky branch, previous version and intermediates stickies. Example when last sticky is saas-13.2: branch_name: master-test-tri closest_sticky: master previous_version: 13.0 intermediate_stickies: saas-13.1, saas-13.2 --- runbot/models/branch.py | 47 ++++++++++++++++ runbot/tests/test_branch.py | 100 ++++++++++++++++++++++++++++++++++ runbot/views/branch_views.xml | 5 ++ 3 files changed, 152 insertions(+) diff --git a/runbot/models/branch.py b/runbot/models/branch.py index 7d85b511..67b6a12c 100644 --- a/runbot/models/branch.py +++ b/runbot/models/branch.py @@ -15,12 +15,17 @@ class runbot_branch(models.Model): _sql_constraints = [('branch_repo_uniq', 'unique (name,repo_id)', 'The branch must be unique per repository !')] repo_id = fields.Many2one('runbot.repo', 'Repository', required=True, ondelete='cascade') + duplicate_repo_id = fields.Many2one('runbot.repo', 'Duplicate Repository', related='repo_id.duplicate_id',) name = fields.Char('Ref Name', required=True) branch_name = fields.Char(compute='_get_branch_infos', string='Branch', readonly=1, store=True) branch_url = fields.Char(compute='_get_branch_url', string='Branch url', readonly=1) pull_head_name = fields.Char(compute='_get_branch_infos', string='PR HEAD name', readonly=1, store=True) target_branch_name = fields.Char(compute='_get_branch_infos', string='PR target branch', store=True) sticky = fields.Boolean('Sticky') + closest_sticky = fields.Many2one('runbot.branch', compute='_compute_closest_sticky', string='Closest sticky') + defined_sticky = fields.Many2one('runbot.branch', string='Force sticky') + previous_version = fields.Many2one('runbot.branch', compute='_compute_previous_version', string='Previous version branch') + intermediate_stickies = fields.Many2many('runbot.branch', compute='_compute_intermediate_stickies', string='Intermediates stickies') coverage_result = fields.Float(compute='_compute_coverage_result', type='Float', string='Last coverage', store=False) # non optimal search in loop, could we store this result ? or optimise state = fields.Char('Status') modules = fields.Char("Modules to Install", help="Comma-separated list of modules to install and test.") @@ -31,6 +36,48 @@ class runbot_branch(models.Model): branch_config_id = fields.Many2one('runbot.build.config', 'Run Config') config_id = fields.Many2one('runbot.build.config', 'Run Config', compute='_compute_config_id', inverse='_inverse_config_id') + @api.depends('sticky', 'defined_sticky', 'target_branch_name', 'name') + # won't be recompute if a new branch is marked as sticky or sticky is removed, but should be ok if not stored + def _compute_closest_sticky(self): + for branch in self: + if branch.sticky: + branch.closest_sticky = branch + elif branch.defined_sticky: + branch.closest_sticky = branch.defined_sticky # be carefull with loop + elif branch.target_branch_name: + corresping_branch = self.search([('branch_name', '=', branch.target_branch_name), ('repo_id', '=', branch.repo_id.id)]) + branch.closest_sticky = corresping_branch.closest_sticky + else: + repo_ids = (branch.repo_id | branch.repo_id.duplicate_id).ids + self.env.cr.execute("select id from runbot_branch where sticky = 't' and repo_id = any(%s) and %s like name||'%%'", (repo_ids, branch.name or '')) + branch.closest_sticky = self.browse(self.env.cr.fetchone()) + + @api.depends('closest_sticky.previous_version') + def _compute_previous_version(self): + for branch in self: + if branch.closest_sticky == branch: + repo_ids = (branch.repo_id | branch.repo_id.duplicate_id).ids + domain = [('branch_name', 'like', '%.0'), ('sticky', '=', True), ('branch_name', '!=', 'master'), ('repo_id', 'in', repo_ids)] + if branch.branch_name != 'master': + domain += [('id', '<', branch.id)] + branch.previous_version = self.search(domain, limit=1, order='id desc') + else: + branch.previous_version = branch.closest_sticky.previous_version + + @api.depends('previous_version', 'closest_sticky.intermediate_stickies') + def _compute_intermediate_stickies(self): + for branch in self: + if branch.closest_sticky == branch: + if not branch.previous_version: + continue + repo_ids = (branch.repo_id | branch.repo_id.duplicate_id).ids + domain = [('id', '>', branch.previous_version.id), ('sticky', '=', True), ('branch_name', '!=', 'master'), ('repo_id', 'in', repo_ids)] + if branch.closest_sticky.branch_name != 'master': + domain += [('id', '<', branch.closest_sticky.id)] + branch.intermediate_stickies = [(6, 0, self.search(domain, order='id desc').ids)] + else: + branch.intermediate_stickies = [(6, 0, branch.closest_sticky.intermediate_stickies.ids)] + def _compute_config_id(self): for branch in self: if branch.branch_config_id: diff --git a/runbot/tests/test_branch.py b/runbot/tests/test_branch.py index 91d7b862..5bd531d3 100644 --- a/runbot/tests/test_branch.py +++ b/runbot/tests/test_branch.py @@ -52,3 +52,103 @@ class Test_Branch(RunbotCase): 'name': 'refs/head/foo-use-coverage-branch-bar' }) self.assertEqual(cov_branch.config_id, self.env.ref('runbot.runbot_build_config_test_coverage')) + + +class TestBranchRelations(RunbotCase): + + def setUp(self): + super(TestBranchRelations, self).setUp() + + self.repo = self.env['runbot.repo'].create({'name': 'bla@example.com:foo/bar'}) + self.repodev = self.env['runbot.repo'].create({'name': 'bla@example.com:foo-dev/bar', 'duplicate_id':self.repo.id }) + self.Branch = self.env['runbot.branch'] + + def create_sticky(name): + return self.Branch.create({ + 'repo_id': self.repo.id, + 'name': 'refs/heads/%s' % name, + 'sticky': True + }) + self.master = create_sticky('master') + create_sticky('11.0') + create_sticky('saas-11.1') + create_sticky('12.0') + create_sticky('saas-12.3') + create_sticky('13.0') + create_sticky('saas-13.1') + self.last = create_sticky('saas-13.2') + + def test_relations_master_dev(self): + b = self.Branch.create({ + 'repo_id': self.repodev.id, + 'name': 'refs/heads/master-test-tri', + }) + self.assertEqual(b.closest_sticky.branch_name, 'master') + self.assertEqual(b.previous_version.branch_name, '13.0') + self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), ['saas-13.1', 'saas-13.2']) + + def test_relations_master(self): + b = self.master + self.assertEqual(b.closest_sticky.branch_name, 'master') + self.assertEqual(b.previous_version.branch_name, '13.0') + self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), ['saas-13.1', 'saas-13.2']) + + def test_relations_no_intermediate(self): + b = self.Branch.create({ + 'repo_id': self.repodev.id, + 'name': 'refs/heads/saas-13.1-test-tri', + }) + self.assertEqual(b.closest_sticky.branch_name, 'saas-13.1') + self.assertEqual(b.previous_version.branch_name, '13.0') + self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), []) + + def test_relations_old_branch(self): + b = self.Branch.create({ + 'repo_id': self.repodev.id, + 'name': 'refs/heads/11.0-test-tri', + }) + self.assertEqual(b.closest_sticky.branch_name, '11.0') + self.assertEqual(b.previous_version.branch_name, False) + self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), []) + + def test_relations_closest_forced(self): + b = self.Branch.create({ + 'repo_id': self.repodev.id, + 'name': 'refs/heads/master-test-tri', + }) + self.assertEqual(b.closest_sticky.branch_name, 'master') + self.assertEqual(b.previous_version.branch_name, '13.0') + self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), ['saas-13.1', 'saas-13.2']) + + b.closest_sticky = self.last + + self.assertEqual(b.closest_sticky.branch_name, 'saas-13.2') + self.assertEqual(b.previous_version.branch_name, '13.0') + self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), ['saas-13.1']) + + def test_relations_no_match(self): + b = self.Branch.create({ + 'repo_id': self.repodev.id, + 'name': 'refs/heads/icantnamemybranches', + }) + + self.assertEqual(b.closest_sticky.branch_name, False) + self.assertEqual(b.previous_version.branch_name, False) + self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), []) + + def test_relations_pr(self): + self.Branch.create({ + 'repo_id': self.repodev.id, + 'name': 'refs/heads/master-test-tri', + }) + b = self.Branch.create({ + 'repo_id': self.repodev.id, + 'target_branch_name': 'master-test-tri', + 'name': 'refs/pull/100', + }) + b.target_branch_name = 'master-test-tri' + self.assertEqual(b.closest_sticky.branch_name, 'master') + self.assertEqual(b.previous_version.branch_name, '13.0') + self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), ['saas-13.1', 'saas-13.2']) + + diff --git a/runbot/views/branch_views.xml b/runbot/views/branch_views.xml index 2c8c97aa..e3eceb55 100644 --- a/runbot/views/branch_views.xml +++ b/runbot/views/branch_views.xml @@ -10,6 +10,7 @@ + @@ -21,6 +22,10 @@ + + + +