| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -78,7 +78,7 @@ fi | |||
| 78 | 78 | cecho b "Starting GitLab..." | |
| 79 | 79 | # run GitLab with root password pre-set and as many unnecessary features disabled to speed up the startup | |
| 80 | 80 | docker run --detach \ | |
| 81 | - --hostname gitlab.foobar.com \ | ||
| 81 | + --hostname localhost \ | ||
| 82 | 82 | --env GITLAB_OMNIBUS_CONFIG="$gitlab_omnibus_config" \ | |
| 83 | 83 | --publish 443:443 --publish 80:80 --publish 2022:22 \ | |
| 84 | 84 | --name gitlab \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -67,50 +67,6 @@ def get_branch_access_levels(self, project_and_group_name, branch): | |||
| 67 | 67 | "projects/%s/protected_branches/%s", (project_and_group_name, branch) | |
| 68 | 68 | ) | |
| 69 | 69 | ||
| 70 | - def get_only_branch_access_levels(self, project_and_group_name, branch): | ||
| 71 | - try: | ||
| 72 | - result = self._make_requests_to_api( | ||
| 73 | - "projects/%s/protected_branches/%s", (project_and_group_name, branch) | ||
| 74 | - ) | ||
| 75 | - | ||
| 76 | - push_access_levels = set() | ||
| 77 | - merge_access_levels = set() | ||
| 78 | - push_access_user_ids = set() | ||
| 79 | - merge_access_user_ids = set() | ||
| 80 | - unprotect_access_level = None | ||
| 81 | - | ||
| 82 | - if "push_access_levels" in result: | ||
| 83 | - for push_access in result["push_access_levels"]: | ||
| 84 | - if not push_access["user_id"]: | ||
| 85 | - push_access_levels.add(push_access["access_level"]) | ||
| 86 | - else: | ||
| 87 | - push_access_user_ids.add(push_access["user_id"]) | ||
| 88 | - | ||
| 89 | - if "merge_access_levels" in result: | ||
| 90 | - for merge_access in result["merge_access_levels"]: | ||
| 91 | - if not merge_access["user_id"]: | ||
| 92 | - merge_access_levels.add(merge_access["access_level"]) | ||
| 93 | - else: | ||
| 94 | - merge_access_user_ids.add(merge_access["user_id"]) | ||
| 95 | - | ||
| 96 | - if ( | ||
| 97 | - "unprotect_access_levels" in result | ||
| 98 | - and len(result["unprotect_access_levels"]) == 1 | ||
| 99 | - ): | ||
| 100 | - unprotect_access_level = result["unprotect_access_levels"][0][ | ||
| 101 | - "access_level" | ||
| 102 | - ] | ||
| 103 | - | ||
| 104 | - return ( | ||
| 105 | - sorted(push_access_levels), | ||
| 106 | - sorted(merge_access_levels), | ||
| 107 | - sorted(push_access_user_ids), | ||
| 108 | - sorted(merge_access_user_ids), | ||
| 109 | - unprotect_access_level, | ||
| 110 | - ) | ||
| 111 | - except NotFoundException: | ||
| 112 | - return None, None, None, None, None | ||
| 113 | - | ||
| 114 | 70 | def create_branch( | |
| 115 | 71 | self, project_and_group_name, new_branch_name, create_branch_from_ref | |
| 116 | 72 | ): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -98,10 +98,6 @@ def _get_project_id(self, project_and_group): | |||
| 98 | 98 | result = self.get_project(project_and_group) | |
| 99 | 99 | return str(result["id"]) | |
| 100 | 100 | ||
| 101 | - def has_no_license(self): | ||
| 102 | - license = self._make_requests_to_api("license") | ||
| 103 | - return not license or license["expired"] | ||
| 104 | - | ||
| 105 | 101 | def _make_requests_to_api( | |
| 106 | 102 | self, | |
| 107 | 103 | path_as_format_string, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -122,11 +122,6 @@ def put_group_settings(self, project_and_group_name, group_settings): | |||
| 122 | 122 | "groups/%s", project_and_group_name, "PUT", group_settings | |
| 123 | 123 | ) | |
| 124 | 124 | ||
| 125 | - def get_group_shared_with(self, group): | ||
| 126 | - group = self.get_group_case_insensitive(group) | ||
| 127 | - | ||
| 128 | - return group["shared_with_groups"] | ||
| 129 | - | ||
| 130 | 125 | def add_share_to_group( | |
| 131 | 126 | self, group, share_with_group_name, group_access, expires_at=None | |
| 132 | 127 | ): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,12 +9,6 @@ def get_project_members(self, project_and_group_name, all=False): | |||
| 9 | 9 | ||
| 10 | 10 | return self._make_requests_to_api(url_template, project_and_group_name) | |
| 11 | 11 | ||
| 12 | - def get_shared_with_groups(self, project_and_group_name): | ||
| 13 | - # a dict with groups that this project has been shared with | ||
| 14 | - return self._make_requests_to_api("projects/%s", project_and_group_name)[ | ||
| 15 | - "shared_with_groups" | ||
| 16 | - ] | ||
| 17 | - | ||
| 18 | 12 | def add_member_to_project( | |
| 19 | 13 | self, | |
| 20 | 14 | project_and_group_name, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,20 +7,6 @@ def get_tags(self, project_and_group_name): | |||
| 7 | 7 | "projects/%s/repository/tags", project_and_group_name | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | - def create_tag(self, project_and_group_name, tag_name, ref, message=None): | ||
| 11 | - data = { | ||
| 12 | - "tag_name": tag_name, | ||
| 13 | - "ref": ref, | ||
| 14 | - "message": message, | ||
| 15 | - } | ||
| 16 | - return self._make_requests_to_api( | ||
| 17 | - "projects/%s/repository/tags", | ||
| 18 | - project_and_group_name, | ||
| 19 | - method="POST", | ||
| 20 | - data=data, | ||
| 21 | - expected_codes=201, | ||
| 22 | - ) | ||
| 23 | - | ||
| 24 | 10 | def delete_tag(self, project_and_group_name, tag_name): | |
| 25 | 11 | self._make_requests_to_api( | |
| 26 | 12 | "projects/%s/repository/tags/%s", | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,11 +17,3 @@ def get_user_by_name(self, username, user_id=None): | |||
| 17 | 17 | if not user_id: | |
| 18 | 18 | user_id = self._get_user_id(username) | |
| 19 | 19 | return self._make_requests_to_api("users/%s", str(user_id), "GET") | |
| 20 | - | ||
| 21 | - def delete_user(self, username, user_id=None): | ||
| 22 | - if not user_id: | ||
| 23 | - user_id = self._get_user_id(username) | ||
| 24 | - # 404 means that the user does not exist anymore, so let's accept it for idempotency | ||
| 25 | - return self._make_requests_to_api( | ||
| 26 | - "users/%s", str(user_id), method="DELETE", expected_codes=[204, 404] | ||
| 27 | - ) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -60,6 +60,7 @@ def get_version_file_path(): | |||
| 60 | 60 | extras_require={ | |
| 61 | 61 | "test": [ | |
| 62 | 62 | "pytest==7.2.0", | |
| 63 | + "python-gitlab==3.11.0", | ||
| 63 | 64 | "xkcdpass==1.19.3", | |
| 64 | 65 | "pre-commit==2.20.0", # not really for tests, but for development | |
| 65 | 66 | "coverage==6.5.0", | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,11 +1,13 @@ | |||
| 1 | 1 | import os | |
| 2 | 2 | import textwrap | |
| 3 | + from contextlib import contextmanager | ||
| 4 | + from typing import Union, TYPE_CHECKING | ||
| 3 | 5 | ||
| 6 | + import gitlab | ||
| 7 | + from gitlab.v4.objects import Group, Project | ||
| 4 | 8 | from xkcdpass import xkcd_password as xp | |
| 5 | 9 | ||
| 6 | 10 | from gitlabform import GitLabForm | |
| 7 | - from gitlabform.gitlab import GitLab, AccessLevel | ||
| 8 | - from gitlabform.gitlab.core import NotFoundException, UnexpectedResponseException | ||
| 9 | 11 | ||
| 10 | 12 | CONFIG = """ | |
| 11 | 13 | config_version: 3 | |
@@ -38,7 +40,25 @@ | |||
| 38 | 40 | else: | |
| 39 | 41 | print(f"{file_path} doesn't exist.") | |
| 40 | 42 | ||
| 41 | - gl = GitLab(config_string=CONFIG) | ||
| 43 | + gl = gitlab.Gitlab( | ||
| 44 | + os.getenv("GITLAB_URL"), private_token=os.getenv("GITLAB_TOKEN"), per_page=100 | ||
| 45 | + ) | ||
| 46 | + | ||
| 47 | + | ||
| 48 | + @contextmanager | ||
| 49 | + def allowed_codes(codes: Union[int, list[int]]): | ||
| 50 | + """ | ||
| 51 | + A context manager that allows failures on specific response status codes | ||
| 52 | + when gitlab.Gitlab objects. | ||
| 53 | + """ | ||
| 54 | + if isinstance(codes, int): | ||
| 55 | + codes = [codes] | ||
| 56 | + | ||
| 57 | + try: | ||
| 58 | + yield | ||
| 59 | + except gitlab.GitlabOperationError as e: | ||
| 60 | + if e.response_code not in codes: | ||
| 61 | + raise e | ||
| 42 | 62 | ||
| 43 | 63 | ||
| 44 | 64 | def get_random_name(entity: str) -> str: | |
@@ -54,108 +74,127 @@ def get_random_suffix(): | |||
| 54 | 74 | return xp.generate_xkcdpassword(wordlist=my_words, numwords=1, delimiter="_") | |
| 55 | 75 | ||
| 56 | 76 | ||
| 57 | - def get_gitlab(): | ||
| 58 | - return gl | ||
| 59 | - | ||
| 60 | - | ||
| 61 | - def create_group(group_name, parent_id=None): | ||
| 62 | - gl.create_group(group_name, group_name, parent_id=parent_id, visibility="internal") | ||
| 77 | + def create_group(group_name, parent_id=None) -> Group: | ||
| 78 | + with allowed_codes(404): | ||
| 79 | + group = gl.groups.create( | ||
| 80 | + { | ||
| 81 | + "name": group_name, | ||
| 82 | + "path": group_name, | ||
| 83 | + "parent_id": parent_id, | ||
| 84 | + "visibility": "internal", | ||
| 85 | + } | ||
| 86 | + ) | ||
| 87 | + if TYPE_CHECKING: | ||
| 88 | + assert isinstance(group, Group) | ||
| 89 | + return group | ||
| 63 | 90 | ||
| 64 | 91 | ||
| 65 | 92 | def create_groups(group_base_name, no_of_groups): | |
| 66 | 93 | groups = [] | |
| 67 | 94 | for group_no in range(1, no_of_groups + 1): | |
| 68 | 95 | group_name = group_base_name + str(group_no) | |
| 69 | 96 | try: | |
| 70 | - gl.get_group_case_insensitive(group_name) | ||
| 71 | - except NotFoundException: | ||
| 72 | - gl.create_group(group_name, group_name) | ||
| 73 | - groups.append(group_name) | ||
| 97 | + group = gl.groups.get(group_name) | ||
| 98 | + except gitlab.GitlabGetError: | ||
| 99 | + group = create_group(group_name) | ||
| 100 | + groups.append(group) | ||
| 74 | 101 | return groups | |
| 75 | 102 | ||
| 76 | 103 | ||
| 77 | 104 | def delete_groups(group_base_name, no_of_groups): | |
| 78 | 105 | for group_no in range(1, no_of_groups + 1): | |
| 79 | 106 | group_name = group_base_name + str(group_no) | |
| 80 | - gl.delete_group(group_name) | ||
| 81 | - | ||
| 82 | - | ||
| 83 | - def create_project(group_name, project_name): | ||
| 84 | - group = gl.get_group(group_name) | ||
| 85 | - gl.create_project( | ||
| 86 | - project_name, | ||
| 87 | - project_name, | ||
| 88 | - group["id"], | ||
| 89 | - default_branch="main", | ||
| 90 | - wait_if_still_being_deleted=True, | ||
| 107 | + with allowed_codes(404): | ||
| 108 | + gl.groups.delete(group_name) | ||
| 109 | + | ||
| 110 | + | ||
| 111 | + def create_project(group: Group, project_name) -> Project: | ||
| 112 | + project = gl.projects.create( | ||
| 113 | + { | ||
| 114 | + "name": project_name, | ||
| 115 | + "path": project_name, | ||
| 116 | + "namespace_id": group.id, | ||
| 117 | + "default_branch": "main", | ||
| 118 | + } | ||
| 91 | 119 | ) | |
| 92 | 120 | ||
| 93 | - gl.add_file( | ||
| 94 | - f"{group_name}/{project_name}", | ||
| 95 | - "main", | ||
| 96 | - "README.md", | ||
| 97 | - DEFAULT_README, | ||
| 98 | - "Create README", | ||
| 121 | + project.files.create( | ||
| 122 | + { | ||
| 123 | + "branch": "main", | ||
| 124 | + "file_path": "README.md", | ||
| 125 | + "content": DEFAULT_README, | ||
| 126 | + "commit_message": "Create README", | ||
| 127 | + } | ||
| 99 | 128 | ) | |
| 129 | + if TYPE_CHECKING: | ||
| 130 | + assert isinstance(project, Project) | ||
| 131 | + return project | ||
| 100 | 132 | ||
| 101 | 133 | ||
| 102 | 134 | def create_users(user_base_name, no_of_users): | |
| 103 | 135 | users = [] | |
| 104 | 136 | for user_no in range(1, no_of_users + 1): | |
| 105 | 137 | username = user_base_name + str(user_no) | |
| 138 | + existing = gl.users.list(username=username) | ||
| 106 | 139 | try: | |
| 107 | - gl.get_user_by_name(username) | ||
| 108 | - except NotFoundException: | ||
| 109 | - gl.create_user( | ||
| 110 | - username + "@example.com", username + " Example", username, "password" | ||
| 140 | + user = existing[0] | ||
| 141 | + except IndexError: | ||
| 142 | + user = gl.users.create( | ||
| 143 | + { | ||
| 144 | + "username": username, | ||
| 145 | + "email": username + "@example.com", | ||
| 146 | + "name": username + " Example", | ||
| 147 | + "password": username + "password", | ||
| 148 | + } | ||
| 111 | 149 | ) | |
| 112 | - users.append(username) | ||
| 150 | + users.append(user) | ||
| 113 | 151 | return users | |
| 114 | 152 | ||
| 115 | 153 | ||
| 116 | - def delete_users(user_base_name, no_of_users): | ||
| 117 | - for user_no in range(1, no_of_users + 1): | ||
| 118 | - username = user_base_name + str(user_no) | ||
| 119 | - gl.delete_user(username) | ||
| 154 | + def get_only_branch_access_levels(project: Project, branch): | ||
| 155 | + protected_branch = None | ||
| 120 | 156 | ||
| 157 | + with allowed_codes(404): | ||
| 158 | + protected_branch = project.protectedbranches.get(branch) | ||
| 121 | 159 | ||
| 122 | - def remove_users_from_project(user_base_name, no_of_users, project_and_group): | ||
| 123 | - for user_no in range(1, no_of_users + 1): | ||
| 124 | - username = user_base_name + str(user_no) | ||
| 125 | - gl.remove_member_from_project(project_and_group, username) | ||
| 160 | + if not protected_branch: | ||
| 161 | + return None, None, None, None, None | ||
| 126 | 162 | ||
| 163 | + push_access_levels = set() | ||
| 164 | + merge_access_levels = set() | ||
| 165 | + push_access_user_ids = set() | ||
| 166 | + merge_access_user_ids = set() | ||
| 167 | + unprotect_access_level = None | ||
| 127 | 168 | ||
| 128 | - def add_users_to_group(group_name, usernames, access_level=AccessLevel.DEVELOPER.value): | ||
| 129 | - for username in usernames: | ||
| 130 | - try: | ||
| 131 | - gl.add_member_to_group(group_name, username, access_level) | ||
| 132 | - except UnexpectedResponseException: | ||
| 133 | - # this is fine - user is already in the group | ||
| 134 | - pass | ||
| 135 | - | ||
| 136 | - | ||
| 137 | - def remove_users_from_group(group_name, usernames): | ||
| 138 | - for username in usernames: | ||
| 139 | - try: | ||
| 140 | - gl.remove_member_from_group(group_name, username) | ||
| 141 | - except NotFoundException: | ||
| 142 | - # this is fine - user is removed from group | ||
| 143 | - pass | ||
| 144 | - | ||
| 145 | - | ||
| 146 | - def delete_variables_from_group(group_name, variables): | ||
| 147 | - for variable in variables: | ||
| 148 | - try: | ||
| 149 | - gl.delete_group_variable(group_name, variable) | ||
| 150 | - except NotFoundException: | ||
| 151 | - # this is fine - variable is removed from group | ||
| 152 | - pass | ||
| 153 | - | ||
| 169 | + if "push_access_levels" in protected_branch.attributes: | ||
| 170 | + for push_access in protected_branch.push_access_levels: | ||
| 171 | + if not push_access["user_id"]: | ||
| 172 | + push_access_levels.add(push_access["access_level"]) | ||
| 173 | + else: | ||
| 174 | + push_access_user_ids.add(push_access["user_id"]) | ||
| 154 | 175 | ||
| 155 | - def delete_pipeline_schedules_from_project(project_and_group): | ||
| 156 | - schedules = gl.get_all_pipeline_schedules(project_and_group) | ||
| 157 | - for schedule in schedules: | ||
| 158 | - gl.delete_pipeline_schedule(project_and_group, schedule["id"]) | ||
| 176 | + if "merge_access_levels" in protected_branch.attributes: | ||
| 177 | + for merge_access in protected_branch.merge_access_levels: | ||
| 178 | + if not merge_access["user_id"]: | ||
| 179 | + merge_access_levels.add(merge_access["access_level"]) | ||
| 180 | + else: | ||
| 181 | + merge_access_user_ids.add(merge_access["user_id"]) | ||
| 182 | + | ||
| 183 | + if ( | ||
| 184 | + "unprotect_access_levels" in protected_branch.attributes | ||
| 185 | + and len(protected_branch.unprotect_access_levels) == 1 | ||
| 186 | + ): | ||
| 187 | + unprotect_access_level = protected_branch.unprotect_access_levels[0][ | ||
| 188 | + "access_level" | ||
| 189 | + ] | ||
| 190 | + | ||
| 191 | + return ( | ||
| 192 | + sorted(push_access_levels), | ||
| 193 | + sorted(merge_access_levels), | ||
| 194 | + sorted(push_access_user_ids), | ||
| 195 | + sorted(merge_access_user_ids), | ||
| 196 | + unprotect_access_level, | ||
| 197 | + ) | ||
| 159 | 198 | ||
| 160 | 199 | ||
| 161 | 200 | def run_gitlabform(config, target, include_archived_projects=True): | |
@@ -165,6 +204,12 @@ def run_gitlabform(config, target, include_archived_projects=True): | |||
| 165 | 204 | # we don't want to repeat ourselves in the tests, so prefix the configs with this mandatory part here | |
| 166 | 205 | config = CONFIG + config | |
| 167 | 206 | ||
| 207 | + # allow passing in gitlab RESTObjects. assume full path string otherwise | ||
| 208 | + if isinstance(target, Group): | ||
| 209 | + target = target.full_path | ||
| 210 | + elif isinstance(target, Project): | ||
| 211 | + target = target.path_with_namespace | ||
| 212 | + | ||
| 168 | 213 | gf = GitLabForm( | |
| 169 | 214 | include_archived_projects=include_archived_projects, | |
| 170 | 215 | config_string=config, | |
| Back | FazBrowse Home | New Git URL |
0 commit comments