gitlabform 4.0.5__tar.gz → 4.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gitlabform-4.0.5 → gitlabform-4.0.6}/PKG-INFO +13 -2
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/__init__.py +9 -4
- gitlabform-4.0.6/gitlabform/gitlab/python_gitlab.py +254 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_members_processor.py +9 -8
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/branches_processor.py +9 -4
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/members_processor.py +15 -17
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/tags_processor.py +22 -8
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform.egg-info/PKG-INFO +13 -2
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform.egg-info/requires.txt +1 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/setup.py +1 -0
- gitlabform-4.0.5/gitlabform/gitlab/python_gitlab.py +0 -111
- {gitlabform-4.0.5 → gitlabform-4.0.6}/LICENSE +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/README.md +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/configuration/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/configuration/common.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/configuration/core.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/configuration/groups.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/configuration/projects.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/configuration/transform.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/constants.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/commits.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/core.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/group_badges.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/group_ldap_links.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/group_variables.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/groups.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/merge_requests.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/pipelines.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/project_badges.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/project_deploy_keys.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/project_merge_requests_approvals.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/project_protected_environments.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/projects.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/users.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/gitlab/variables.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/lists/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/lists/filter.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/lists/groups.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/lists/projects.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/output.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/abstract_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/application/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/application/application_settings_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/defining_keys.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_badges_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_labels_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_ldap_links_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_saml_links_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_settings_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_variables_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/multiple_entities_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/badges_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/deploy_keys_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/files_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/hooks_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/integrations_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/job_token_scope_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/merge_requests_approval_rules.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/merge_requests_approvals.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/project_labels_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/project_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/project_push_rules_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/project_settings_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/resource_groups_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/schedules_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/variables_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/shared/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/shared/protected_environments_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/single_entity_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/util/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/util/decorators.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/util/difference_logger.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/util/labels_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/run.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/util.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform.egg-info/SOURCES.txt +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform.egg-info/dependency_links.txt +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform.egg-info/entry_points.txt +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform.egg-info/top_level.txt +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/setup.cfg +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/conftest.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_application_settings.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_archive_project.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_badges.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_branches.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_deploy_keys.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_deploy_keys_all_projects.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_files.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_files_templates.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_group_badges.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_group_labels.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_group_members_case_insensitive.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_group_members_groups.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_group_members_users.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_group_settings.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_group_variables.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_hooks.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_integrations.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_job_token_scope.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_members.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_members_add_group.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_members_enforce.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_project_group_members_case_insensitive.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_project_labels.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_project_members_case_insensitve.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_project_settings.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_resource_groups.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_running.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_schedules.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_tags.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_token_from_config.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_transfer_project.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_variables.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_case_sensitivity.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_inheritance_break_projects_and_groups.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_inheritance_break_subgroups.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_inheritance_break_validation.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_projects_and_groups.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_skip_groups_skip_projects.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_subgroups.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_yaml_version.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/processors/__init__.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/processors/test_abstract_processor.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/processors/test_difference_logger.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/processors/test_schedules_processor_extended_cron_pattern.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/test_access_levels.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/test_non_empty_configs_provider.py +0 -0
- {gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: gitlabform
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.6
|
|
4
4
|
Summary: 🏗 Specialized configuration as a code tool for GitLab projects, groups and more using hierarchical configuration written in YAML
|
|
5
5
|
Home-page: https://gitlabform.github.io/gitlabform
|
|
6
6
|
Author: Greg Dubicki and Contributors
|
|
@@ -26,6 +26,7 @@ Requires-Dist: MarkupSafe==3.0.2
|
|
|
26
26
|
Requires-Dist: mergedeep==1.3.4
|
|
27
27
|
Requires-Dist: packaging==24.2
|
|
28
28
|
Requires-Dist: python-gitlab==5.3.0
|
|
29
|
+
Requires-Dist: python-gitlab[graphql]==5.3.0
|
|
29
30
|
Requires-Dist: requests==2.32.3
|
|
30
31
|
Requires-Dist: ruamel.yaml==0.17.21
|
|
31
32
|
Requires-Dist: types-requests==2.32.0.20241016
|
|
@@ -44,6 +45,16 @@ Requires-Dist: xkcdpass==1.19.9; extra == "test"
|
|
|
44
45
|
Provides-Extra: docs
|
|
45
46
|
Requires-Dist: mkdocs; extra == "docs"
|
|
46
47
|
Requires-Dist: mkdocs-material; extra == "docs"
|
|
48
|
+
Dynamic: author
|
|
49
|
+
Dynamic: classifier
|
|
50
|
+
Dynamic: description
|
|
51
|
+
Dynamic: description-content-type
|
|
52
|
+
Dynamic: home-page
|
|
53
|
+
Dynamic: keywords
|
|
54
|
+
Dynamic: provides-extra
|
|
55
|
+
Dynamic: requires-dist
|
|
56
|
+
Dynamic: requires-python
|
|
57
|
+
Dynamic: summary
|
|
47
58
|
|
|
48
59
|
[](https://badge.fury.io/gh/gitlabform%2Fgitlabform)
|
|
49
60
|

|
|
@@ -2,6 +2,8 @@ import enum
|
|
|
2
2
|
|
|
3
3
|
from typing import List
|
|
4
4
|
|
|
5
|
+
from gitlab import GraphQL
|
|
6
|
+
|
|
5
7
|
from gitlabform.gitlab.commits import GitLabCommits
|
|
6
8
|
from gitlabform.gitlab.group_badges import GitLabGroupBadges
|
|
7
9
|
from gitlabform.gitlab.group_ldap_links import GitLabGroupLDAPLinks
|
|
@@ -71,14 +73,17 @@ class GitlabWrapper:
|
|
|
71
73
|
timeout = gitlabform.timeout
|
|
72
74
|
session = gitlabform.session
|
|
73
75
|
|
|
76
|
+
graphql = GraphQL(url=url, token=token)
|
|
77
|
+
|
|
74
78
|
self._gitlab: PythonGitlab = PythonGitlab(
|
|
75
|
-
url,
|
|
76
|
-
token,
|
|
79
|
+
url=url,
|
|
80
|
+
private_token=token,
|
|
77
81
|
ssl_verify=ssl_verify,
|
|
82
|
+
timeout=timeout,
|
|
78
83
|
api_version="4",
|
|
79
|
-
session=session,
|
|
80
84
|
retry_transient_errors=True,
|
|
81
|
-
|
|
85
|
+
graphql=graphql,
|
|
86
|
+
session=session,
|
|
82
87
|
)
|
|
83
88
|
|
|
84
89
|
def get_gitlab(self):
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from typing import Union, Any, Optional, Dict, List
|
|
3
|
+
|
|
4
|
+
import gitlab.const
|
|
5
|
+
from gitlab import Gitlab, GitlabGetError, GraphQL
|
|
6
|
+
from gitlab.base import RESTObject
|
|
7
|
+
from gitlab.v4.objects import Group, Project
|
|
8
|
+
|
|
9
|
+
from cli_ui import debug as verbose
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Extends the python-gitlab class to add convenience wrappers for common functionality used within gitlabform
|
|
13
|
+
class PythonGitlab(Gitlab):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
graphql: GraphQL,
|
|
17
|
+
url: Optional[str] = None,
|
|
18
|
+
private_token: Optional[str] = None,
|
|
19
|
+
oauth_token: Optional[str] = None,
|
|
20
|
+
job_token: Optional[str] = None,
|
|
21
|
+
ssl_verify: Union[bool, str] = True,
|
|
22
|
+
http_username: Optional[str] = None,
|
|
23
|
+
http_password: Optional[str] = None,
|
|
24
|
+
timeout: Optional[float] = None,
|
|
25
|
+
api_version: str = "4",
|
|
26
|
+
per_page: Optional[int] = None,
|
|
27
|
+
pagination: Optional[str] = None,
|
|
28
|
+
order_by: Optional[str] = None,
|
|
29
|
+
user_agent: str = gitlab.const.USER_AGENT,
|
|
30
|
+
retry_transient_errors: bool = False,
|
|
31
|
+
keep_base_url: bool = False,
|
|
32
|
+
**kwargs: Any,
|
|
33
|
+
) -> None:
|
|
34
|
+
super().__init__(
|
|
35
|
+
url,
|
|
36
|
+
private_token,
|
|
37
|
+
oauth_token,
|
|
38
|
+
job_token,
|
|
39
|
+
ssl_verify,
|
|
40
|
+
http_username,
|
|
41
|
+
http_password,
|
|
42
|
+
timeout,
|
|
43
|
+
api_version,
|
|
44
|
+
per_page,
|
|
45
|
+
pagination,
|
|
46
|
+
order_by,
|
|
47
|
+
user_agent,
|
|
48
|
+
retry_transient_errors,
|
|
49
|
+
keep_base_url,
|
|
50
|
+
**kwargs,
|
|
51
|
+
)
|
|
52
|
+
# Python Gitlab GraphQL interface
|
|
53
|
+
# https://python-gitlab.readthedocs.io/en/stable/api-usage-graphql.html
|
|
54
|
+
self.graphql = graphql
|
|
55
|
+
|
|
56
|
+
@functools.lru_cache()
|
|
57
|
+
def get_user_id_cached(self, username) -> int:
|
|
58
|
+
user = self._get_user_by_username_cached(username)
|
|
59
|
+
return user.id
|
|
60
|
+
|
|
61
|
+
def get_group_id(self, groupname) -> int:
|
|
62
|
+
group = self.get_group_by_path_cached(groupname)
|
|
63
|
+
return group.id
|
|
64
|
+
|
|
65
|
+
def get_project_id(self, name) -> int:
|
|
66
|
+
project = self.get_project_by_path_cached(name)
|
|
67
|
+
return project.id
|
|
68
|
+
|
|
69
|
+
@functools.lru_cache()
|
|
70
|
+
def get_project_by_path_cached(self, name: str, lazy: bool = False) -> Project:
|
|
71
|
+
project: Project = self.projects.get(name, lazy)
|
|
72
|
+
if project:
|
|
73
|
+
return project
|
|
74
|
+
|
|
75
|
+
raise GitlabGetError("No project found when getting '%s'" % name, 404)
|
|
76
|
+
|
|
77
|
+
@functools.lru_cache()
|
|
78
|
+
def get_group_by_path_cached(self, groupname: str) -> Group:
|
|
79
|
+
group: Group = self.groups.get(groupname)
|
|
80
|
+
if group:
|
|
81
|
+
return group
|
|
82
|
+
|
|
83
|
+
raise GitlabGetError("No group found when getting '%s'" % groupname, 404)
|
|
84
|
+
|
|
85
|
+
# Uses "LIST" to get a user by username, to get the full User object, call get using the user's id
|
|
86
|
+
@functools.lru_cache()
|
|
87
|
+
def _get_user_by_username_cached(self, username: str) -> RESTObject:
|
|
88
|
+
# Gitlab API will only ever return 0 or 1 entry when GETting using `username` attribute
|
|
89
|
+
# https://docs.gitlab.com/ee/api/users.html#for-non-administrator-users
|
|
90
|
+
# so will always be list[RESTObject] and never RESTObjectList from python-gitlab's api
|
|
91
|
+
users: list[RESTObject] = self.users.list(username=username) # type: ignore
|
|
92
|
+
|
|
93
|
+
if len(users) == 0:
|
|
94
|
+
raise GitlabGetError(
|
|
95
|
+
"No users found when searching for username '%s'" % username, 404
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return users[0]
|
|
99
|
+
|
|
100
|
+
@functools.lru_cache()
|
|
101
|
+
def _get_member_roles_from_group_cached(
|
|
102
|
+
self, group_full_path: str
|
|
103
|
+
) -> List[Dict[str, str]]:
|
|
104
|
+
"""Query GraphQL using Python Gitlab
|
|
105
|
+
https://python-gitlab.readthedocs.io/en/stable/api-usage-graphql.html
|
|
106
|
+
|
|
107
|
+
GET Member_Role via REST Api needs "Owner" on Gitlab.com or "Admin" on Dedicated
|
|
108
|
+
List of custom roles can be retrieved via GraphQL endpoint with "Guest+"
|
|
109
|
+
https://gitlab.com/gitlab-org/gitlab/-/issues/511919#note_2287581884
|
|
110
|
+
|
|
111
|
+
https://docs.gitlab.com/ee/api/graphql/reference/index.html#groupmemberroles
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
if group_full_path is None:
|
|
115
|
+
raise GitlabGetError(
|
|
116
|
+
"Group Path must be provided when getting member roles",
|
|
117
|
+
404,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
query = (
|
|
121
|
+
"""
|
|
122
|
+
{
|
|
123
|
+
group(fullPath: \""""
|
|
124
|
+
+ group_full_path
|
|
125
|
+
+ """\") {
|
|
126
|
+
memberRoles {
|
|
127
|
+
nodes {
|
|
128
|
+
id
|
|
129
|
+
name
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
"""
|
|
135
|
+
)
|
|
136
|
+
verbose(
|
|
137
|
+
f"Executing graphQl query to get Member Roles for Group '{group_full_path}'"
|
|
138
|
+
)
|
|
139
|
+
result = self.graphql.execute(query)
|
|
140
|
+
|
|
141
|
+
# Validate Group / MemberRoles exist
|
|
142
|
+
if (
|
|
143
|
+
result["group"] is not None
|
|
144
|
+
and result["group"]["memberRoles"] is not None
|
|
145
|
+
and result["group"]["memberRoles"]["nodes"] is not None
|
|
146
|
+
):
|
|
147
|
+
member_role_nodes = result["group"]["memberRoles"]["nodes"]
|
|
148
|
+
else:
|
|
149
|
+
raise GitlabGetError(f"Failed to get Member Roles from Group: {query}")
|
|
150
|
+
|
|
151
|
+
return self._convert_result_to_member_roles(member_role_nodes)
|
|
152
|
+
|
|
153
|
+
@functools.lru_cache()
|
|
154
|
+
def _get_member_roles_from_instance_cached(self) -> List[Dict[str, str]]:
|
|
155
|
+
"""Query GraphQL using Python Gitlab
|
|
156
|
+
https://python-gitlab.readthedocs.io/en/stable/api-usage-graphql.html
|
|
157
|
+
|
|
158
|
+
GET Member_Role via REST Api needs "Admin" on Dedicated/Self-Hosted
|
|
159
|
+
|
|
160
|
+
On Self-Hosted/Dedicated it is only possible to create Member Roles at an instance level, so we query at that
|
|
161
|
+
level and cache the result in order to save API calls when processing multiple Groups
|
|
162
|
+
|
|
163
|
+
List of custom roles can be retrieved via GraphQL endpoint with "Guest+"
|
|
164
|
+
https://gitlab.com/gitlab-org/gitlab/-/issues/511919#note_2287581884
|
|
165
|
+
|
|
166
|
+
https://docs.gitlab.com/ee/api/graphql/reference/index.html#groupmemberroles
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
query = """
|
|
170
|
+
{
|
|
171
|
+
memberRoles {
|
|
172
|
+
edges {
|
|
173
|
+
node {
|
|
174
|
+
id
|
|
175
|
+
name
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
"""
|
|
181
|
+
result = self.graphql.execute(query)
|
|
182
|
+
|
|
183
|
+
if (
|
|
184
|
+
result["memberRoles"] is not None
|
|
185
|
+
and result["memberRoles"]["edges"] is not None
|
|
186
|
+
):
|
|
187
|
+
member_role_edges = result["memberRoles"]["edges"]
|
|
188
|
+
else:
|
|
189
|
+
raise GitlabGetError(f"Failed to get Member Roles from instance: {query}")
|
|
190
|
+
|
|
191
|
+
member_role_nodes = []
|
|
192
|
+
|
|
193
|
+
for edge in member_role_edges:
|
|
194
|
+
member_role_nodes.append(edge["node"])
|
|
195
|
+
|
|
196
|
+
return self._convert_result_to_member_roles(member_role_nodes)
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def _convert_result_to_member_roles(
|
|
200
|
+
member_role_nodes: List[Dict[str, str]]
|
|
201
|
+
) -> List[Dict[str, str]]:
|
|
202
|
+
# Unwrap Ids from GraphQl Gids
|
|
203
|
+
member_role_id_prefix = "gid://gitlab/MemberRole/"
|
|
204
|
+
|
|
205
|
+
member_roles = []
|
|
206
|
+
for node in member_role_nodes:
|
|
207
|
+
member_role_id = node["id"]
|
|
208
|
+
member_role_id = member_role_id.replace(member_role_id_prefix, "")
|
|
209
|
+
member_roles.append({"id": member_role_id, "name": node["name"]})
|
|
210
|
+
|
|
211
|
+
return member_roles
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def _get_member_role_from_member_roles(
|
|
215
|
+
name_or_id: Union[int, str], member_roles: List[Dict[str, str]]
|
|
216
|
+
) -> Dict[str, str]:
|
|
217
|
+
for member_role in member_roles:
|
|
218
|
+
member_role_id: str = member_role["id"]
|
|
219
|
+
member_role_name: str = member_role["name"]
|
|
220
|
+
if int(member_role_id) == name_or_id:
|
|
221
|
+
return member_role
|
|
222
|
+
elif member_role_name.lower() == str(name_or_id).lower():
|
|
223
|
+
return member_role
|
|
224
|
+
|
|
225
|
+
# Failed to find member role so throw an exception explaining such to user
|
|
226
|
+
raise GitlabGetError(
|
|
227
|
+
f"Member Role with name or id {name_or_id} could not be found",
|
|
228
|
+
404,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
def get_member_role_id_cached(
|
|
232
|
+
self, name_or_id: Union[int, str], group_full_path: str
|
|
233
|
+
) -> int:
|
|
234
|
+
"""
|
|
235
|
+
GETs a member role id set in the config by the Name of the Member Role (by calling out to API) or by Id
|
|
236
|
+
|
|
237
|
+
Get member_roles calls themselves are Cached, the logic in this method otherwise performs no API calls,
|
|
238
|
+
so we should not cache the result of this specific method, otherwise we may fill the cache with duplicate data
|
|
239
|
+
"""
|
|
240
|
+
if type(name_or_id) is int:
|
|
241
|
+
# Already supplied as an id so no need to go get it from API
|
|
242
|
+
return name_or_id
|
|
243
|
+
|
|
244
|
+
if self._is_gitlab_saas():
|
|
245
|
+
member_roles = self._get_member_roles_from_group_cached(group_full_path)
|
|
246
|
+
else:
|
|
247
|
+
member_roles = self._get_member_roles_from_instance_cached()
|
|
248
|
+
|
|
249
|
+
member_role = self._get_member_role_from_member_roles(name_or_id, member_roles)
|
|
250
|
+
|
|
251
|
+
return int(member_role["id"])
|
|
252
|
+
|
|
253
|
+
def _is_gitlab_saas(self) -> bool:
|
|
254
|
+
return self.url == gitlab.const.DEFAULT_URL
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_members_processor.py
RENAMED
|
@@ -208,13 +208,13 @@ class GroupMembersProcessor(AbstractProcessor):
|
|
|
208
208
|
)
|
|
209
209
|
if member_role_id_or_name:
|
|
210
210
|
member_role_id_to_set = self.gl.get_member_role_id_cached(
|
|
211
|
-
member_role_id_or_name, group.
|
|
211
|
+
member_role_id_or_name, group.full_path
|
|
212
212
|
)
|
|
213
213
|
else:
|
|
214
214
|
member_role_id_to_set = None
|
|
215
215
|
|
|
216
216
|
common_username = user.lower()
|
|
217
|
-
user_id = self.gl.
|
|
217
|
+
user_id = self.gl.get_user_id_cached(user)
|
|
218
218
|
|
|
219
219
|
if common_username in users_before:
|
|
220
220
|
group_member: GroupMember = group.members.get(user_id)
|
|
@@ -275,12 +275,7 @@ class GroupMembersProcessor(AbstractProcessor):
|
|
|
275
275
|
continue
|
|
276
276
|
debug("Removing user '%s' who is not configured to be a member.", user)
|
|
277
277
|
try:
|
|
278
|
-
user_id = self.gl.
|
|
279
|
-
try:
|
|
280
|
-
group.members.delete(user_id)
|
|
281
|
-
except GitlabDeleteError as delete_error:
|
|
282
|
-
warning(f"Member '{user}' could not be deleted: {delete_error}")
|
|
283
|
-
pass
|
|
278
|
+
user_id = self.gl.get_user_id_cached(user)
|
|
284
279
|
except gitlab.GitlabGetError:
|
|
285
280
|
# User does not exist an instance level but is for whatever reason present on a Group/Project
|
|
286
281
|
# We should raise error into Logs but not prevent the rest of GitLabForm from executing
|
|
@@ -289,6 +284,12 @@ class GroupMembersProcessor(AbstractProcessor):
|
|
|
289
284
|
error(
|
|
290
285
|
f"Could not find User '{user}' on the Instance so can not remove User"
|
|
291
286
|
)
|
|
287
|
+
continue
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
group.members.delete(user_id)
|
|
291
|
+
except GitlabDeleteError as delete_error:
|
|
292
|
+
warning(f"Member '{user}' could not be deleted: {delete_error}")
|
|
292
293
|
pass
|
|
293
294
|
|
|
294
295
|
else:
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
from logging import debug
|
|
1
2
|
from typing import Optional
|
|
2
|
-
from
|
|
3
|
-
from cli_ui import warning, fatal
|
|
4
|
-
from gitlab.v4.objects import Project, ProjectProtectedBranch
|
|
3
|
+
from cli_ui import warning, fatal, debug as verbose
|
|
5
4
|
from gitlab import (
|
|
6
5
|
GitlabGetError,
|
|
7
6
|
GitlabDeleteError,
|
|
8
7
|
GitlabCreateError,
|
|
9
8
|
)
|
|
9
|
+
from gitlab.v4.objects import Project, ProjectProtectedBranch
|
|
10
|
+
|
|
11
|
+
from gitlabform import gitlab
|
|
10
12
|
from gitlabform.constants import EXIT_INVALID_INPUT, EXIT_PROCESSING_ERROR
|
|
11
13
|
from gitlabform.gitlab import GitLab
|
|
12
14
|
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
@@ -123,13 +125,16 @@ class BranchesProcessor(AbstractProcessor):
|
|
|
123
125
|
or group name but GitLab only supports their id. This method will transform the
|
|
124
126
|
config by replacing them with ids.
|
|
125
127
|
"""
|
|
128
|
+
verbose("Transforming User and Group names in Branch configuration to Ids")
|
|
126
129
|
|
|
127
130
|
for key in branch_config:
|
|
128
131
|
if isinstance(branch_config[key], list):
|
|
129
132
|
for item in branch_config[key]:
|
|
130
133
|
if isinstance(item, dict):
|
|
131
134
|
if "user" in item:
|
|
132
|
-
item["user_id"] = self.gl.
|
|
135
|
+
item["user_id"] = self.gl.get_user_id_cached(
|
|
136
|
+
item.pop("user")
|
|
137
|
+
)
|
|
133
138
|
elif "group" in item:
|
|
134
139
|
item["group_id"] = self.gl.get_group_id(item.pop("group"))
|
|
135
140
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import gitlab
|
|
2
1
|
from cli_ui import debug as verbose, warning, info, error
|
|
3
2
|
from cli_ui import fatal
|
|
4
|
-
from gitlab import GitlabGetError
|
|
3
|
+
from gitlab import GitlabGetError, GitlabDeleteError
|
|
5
4
|
from gitlab.v4.objects import Project
|
|
6
5
|
|
|
7
6
|
from gitlabform.constants import EXIT_INVALID_INPUT
|
|
@@ -114,7 +113,7 @@ class MembersProcessor(AbstractProcessor):
|
|
|
114
113
|
for user in users:
|
|
115
114
|
info(f"Processing user '{user}'...")
|
|
116
115
|
try:
|
|
117
|
-
user_id = self.gl.
|
|
116
|
+
user_id = self.gl.get_user_id_cached(user)
|
|
118
117
|
except GitlabGetError:
|
|
119
118
|
warning(f"Could not find user '{user}' in Gitlab, skipping...")
|
|
120
119
|
continue
|
|
@@ -133,14 +132,9 @@ class MembersProcessor(AbstractProcessor):
|
|
|
133
132
|
users[user]["member_role"] if "member_role" in users[user] else None
|
|
134
133
|
)
|
|
135
134
|
if member_role_id_or_name:
|
|
136
|
-
|
|
137
|
-
if self.gl.is_gitlab_saas():
|
|
138
|
-
group_id = project.namespace["id"]
|
|
139
|
-
else:
|
|
140
|
-
group_id = None
|
|
141
|
-
|
|
135
|
+
group_name_and_path = project.namespace["full_path"]
|
|
142
136
|
member_role_id = self.gl.get_member_role_id_cached(
|
|
143
|
-
member_role_id_or_name,
|
|
137
|
+
member_role_id_or_name, group_name_and_path
|
|
144
138
|
)
|
|
145
139
|
else:
|
|
146
140
|
member_role_id = None
|
|
@@ -224,20 +218,24 @@ class MembersProcessor(AbstractProcessor):
|
|
|
224
218
|
f"Removing user '{user_not_in_config}' that is not configured to be a member."
|
|
225
219
|
)
|
|
226
220
|
try:
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
).get_id()
|
|
230
|
-
project.members.delete(id=cached_user_id)
|
|
231
|
-
except gitlab.GitlabGetError:
|
|
221
|
+
user_id = self.gl.get_user_id_cached(user_not_in_config)
|
|
222
|
+
except GitlabGetError:
|
|
232
223
|
# User does not exist an instance level but is for whatever reason present on a Group/Project
|
|
233
224
|
# We should raise error into Logs but not prevent the rest of GitLabForm from executing
|
|
234
225
|
# This error is more likely to be prevalent in Dedicated instances; it is unlikely for a User to
|
|
235
226
|
# be completely deleted from gitlab.com
|
|
236
|
-
|
|
227
|
+
warning(
|
|
237
228
|
f"Could not find User '{user_not_in_config}' on the Instance so can not remove User from Project '{project_and_group}'"
|
|
238
229
|
)
|
|
239
|
-
|
|
230
|
+
continue
|
|
240
231
|
|
|
232
|
+
try:
|
|
233
|
+
project.members.delete(id=user_id)
|
|
234
|
+
except GitlabDeleteError as delete_error:
|
|
235
|
+
error(
|
|
236
|
+
f"Member '{user_not_in_config}' could not be deleted: {delete_error}"
|
|
237
|
+
)
|
|
238
|
+
raise delete_error
|
|
241
239
|
else:
|
|
242
240
|
verbose("Not enforcing user members.")
|
|
243
241
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from logging import debug
|
|
2
|
-
from cli_ui import warning, fatal
|
|
2
|
+
from cli_ui import warning, fatal, error
|
|
3
3
|
|
|
4
4
|
from gitlabform.constants import EXIT_PROCESSING_ERROR
|
|
5
5
|
from gitlabform.gitlab import GitLab
|
|
@@ -35,16 +35,30 @@ class TagsProcessor(AbstractProcessor):
|
|
|
35
35
|
elif "user_id" in config:
|
|
36
36
|
user_ids.add(config["user_id"])
|
|
37
37
|
elif "user" in config:
|
|
38
|
-
|
|
39
|
-
config["user"]
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
try:
|
|
39
|
+
user_id = self.gl.get_user_id_cached(config["user"])
|
|
40
|
+
except GitlabGetError as e:
|
|
41
|
+
error(
|
|
42
|
+
f"Could not find User '{config["user"]}' on the Instance, cannot Protect "
|
|
43
|
+
f"Tag with them"
|
|
44
|
+
)
|
|
45
|
+
raise e
|
|
46
|
+
|
|
47
|
+
user_ids.add(user_id)
|
|
42
48
|
elif "group_id" in config:
|
|
43
49
|
group_ids.add(config["group_id"])
|
|
44
50
|
elif "group" in config:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
try:
|
|
52
|
+
gitlab_group = self.gl.get_group_by_path_cached(
|
|
53
|
+
config["group"]
|
|
54
|
+
)
|
|
55
|
+
except GitlabGetError as e:
|
|
56
|
+
error(
|
|
57
|
+
f"Could not find Group '{config["group"]}' on the Instance, cannot Protect "
|
|
58
|
+
f"Tag with them"
|
|
59
|
+
)
|
|
60
|
+
raise e
|
|
61
|
+
|
|
48
62
|
group_ids.add(gitlab_group.get_id())
|
|
49
63
|
|
|
50
64
|
for val in access_levels:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: gitlabform
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.6
|
|
4
4
|
Summary: 🏗 Specialized configuration as a code tool for GitLab projects, groups and more using hierarchical configuration written in YAML
|
|
5
5
|
Home-page: https://gitlabform.github.io/gitlabform
|
|
6
6
|
Author: Greg Dubicki and Contributors
|
|
@@ -26,6 +26,7 @@ Requires-Dist: MarkupSafe==3.0.2
|
|
|
26
26
|
Requires-Dist: mergedeep==1.3.4
|
|
27
27
|
Requires-Dist: packaging==24.2
|
|
28
28
|
Requires-Dist: python-gitlab==5.3.0
|
|
29
|
+
Requires-Dist: python-gitlab[graphql]==5.3.0
|
|
29
30
|
Requires-Dist: requests==2.32.3
|
|
30
31
|
Requires-Dist: ruamel.yaml==0.17.21
|
|
31
32
|
Requires-Dist: types-requests==2.32.0.20241016
|
|
@@ -44,6 +45,16 @@ Requires-Dist: xkcdpass==1.19.9; extra == "test"
|
|
|
44
45
|
Provides-Extra: docs
|
|
45
46
|
Requires-Dist: mkdocs; extra == "docs"
|
|
46
47
|
Requires-Dist: mkdocs-material; extra == "docs"
|
|
48
|
+
Dynamic: author
|
|
49
|
+
Dynamic: classifier
|
|
50
|
+
Dynamic: description
|
|
51
|
+
Dynamic: description-content-type
|
|
52
|
+
Dynamic: home-page
|
|
53
|
+
Dynamic: keywords
|
|
54
|
+
Dynamic: provides-extra
|
|
55
|
+
Dynamic: requires-dist
|
|
56
|
+
Dynamic: requires-python
|
|
57
|
+
Dynamic: summary
|
|
47
58
|
|
|
48
59
|
[](https://badge.fury.io/gh/gitlabform%2Fgitlabform)
|
|
49
60
|

|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import functools
|
|
2
|
-
from typing import Union
|
|
3
|
-
|
|
4
|
-
from cli_ui import debug
|
|
5
|
-
|
|
6
|
-
import gitlab.const
|
|
7
|
-
from gitlab import Gitlab, GitlabGetError
|
|
8
|
-
from gitlab.base import RESTObject
|
|
9
|
-
from gitlab.v4.objects import Group, Project
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# Extends the python-gitlab class to add convenience wrappers for common functionality used within gitlabform
|
|
13
|
-
class PythonGitlab(Gitlab):
|
|
14
|
-
def get_user_id(self, username) -> int:
|
|
15
|
-
user = self.get_user_by_username_cached(username)
|
|
16
|
-
return user.id
|
|
17
|
-
|
|
18
|
-
def get_group_id(self, groupname) -> int:
|
|
19
|
-
group = self.get_group_by_path_cached(groupname)
|
|
20
|
-
return group.id
|
|
21
|
-
|
|
22
|
-
def get_project_id(self, name) -> int:
|
|
23
|
-
project = self.get_project_by_path_cached(name)
|
|
24
|
-
return project.id
|
|
25
|
-
|
|
26
|
-
@functools.lru_cache()
|
|
27
|
-
def get_project_by_path_cached(self, name: str, lazy: bool = False) -> Project:
|
|
28
|
-
project: Project = self.projects.get(name, lazy)
|
|
29
|
-
if project:
|
|
30
|
-
return project
|
|
31
|
-
|
|
32
|
-
raise GitlabGetError("No project found when getting '%s'" % name, 404)
|
|
33
|
-
|
|
34
|
-
@functools.lru_cache()
|
|
35
|
-
def get_group_by_path_cached(self, groupname: str) -> Group:
|
|
36
|
-
group: Group = self.groups.get(groupname)
|
|
37
|
-
if group:
|
|
38
|
-
return group
|
|
39
|
-
|
|
40
|
-
raise GitlabGetError("No group found when getting '%s'" % groupname, 404)
|
|
41
|
-
|
|
42
|
-
# Uses "LIST" to get a user by username, to get the full User object, call get using the user's id
|
|
43
|
-
@functools.lru_cache()
|
|
44
|
-
def get_user_by_username_cached(self, username: str) -> RESTObject:
|
|
45
|
-
# Gitlab API will only ever return 0 or 1 entry when GETting using `username` attribute
|
|
46
|
-
# https://docs.gitlab.com/ee/api/users.html#for-non-administrator-users
|
|
47
|
-
# so will always be list[RESTObject] and never RESTObjectList from python-gitlab's api
|
|
48
|
-
users: list[RESTObject] = self.users.list(username=username) # type: ignore
|
|
49
|
-
|
|
50
|
-
if len(users) == 0:
|
|
51
|
-
raise GitlabGetError(
|
|
52
|
-
"No users found when searching for username '%s'" % username, 404
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
return users[0]
|
|
56
|
-
|
|
57
|
-
@functools.lru_cache()
|
|
58
|
-
def get_member_roles_cached(self, group_id: Union[int, None]):
|
|
59
|
-
"""Python-Gitlab does not natively support Member Roles yet
|
|
60
|
-
use https://python-gitlab.readthedocs.io/en/stable/api-levels.html#lower-level-api-http-methods
|
|
61
|
-
to directly invoke member_roles GET endpoint(s) in GitLab
|
|
62
|
-
https://docs.gitlab.com/ee/api/member_roles.html
|
|
63
|
-
"""
|
|
64
|
-
if self.is_gitlab_saas():
|
|
65
|
-
if group_id is None:
|
|
66
|
-
raise GitlabGetError(
|
|
67
|
-
"Group Id must be provided when getting member roles on GitLab SaaS",
|
|
68
|
-
404,
|
|
69
|
-
)
|
|
70
|
-
# SAAS
|
|
71
|
-
path = f"/groups/{group_id}/member_roles"
|
|
72
|
-
else:
|
|
73
|
-
# Self-Managed & Dedicated
|
|
74
|
-
path = f"/member_roles"
|
|
75
|
-
|
|
76
|
-
debug(f"Retrieving member roles from: {path}")
|
|
77
|
-
return self.http_get(path)
|
|
78
|
-
|
|
79
|
-
@functools.lru_cache()
|
|
80
|
-
def get_member_role_cached(
|
|
81
|
-
self, name_or_id: Union[int, str], group_id: Union[int, None]
|
|
82
|
-
):
|
|
83
|
-
member_roles = self.get_member_roles_cached(group_id)
|
|
84
|
-
for member_role in member_roles:
|
|
85
|
-
if member_role["id"] == name_or_id:
|
|
86
|
-
return member_role
|
|
87
|
-
elif (
|
|
88
|
-
type(name_or_id) == str
|
|
89
|
-
and member_role["name"].lower() == name_or_id.lower()
|
|
90
|
-
):
|
|
91
|
-
return member_role
|
|
92
|
-
|
|
93
|
-
# Failed to find member role so throw an exception explaining such to user
|
|
94
|
-
raise GitlabGetError(
|
|
95
|
-
f"Member Role with name or id {name_or_id} could not be found",
|
|
96
|
-
404,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
@functools.lru_cache()
|
|
100
|
-
def get_member_role_id_cached(
|
|
101
|
-
self, name_or_id: Union[int, str], group_id: Union[int, None]
|
|
102
|
-
) -> int:
|
|
103
|
-
if type(name_or_id) is int:
|
|
104
|
-
# Already supplied as an id so no need to go get it from API
|
|
105
|
-
return name_or_id
|
|
106
|
-
member_role = self.get_member_role_cached(name_or_id, group_id)
|
|
107
|
-
return member_role["id"]
|
|
108
|
-
|
|
109
|
-
@functools.lru_cache()
|
|
110
|
-
def is_gitlab_saas(self) -> bool:
|
|
111
|
-
return self.url == gitlab.const.DEFAULT_URL
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_ldap_links_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_saml_links_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_settings_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/group/group_variables_processor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/deploy_keys_processor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/integrations_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/job_token_scope_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/merge_requests_approval_rules.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/merge_requests_approvals.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/project_labels_processor.py
RENAMED
|
File without changes
|
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/project_push_rules_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/project_settings_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/gitlabform/processors/project/resource_groups_processor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_application_settings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_deploy_keys_all_projects.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/tests/acceptance/standard/test_group_members_groups.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_inheritance_break_subgroups.py
RENAMED
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_inheritance_break_validation.py
RENAMED
|
File without changes
|
|
File without changes
|
{gitlabform-4.0.5 → gitlabform-4.0.6}/tests/unit/configuration/test_skip_groups_skip_projects.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|