From e2887a7473147d490d78c9aaec9e43b8ee5f9819 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 7 Feb 2022 15:15:13 +0100 Subject: [PATCH] [IMP] runbot_merge: allow only freezing a subset of a project - add flag to not select repos for freezing - allow removing more repositories from the wizard - when performing the freeze, only create branches for the selected repos --- runbot_merge/__manifest__.py | 2 +- runbot_merge/models/project.py | 6 +- .../models/project_freeze/__init__.py | 14 ++-- runbot_merge/models/project_freeze/views.xml | 13 +++- runbot_merge/security/ir.model.access.csv | 2 +- runbot_merge/tests/test_multirepo.py | 72 +++++++++++++++++++ 6 files changed, 101 insertions(+), 8 deletions(-) diff --git a/runbot_merge/__manifest__.py b/runbot_merge/__manifest__.py index e44f1830..8375a3d8 100644 --- a/runbot_merge/__manifest__.py +++ b/runbot_merge/__manifest__.py @@ -9,9 +9,9 @@ 'data/merge_cron.xml', 'views/res_partner.xml', 'views/runbot_merge_project.xml', - 'models/project_freeze/views.xml', 'views/mergebot.xml', 'views/templates.xml', + 'models/project_freeze/views.xml', ], 'post_load': 'enable_sentry', 'pre_init_hook': '_check_citext', diff --git a/runbot_merge/models/project.py b/runbot_merge/models/project.py index 77c778e2..f3d27025 100644 --- a/runbot_merge/models/project.py +++ b/runbot_merge/models/project.py @@ -115,6 +115,10 @@ class Project(models.Model): w = Freeze.search([('project_id', '=', self.id)]) or Freeze.create({ 'project_id': self.id, 'branch_name': self._next_freeze(), - 'release_pr_ids': [(0, 0, {'repository_id': repo.id}) for repo in self.repo_ids] + 'release_pr_ids': [ + (0, 0, {'repository_id': repo.id}) + for repo in self.repo_ids + if repo.freeze + ] }) return w.action_open() diff --git a/runbot_merge/models/project_freeze/__init__.py b/runbot_merge/models/project_freeze/__init__.py index 92bc6c65..39941d28 100644 --- a/runbot_merge/models/project_freeze/__init__.py +++ b/runbot_merge/models/project_freeze/__init__.py @@ -97,7 +97,8 @@ class FreezeWizard(models.Model): # create new branch on every repository errors = [] repository = None - for repository in project_id.repo_ids: + for rel in self.release_pr_ids: + repository = rel.repository_id gh = repository.github() # annoyance: can't directly alias a ref to an other ref, need to # resolve the "old" branch explicitely @@ -117,11 +118,11 @@ class FreezeWizard(models.Model): # if an error occurred during creation, try to clean up then raise error if errors: - for r in project_id.repo_ids: - if r == repository: + for r in self.release_pr_ids: + if r.repository_id == repository: break - deletion = r.github().delete(f'git/refs/heads/{self.branch_name}') + deletion = r.repository_id.github().delete(f'git/refs/heads/{self.branch_name}') if not deletion.ok: errors.append(f"Consequently unable to delete branch {self.branch_name} of repository {r.name}.") time.sleep(1) @@ -166,6 +167,11 @@ class ReleasePullRequest(models.Model): return super().write(vals) +class RepositoryFreeze(models.Model): + _inherit = 'runbot_merge.repository' + freeze = fields.Boolean(required=True, default=True, + help="Freeze this repository by default") + @enum.unique class Colors(enum.IntEnum): No = 0 diff --git a/runbot_merge/models/project_freeze/views.xml b/runbot_merge/models/project_freeze/views.xml index 255be83d..87b1baca 100644 --- a/runbot_merge/models/project_freeze/views.xml +++ b/runbot_merge/models/project_freeze/views.xml @@ -25,7 +25,7 @@ - + @@ -49,4 +49,15 @@ + + + Add freeze field to repo form + runbot_merge.repository + + + + + + + diff --git a/runbot_merge/security/ir.model.access.csv b/runbot_merge/security/ir.model.access.csv index 5815aed8..4bf9e0d2 100644 --- a/runbot_merge/security/ir.model.access.csv +++ b/runbot_merge/security/ir.model.access.csv @@ -1,7 +1,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_runbot_merge_project_admin,Admin access to project,model_runbot_merge_project,runbot_merge.group_admin,1,1,1,1 access_runbot_merge_project_freeze,Admin access to freeze wizard,model_runbot_merge_project_freeze,runbot_merge.group_admin,1,1,0,0 -access_runbot_merge_project_freeze_prs,Admin access to freeze wizard prs,model_runbot_merge_project_freeze_prs,runbot_merge.group_admin,1,1,0,0 +access_runbot_merge_project_freeze_prs,Admin access to freeze wizard prs,model_runbot_merge_project_freeze_prs,runbot_merge.group_admin,1,1,0,1 access_runbot_merge_repository_admin,Admin access to repo,model_runbot_merge_repository,runbot_merge.group_admin,1,1,1,1 access_runbot_merge_repository_status_admin,Admin access to repo statuses,model_runbot_merge_repository_status,runbot_merge.group_admin,1,1,1,1 access_runbot_merge_branch_admin,Admin access to branches,model_runbot_merge_branch,runbot_merge.group_admin,1,1,1,1 diff --git a/runbot_merge/tests/test_multirepo.py b/runbot_merge/tests/test_multirepo.py index bfb4f316..7b71d2f6 100644 --- a/runbot_merge/tests/test_multirepo.py +++ b/runbot_merge/tests/test_multirepo.py @@ -1234,3 +1234,75 @@ def test_freeze_complete(env, project, repo_a, repo_b, repo_c, users, config): assert c_c.message.startswith('Release 1.1 (C)') assert repo_c.read_tree(c_c) == {'f': '2', 'version': ''} assert repo_c.commit(c_c.parents[0]).parents[0] == masters[2] + + +def test_freeze_subset(env, project, repo_a, repo_b, repo_c, users, config): + """It should be possible to only freeze a subset of a project when e.g. one + of the repository is managed differently than the rest and has + non-synchronous releases. + + - it should be possible to mark repositories as non-freezed (just opted out + of the entire thing), in which case no freeze PRs should be asked of them + - it should be possible to remove repositories from the freeze wizard + - repositories which are not in the freeze wizard should just not be frozen + + To do things correctly that should probably match with the branch filters + and stuff, but that's a configuration concern. + """ + # have a project with 3 repos, and two branches (1.0 and master) + project.branch_ids = [ + (1, project.branch_ids.id, {'sequence': 1}), + (0, 0, {'name': '1.0', 'sequence': 2}), + ] + + masters = [] + for r in [repo_a, repo_b, repo_c]: + with r: + [root, _] = r.make_commits( + None, + Commit('base', tree={'version': '', 'f': '0'}), + Commit('release 1.0', tree={'version': '1.0'} if r is repo_a else None), + ref='heads/1.0' + ) + masters.extend(r.make_commits(root, Commit('other', tree={'f': '1'}), ref='heads/master')) + + with repo_a: + repo_a.make_commits( + masters[0], + Commit('Release 1.1', tree={'version': '1.1'}), + ref='heads/release-1.1' + ) + pr_rel_a = repo_a.make_pr(target='master', head='release-1.1') + + # the third repository we opt out of freezing + project.repo_ids.filtered(lambda r: r.name == repo_c.name).freeze = False + env.run_crons() # process the PRs + + # open the freeze wizard + w = project.action_prepare_freeze() + w_id = env[w['res_model']].browse([w['res_id']]) + # check that there are only rels for repos A and B + assert w_id.mapped('release_pr_ids.repository_id.name') == [repo_a.name, repo_b.name] + # remove B from the set + b_id = w_id.release_pr_ids.filtered(lambda r: r.repository_id.name == repo_b.name) + w_id.write({'release_pr_ids': [(3, b_id.id, 0)]}) + assert len(w_id.release_pr_ids) == 1 + # set lone release PR + w_id.release_pr_ids.pr_id = to_pr(env, pr_rel_a).id + assert not w_id.errors + + w_id.action_freeze() + assert not w_id.exists() + + assert repo_a.commit('1.1'), "should have created branch in repo A" + try: + repo_b.commit('1.1') + pytest.fail("should *not* have created branch in repo B") + except AssertionError: + ... + try: + repo_c.commit('1.1') + pytest.fail("should *not* have created branch in repo C") + except AssertionError: + ... + # can't stage because we (wilfully) don't have branches 1.1 in repos B and C