Skip to content

Commit 053d0ae

Browse files
patchback[bot]phillidfelixfontein
authored
[PR #10795/f772bcda backport][stable-11] gitlab_protected_branch: refactor, add allow_force_push, code_owner_approval_required (#10803)
gitlab_protected_branch: refactor, add `allow_force_push`, `code_owner_approval_required` (#10795) * gitlab_protected_branch: fix typo * gitlab_protected_branch: lump parameters into options dictionary Hardcoding parameter lists gets repetitive. Refactor this module to use an options dictionary like many other gitlab_* modules. This makes it cleaner to add new options. * gitlab_protected_branch: update when possible Until now, the module deletes and re-creates the protected branch if any change is detected. This makes sense for the access level parameters, as these are not easily mutated after creation. However, in order to add further options which _can_ easily be updated, we should support updating by default, unless known-immutable parameters are changing. * gitlab_protected_branch: add `allow_force_push` option * gitlab_protected_branch: add `code_owner_approval_required` option * gitlab_protected_branch: add issues to changelog * Update changelog. --------- (cherry picked from commit f772bcd) Co-authored-by: David Phillips <[email protected]> Co-authored-by: Felix Fontein <[email protected]>
1 parent f9baa99 commit 053d0ae

File tree

3 files changed

+100
-36
lines changed

3 files changed

+100
-36
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
minor_changes:
2+
- gitlab_protected_branch - add ``allow_force_push``, ``code_owner_approval_required`` (https://github.com/ansible-collections/community.general/pull/10795, https://github.com/ansible-collections/community.general/issues/6432, https://github.com/ansible-collections/community.general/issues/10289, https://github.com/ansible-collections/community.general/issues/10765).
3+
- gitlab_protected_branch - update protected branches if possible instead of recreating them (https://github.com/ansible-collections/community.general/pull/10795).

plugins/modules/gitlab_protected_branch.py

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@
5858
default: maintainer
5959
type: str
6060
choices: ["maintainer", "developer", "nobody"]
61+
allow_force_push:
62+
description:
63+
- Whether or not to allow force pushes to the protected branch.
64+
type: bool
65+
version_added: '11.3.0'
66+
code_owner_approval_required:
67+
description:
68+
- Whether or not to require code owner approval to push.
69+
type: bool
70+
version_added: '11.3.0'
6171
"""
6272

6373

@@ -106,27 +116,43 @@ def protected_branch_exist(self, name):
106116
except Exception as e:
107117
return False
108118

109-
def create_protected_branch(self, name, merge_access_levels, push_access_level):
110-
if self._module.check_mode:
111-
return True
112-
merge = self.ACCESS_LEVEL[merge_access_levels]
113-
push = self.ACCESS_LEVEL[push_access_level]
114-
self.project.protectedbranches.create({
119+
def create_or_update_protected_branch(self, name, options):
120+
protected_branch_options = {
115121
'name': name,
116-
'merge_access_level': merge,
117-
'push_access_level': push
118-
})
119-
120-
def compare_protected_branch(self, name, merge_access_levels, push_access_level):
121-
configured_merge = self.ACCESS_LEVEL[merge_access_levels]
122-
configured_push = self.ACCESS_LEVEL[push_access_level]
123-
current = self.protected_branch_exist(name=name)
124-
current_merge = current.merge_access_levels[0]['access_level']
125-
current_push = current.push_access_levels[0]['access_level']
126-
if current:
127-
if current.name == name and current_merge == configured_merge and current_push == configured_push:
128-
return True
129-
return False
122+
'allow_force_push': options['allow_force_push'],
123+
'code_owner_approval_required': options['code_owner_approval_required'],
124+
}
125+
protected_branch = self.protected_branch_exist(name=name)
126+
changed = False
127+
if protected_branch and self.can_update(protected_branch, options):
128+
for arg_key, arg_value in protected_branch_options.items():
129+
if arg_value is not None:
130+
if getattr(protected_branch, arg_key) != arg_value:
131+
setattr(protected_branch, arg_key, arg_value)
132+
changed = True
133+
if changed and not self._module.check_mode:
134+
protected_branch.save()
135+
else:
136+
# Set immutable options only on (re)creation
137+
protected_branch_options['merge_access_level'] = options['merge_access_levels']
138+
protected_branch_options['push_access_level'] = options['push_access_level']
139+
if protected_branch:
140+
# Exists, but couldn't update. So, delete first
141+
self.delete_protected_branch(name)
142+
if not self._module.check_mode:
143+
self.project.protectedbranches.create(protected_branch_options)
144+
changed = True
145+
146+
return changed
147+
148+
def can_update(self, protected_branch, options):
149+
# these keys are not set on update the same way they are on creation
150+
configured_merge = options['merge_access_levels']
151+
configured_push = options['push_access_level']
152+
current_merge = protected_branch.merge_access_levels[0]['access_level']
153+
current_push = protected_branch.push_access_levels[0]['access_level']
154+
return ((configured_merge is None or current_merge == configured_merge) and
155+
(configured_push is None or current_push == configured_push))
130156

131157
def delete_protected_branch(self, name):
132158
if self._module.check_mode:
@@ -142,6 +168,8 @@ def main():
142168
name=dict(type='str', required=True),
143169
merge_access_levels=dict(type='str', default="maintainer", choices=["maintainer", "developer", "nobody"]),
144170
push_access_level=dict(type='str', default="maintainer", choices=["maintainer", "developer", "nobody"]),
171+
allow_force_push=dict(type='bool'),
172+
code_owner_approval_required=dict(type='bool'),
145173
state=dict(type='str', default="present", choices=["absent", "present"]),
146174
)
147175

@@ -174,23 +202,24 @@ def main():
174202

175203
gitlab_version = gitlab.__version__
176204
if LooseVersion(gitlab_version) < LooseVersion('2.3.0'):
177-
module.fail_json(msg="community.general.gitlab_proteched_branch requires python-gitlab Python module >= 2.3.0 (installed version: [%s])."
205+
module.fail_json(msg="community.general.gitlab_protected_branch requires python-gitlab Python module >= 2.3.0 (installed version: [%s])."
178206
" Please upgrade python-gitlab to version 2.3.0 or above." % gitlab_version)
179207

180208
this_gitlab = GitlabProtectedBranch(module=module, project=project, gitlab_instance=gitlab_instance)
181209

182210
p_branch = this_gitlab.protected_branch_exist(name=name)
183-
if not p_branch and state == "present":
184-
this_gitlab.create_protected_branch(name=name, merge_access_levels=merge_access_levels, push_access_level=push_access_level)
185-
module.exit_json(changed=True, msg="Created the proteched branch.")
186-
elif p_branch and state == "present":
187-
if not this_gitlab.compare_protected_branch(name, merge_access_levels, push_access_level):
188-
this_gitlab.delete_protected_branch(name=name)
189-
this_gitlab.create_protected_branch(name=name, merge_access_levels=merge_access_levels, push_access_level=push_access_level)
190-
module.exit_json(changed=True, msg="Recreated the proteched branch.")
211+
options = {
212+
"merge_access_levels": this_gitlab.ACCESS_LEVEL[merge_access_levels],
213+
"push_access_level": this_gitlab.ACCESS_LEVEL[push_access_level],
214+
"allow_force_push": module.params["allow_force_push"],
215+
"code_owner_approval_required": module.params["code_owner_approval_required"],
216+
}
217+
if state == "present":
218+
changed = this_gitlab.create_or_update_protected_branch(name, options)
219+
module.exit_json(changed=changed, msg="Created or updated the protected branch.")
191220
elif p_branch and state == "absent":
192221
this_gitlab.delete_protected_branch(name=name)
193-
module.exit_json(changed=True, msg="Deleted the proteched branch.")
222+
module.exit_json(changed=True, msg="Deleted the protected branch.")
194223
module.exit_json(changed=False, msg="No changes are needed.")
195224

196225

tests/unit/plugins/modules/test_gitlab_protected_branch.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ def _dummy(x):
4747
with_httmock = _dummy
4848

4949

50+
class MockProtectedBranch():
51+
def __init__(self, merge_access_levels, push_access_levels):
52+
self.merge_access_levels = merge_access_levels
53+
self.push_access_levels = push_access_levels
54+
55+
5056
class TestGitlabProtectedBranch(GitlabModuleTestCase):
5157
@with_httmock(resp_get_project_by_name)
5258
@with_httmock(resp_get_user)
@@ -66,14 +72,40 @@ def test_protected_branch_exist_not_exist(self):
6672
rvalue = self.moduleUtil.protected_branch_exist(name="master")
6773
self.assertEqual(rvalue, False)
6874

69-
@with_httmock(resp_get_protected_branch)
70-
def test_compare_protected_branch(self):
71-
rvalue = self.moduleUtil.compare_protected_branch(name="master", merge_access_levels="maintainer", push_access_level="maintainer")
75+
def test_can_update_zero_delta(self):
76+
protected_branch = MockProtectedBranch(
77+
merge_access_levels=[{"access_level": 40}],
78+
push_access_levels=[{"access_level": 40}],
79+
)
80+
options = {
81+
"merge_access_levels": 40,
82+
"push_access_level": 40
83+
}
84+
rvalue = self.moduleUtil.can_update(protected_branch, options)
7285
self.assertEqual(rvalue, True)
7386

74-
@with_httmock(resp_get_protected_branch)
75-
def test_compare_protected_branch_different_settings(self):
76-
rvalue = self.moduleUtil.compare_protected_branch(name="master", merge_access_levels="developer", push_access_level="maintainer")
87+
def test_can_update_no_configured(self):
88+
protected_branch = MockProtectedBranch(
89+
merge_access_levels=[{"access_level": 40}],
90+
push_access_levels=[{"access_level": 40}],
91+
)
92+
options = {
93+
"merge_access_levels": None,
94+
"push_access_level": None
95+
}
96+
rvalue = self.moduleUtil.can_update(protected_branch, options)
97+
self.assertEqual(rvalue, True)
98+
99+
def test_can_update_different_settings(self):
100+
protected_branch = MockProtectedBranch(
101+
merge_access_levels=[{"access_level": 40}],
102+
push_access_levels=[{"access_level": 40}],
103+
)
104+
options = {
105+
"merge_access_levels": 40,
106+
"push_access_level": 30
107+
}
108+
rvalue = self.moduleUtil.can_update(protected_branch, options)
77109
self.assertEqual(rvalue, False)
78110

79111
@with_httmock(resp_get_protected_branch)

0 commit comments

Comments
 (0)