diff --git a/docs/gl_objects/groups.rst b/docs/gl_objects/groups.rst index c4d9fdc1e..b9b865a97 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 8cb505277..84fd6002e 100644 --- a/gitlab/v4/objects/groups.py +++ b/gitlab/v4/objects/groups.py @@ -33,6 +33,7 @@ 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 d1fe81c17..9b4980b16 100644 --- a/gitlab/v4/objects/push_rules.py +++ b/gitlab/v4/objects/push_rules.py @@ -12,6 +12,8 @@ from gitlab.types import RequiredOptional __all__ = [ + "GroupPushRules", + "GroupPushRulesManager", "ProjectPushRules", "ProjectPushRulesManager", ] @@ -27,16 +29,64 @@ class ProjectPushRulesManager( _path = "/projects/{project_id}/push_rule" _obj_cls = ProjectPushRules _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 2c91d38d8..cebdfc7b0 100644 --- a/tests/unit/objects/test_groups.py +++ b/tests/unit/objects/test_groups.py @@ -38,6 +38,19 @@ "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()
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: