Repository: python-gitlab/python-gitlab

Issue:
Allow update of protected branches
In gitlab 15.6 gitlab finally added api support to update protected branch settings, so ProjectProtectedBranch should be updated accordingly

https://gitlab.com/gitlab-org/gitlab/-/issues/20229/





Discussion:
This is a premium feature and we'll need to add a `http_patch` method for this as well.

Edit: And once the Free version PUT endpoint is implemented, we kind of need to be able to distinguish that in the `.save()` and `.update()` methods.
Link to the API docs:

https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch
I think free version will have this endpoint. We know gitlab developers, right?
How about provide `.save()` or `.update()` anyway?

Resolving patch:
diff --git a/gitlab/v4/objects/branches.py b/gitlab/v4/objects/branches.py
--- a/gitlab/v4/objects/branches.py
+++ b/gitlab/v4/objects/branches.py
@@ -1,7 +1,13 @@
 from typing import Any, cast, Union
 
 from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
+from gitlab.mixins import (
+    CRUDMixin,
+    NoUpdateMixin,
+    ObjectDeleteMixin,
+    SaveMixin,
+    UpdateMethod,
+)
 from gitlab.types import RequiredOptional
 
 __all__ = [
@@ -28,11 +34,11 @@ def get(
         return cast(ProjectBranch, super().get(id=id, lazy=lazy, **kwargs))
 
 
-class ProjectProtectedBranch(ObjectDeleteMixin, RESTObject):
+class ProjectProtectedBranch(SaveMixin, ObjectDeleteMixin, RESTObject):
     _id_attr = "name"
 
 
-class ProjectProtectedBranchManager(NoUpdateMixin, RESTManager):
+class ProjectProtectedBranchManager(CRUDMixin, RESTManager):
     _path = "/projects/{project_id}/protected_branches"
     _obj_cls = ProjectProtectedBranch
     _from_parent_attrs = {"project_id": "id"}
@@ -49,6 +55,7 @@ class ProjectProtectedBranchManager(NoUpdateMixin, RESTManager):
             "code_owner_approval_required",
         ),
     )
+    _update_method = UpdateMethod.PATCH
 
     def get(
         self, id: Union[str, int], lazy: bool = False, **kwargs: Any


Test patch:
diff --git a/tests/functional/api/test_projects.py b/tests/functional/api/test_projects.py
--- a/tests/functional/api/test_projects.py
+++ b/tests/functional/api/test_projects.py
@@ -258,12 +258,31 @@ def test_project_pages_domains(gl, project):
     assert domain not in project.pagesdomains.list()
 
 
-def test_project_protected_branches(project):
-    p_b = project.protectedbranches.create({"name": "*-stable"})
+def test_project_protected_branches(project, wait_for_sidekiq, gitlab_version):
+    # Updating a protected branch is possible from Gitlab 15.6
+    # https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch
+    can_update_prot_branch = gitlab_version.major > 15 or (
+        gitlab_version.major == 15 and gitlab_version.minor >= 6
+    )
+
+    p_b = project.protectedbranches.create(
+        {
+            "name": "*-stable",
+            "allow_force_push": False,
+        }
+    )
     assert p_b.name == "*-stable"
+    assert not p_b.allow_force_push
     assert p_b in project.protectedbranches.list()
 
+    if can_update_prot_branch:
+        p_b.allow_force_push = True
+        p_b.save()
+        wait_for_sidekiq(timeout=60)
+
     p_b = project.protectedbranches.get("*-stable")
+    if can_update_prot_branch:
+        assert p_b.allow_force_push
     p_b.delete()
     assert p_b not in project.protectedbranches.list()