summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorO'Keefe, Gerard (Gerry) <gokeefe@atb.com>2022-07-04 10:54:51 -0700
committerJohn L. Villalovos <john@sodarock.com>2022-07-04 10:54:51 -0700
commitb5cdc097005c8a48a16e793a69c343198b14e035 (patch)
tree972c6a46ccb6effee32a96453b90beb2138ef545
parent3df404c8165c36486bbcdf03816bd0b3173d9de8 (diff)
downloadgitlab-b5cdc097005c8a48a16e793a69c343198b14e035.tar.gz
feat: add support for group push rules
Add the GroupPushRules and GroupPushRulesManager classes. Closes: #1259
-rw-r--r--docs/gl_objects/groups.rst34
-rw-r--r--gitlab/v4/objects/groups.py2
-rw-r--r--gitlab/v4/objects/push_rules.py57
-rw-r--r--tests/unit/objects/test_groups.py103
4 files changed, 194 insertions, 2 deletions
diff --git a/docs/gl_objects/groups.rst b/docs/gl_objects/groups.rst
index c4d9fdc..b9b865a 100644
--- a/docs/gl_objects/groups.rst
+++ b/docs/gl_objects/groups.rst
@@ -376,3 +376,37 @@ Delete a group hook::
group.hooks.delete(hook_id)
# or
hook.delete()
+
+Group push rules
+==================
+
+Reference
+---------
+
+* v4 API:
+
+ + :class:`gitlab.v4.objects.GroupPushRules`
+ + :class:`gitlab.v4.objects.GroupPushRulesManager`
+ + :attr:`gitlab.v4.objects.Group.pushrules`
+
+* GitLab API: https://docs.gitlab.com/ee/api/groups.html#push-rules
+
+Examples
+---------
+
+Create group push rules (at least one rule is necessary)::
+
+ group.pushrules.create({'deny_delete_tag': True})
+
+Get group push rules (returns None is there are no push rules)::
+
+ pr = group.pushrules.get()
+
+Edit group push rules::
+
+ pr.branch_name_regex = '^(master|develop|support-\d+|release-\d+\..+|hotfix-.+|feature-.+)$'
+ pr.save()
+
+Delete group push rules::
+
+ pr.delete()
diff --git a/gitlab/v4/objects/groups.py b/gitlab/v4/objects/groups.py
index 8cb5052..84fd600 100644
--- a/gitlab/v4/objects/groups.py
+++ b/gitlab/v4/objects/groups.py
@@ -33,6 +33,7 @@ from .milestones import GroupMilestoneManager # noqa: F401
from .notification_settings import GroupNotificationSettingsManager # noqa: F401
from .packages import GroupPackageManager # noqa: F401
from .projects import GroupProjectManager # noqa: F401
+from .push_rules import GroupPushRulesManager
from .runners import GroupRunnerManager # noqa: F401
from .statistics import GroupIssuesStatisticsManager # noqa: F401
from .variables import GroupVariableManager # noqa: F401
@@ -75,6 +76,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
notificationsettings: GroupNotificationSettingsManager
packages: GroupPackageManager
projects: GroupProjectManager
+ pushrules: GroupPushRulesManager
runners: GroupRunnerManager
subgroups: "GroupSubgroupManager"
variables: GroupVariableManager
diff --git a/gitlab/v4/objects/push_rules.py b/gitlab/v4/objects/push_rules.py
index d1fe81c..9b4980b 100644
--- a/gitlab/v4/objects/push_rules.py
+++ b/gitlab/v4/objects/push_rules.py
@@ -12,6 +12,8 @@ from gitlab.mixins import (
from gitlab.types import RequiredOptional
__all__ = [
+ "GroupPushRules",
+ "GroupPushRulesManager",
"ProjectPushRules",
"ProjectPushRulesManager",
]
@@ -29,14 +31,62 @@ class ProjectPushRulesManager(
_from_parent_attrs = {"project_id": "id"}
_create_attrs = RequiredOptional(
optional=(
+ "author_email_regex",
+ "branch_name_regex",
+ "commit_committer_check",
+ "commit_message_negative_regex",
+ "commit_message_regex",
+ "deny_delete_tag",
+ "file_name_regex",
+ "max_file_size",
+ "member_check",
+ "prevent_secrets",
+ "reject_unsigned_commits",
+ ),
+ )
+ _update_attrs = RequiredOptional(
+ optional=(
+ "author_email_regex",
+ "branch_name_regex",
+ "commit_committer_check",
+ "commit_message_negative_regex",
+ "commit_message_regex",
+ "deny_delete_tag",
+ "file_name_regex",
+ "max_file_size",
+ "member_check",
+ "prevent_secrets",
+ "reject_unsigned_commits",
+ ),
+ )
+
+ def get(self, **kwargs: Any) -> ProjectPushRules:
+ return cast(ProjectPushRules, super().get(**kwargs))
+
+
+class GroupPushRules(SaveMixin, ObjectDeleteMixin, RESTObject):
+ _id_attr = None
+
+
+class GroupPushRulesManager(
+ GetWithoutIdMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = "/groups/{group_id}/push_rule"
+ _obj_cls = GroupPushRules
+ _from_parent_attrs = {"group_id": "id"}
+ _create_attrs = RequiredOptional(
+ optional=(
"deny_delete_tag",
"member_check",
"prevent_secrets",
"commit_message_regex",
+ "commit_message_negative_regex",
"branch_name_regex",
"author_email_regex",
"file_name_regex",
"max_file_size",
+ "commit_committer_check",
+ "reject_unsigned_commits",
),
)
_update_attrs = RequiredOptional(
@@ -45,12 +95,15 @@ class ProjectPushRulesManager(
"member_check",
"prevent_secrets",
"commit_message_regex",
+ "commit_message_negative_regex",
"branch_name_regex",
"author_email_regex",
"file_name_regex",
"max_file_size",
+ "commit_committer_check",
+ "reject_unsigned_commits",
),
)
- def get(self, **kwargs: Any) -> ProjectPushRules:
- return cast(ProjectPushRules, super().get(**kwargs))
+ def get(self, **kwargs: Any) -> GroupPushRules:
+ return cast(GroupPushRules, super().get(**kwargs))
diff --git a/tests/unit/objects/test_groups.py b/tests/unit/objects/test_groups.py
index 2c91d38..cebdfc7 100644
--- a/tests/unit/objects/test_groups.py
+++ b/tests/unit/objects/test_groups.py
@@ -38,6 +38,19 @@ subgroup_descgroup_content = [
"created_at": "2020-01-15T12:36:29.590Z",
},
]
+push_rules_content = {
+ "id": 2,
+ "created_at": "2020-08-17T19:09:19.580Z",
+ "commit_message_regex": "[a-zA-Z]",
+ "commit_message_negative_regex": "[x+]",
+ "branch_name_regex": "[a-z]",
+ "deny_delete_tag": True,
+ "member_check": True,
+ "prevent_secrets": True,
+ "author_email_regex": "^[A-Za-z0-9.]+@gitlab.com$",
+ "file_name_regex": "(exe)$",
+ "max_file_size": 100,
+}
@pytest.fixture
@@ -111,6 +124,72 @@ def resp_transfer_group():
yield rsps
+@pytest.fixture
+def resp_list_push_rules_group():
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/groups/1/push_rule",
+ json=push_rules_content,
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_create_push_rules_group():
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.POST,
+ url="http://localhost/api/v4/groups/1/push_rule",
+ json=push_rules_content,
+ content_type="application/json",
+ status=201,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_update_push_rules_group():
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/groups/1/push_rule",
+ json=push_rules_content,
+ content_type="application/json",
+ status=200,
+ )
+ rsps.add(
+ method=responses.PUT,
+ url="http://localhost/api/v4/groups/1/push_rule",
+ json=push_rules_content,
+ content_type="application/json",
+ status=201,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_delete_push_rules_group(no_content):
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/groups/1/push_rule",
+ json=push_rules_content,
+ content_type="application/json",
+ status=200,
+ )
+ rsps.add(
+ method=responses.DELETE,
+ url="http://localhost/api/v4/groups/1/push_rule",
+ json=no_content,
+ content_type="application/json",
+ status=204,
+ )
+ yield rsps
+
+
def test_get_group(gl, resp_groups):
data = gl.groups.get(1)
assert isinstance(data, gitlab.v4.objects.Group)
@@ -173,3 +252,27 @@ def test_refresh_group_import_status(group, resp_groups):
def test_transfer_group(gl, resp_transfer_group):
group = gl.groups.get(1, lazy=True)
group.transfer("test-namespace")
+
+
+def test_list_group_push_rules(group, resp_list_push_rules_group):
+ pr = group.pushrules.get()
+ assert pr
+ assert pr.deny_delete_tag
+
+
+def test_create_group_push_rule(group, resp_create_push_rules_group):
+ group.pushrules.create({"deny_delete_tag": True})
+
+
+def test_update_group_push_rule(
+ group,
+ resp_update_push_rules_group,
+):
+ pr = group.pushrules.get()
+ pr.deny_delete_tag = False
+ pr.save()
+
+
+def test_delete_group_push_rule(group, resp_delete_push_rules_group):
+ pr = group.pushrules.get()
+ pr.delete()