gitlabform 0.0.540a0__py3-none-any.whl
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/__init__.py +719 -0
- gitlabform/configuration/__init__.py +12 -0
- gitlabform/configuration/common.py +19 -0
- gitlabform/configuration/core.py +323 -0
- gitlabform/configuration/groups.py +127 -0
- gitlabform/configuration/projects.py +73 -0
- gitlabform/configuration/transform.py +259 -0
- gitlabform/constants.py +7 -0
- gitlabform/gitlab/__init__.py +108 -0
- gitlabform/gitlab/commits.py +39 -0
- gitlabform/gitlab/core.py +334 -0
- gitlabform/gitlab/group_badges.py +50 -0
- gitlabform/gitlab/group_ldap_links.py +40 -0
- gitlabform/gitlab/groups.py +96 -0
- gitlabform/gitlab/merge_requests.py +57 -0
- gitlabform/gitlab/pipelines.py +23 -0
- gitlabform/gitlab/project_badges.py +52 -0
- gitlabform/gitlab/project_deploy_keys.py +102 -0
- gitlabform/gitlab/project_merge_requests_approvals.py +94 -0
- gitlabform/gitlab/project_protected_environments.py +37 -0
- gitlabform/gitlab/projects.py +151 -0
- gitlabform/gitlab/python_gitlab.py +251 -0
- gitlabform/gitlab/variables.py +47 -0
- gitlabform/lists/__init__.py +62 -0
- gitlabform/lists/filter.py +99 -0
- gitlabform/lists/groups.py +87 -0
- gitlabform/lists/projects.py +239 -0
- gitlabform/output.py +46 -0
- gitlabform/processors/__init__.py +43 -0
- gitlabform/processors/abstract_processor.py +187 -0
- gitlabform/processors/application/__init__.py +17 -0
- gitlabform/processors/application/application_settings_processor.py +39 -0
- gitlabform/processors/defining_keys.py +152 -0
- gitlabform/processors/group/__init__.py +48 -0
- gitlabform/processors/group/group_badges_processor.py +17 -0
- gitlabform/processors/group/group_hooks_processor.py +75 -0
- gitlabform/processors/group/group_labels_processor.py +28 -0
- gitlabform/processors/group/group_ldap_links_processor.py +16 -0
- gitlabform/processors/group/group_members_processor.py +287 -0
- gitlabform/processors/group/group_push_rules_processor.py +44 -0
- gitlabform/processors/group/group_saml_links_processor.py +65 -0
- gitlabform/processors/group/group_settings_processor.py +90 -0
- gitlabform/processors/group/group_variables_processor.py +26 -0
- gitlabform/processors/multiple_entities_processor.py +171 -0
- gitlabform/processors/project/__init__.py +80 -0
- gitlabform/processors/project/badges_processor.py +17 -0
- gitlabform/processors/project/branches_processor.py +514 -0
- gitlabform/processors/project/deploy_keys_processor.py +18 -0
- gitlabform/processors/project/files_processor.py +301 -0
- gitlabform/processors/project/hooks_processor.py +64 -0
- gitlabform/processors/project/integrations_processor.py +33 -0
- gitlabform/processors/project/job_token_scope_processor.py +216 -0
- gitlabform/processors/project/members_processor.py +204 -0
- gitlabform/processors/project/merge_requests_approval_rules.py +17 -0
- gitlabform/processors/project/merge_requests_approvals.py +59 -0
- gitlabform/processors/project/project_labels_processor.py +27 -0
- gitlabform/processors/project/project_processor.py +62 -0
- gitlabform/processors/project/project_push_rules_processor.py +52 -0
- gitlabform/processors/project/project_security_settings.py +66 -0
- gitlabform/processors/project/project_settings_processor.py +239 -0
- gitlabform/processors/project/project_variables_processor.py +94 -0
- gitlabform/processors/project/remote_mirrors_processor.py +278 -0
- gitlabform/processors/project/resource_groups_processor.py +48 -0
- gitlabform/processors/project/schedules_processor.py +208 -0
- gitlabform/processors/project/tags_processor.py +108 -0
- gitlabform/processors/shared/__init__.py +0 -0
- gitlabform/processors/shared/protected_environments_processor.py +20 -0
- gitlabform/processors/util/__init__.py +0 -0
- gitlabform/processors/util/decorators.py +44 -0
- gitlabform/processors/util/difference_logger.py +70 -0
- gitlabform/processors/util/labels_processor.py +120 -0
- gitlabform/processors/util/variables_processor.py +143 -0
- gitlabform/run.py +9 -0
- gitlabform/util.py +7 -0
- gitlabform-0.0.540a0.dist-info/METADATA +54 -0
- gitlabform-0.0.540a0.dist-info/RECORD +79 -0
- gitlabform-0.0.540a0.dist-info/WHEEL +4 -0
- gitlabform-0.0.540a0.dist-info/entry_points.txt +9 -0
- gitlabform-0.0.540a0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from logging import debug, info, critical
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from ez_yaml import ez_yaml
|
|
5
|
+
from ruamel.yaml import YAML
|
|
6
|
+
from types import SimpleNamespace
|
|
7
|
+
|
|
8
|
+
from ruamel.yaml.comments import CommentedMap
|
|
9
|
+
from yamlpath import Processor
|
|
10
|
+
from yamlpath.exceptions import YAMLPathException
|
|
11
|
+
from yamlpath.wrappers import ConsolePrinter
|
|
12
|
+
|
|
13
|
+
from gitlabform.constants import EXIT_INVALID_INPUT, APPROVAL_RULE_NAME
|
|
14
|
+
from gitlabform.configuration import Configuration
|
|
15
|
+
from gitlabform.gitlab import AccessLevel
|
|
16
|
+
from gitlabform.gitlab import GitLab
|
|
17
|
+
|
|
18
|
+
# Configuration transformers are classes which take the input configuration as YAML and change it
|
|
19
|
+
# to from the more user-friendly input to an output that is more applicable to passing to GitLab
|
|
20
|
+
# over its API.
|
|
21
|
+
#
|
|
22
|
+
# For example, we want to operate on usernames in the configuration while GitLab sometimes operates
|
|
23
|
+
# on user ids. Therefore, one of the transformers changes "user_id: <number>" into "user: <username>".
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ConfigurationTransformers:
|
|
27
|
+
def __init__(self, gitlab: GitLab):
|
|
28
|
+
self.user_transformer = UserTransformer(gitlab)
|
|
29
|
+
self.group_transformer = GroupTransformer(gitlab)
|
|
30
|
+
self.implicit_name_transformer = ImplicitNameTransformer(gitlab)
|
|
31
|
+
self.access_level_transformer = AccessLevelsTransformer(gitlab)
|
|
32
|
+
|
|
33
|
+
def transform(self, configuration: Configuration) -> None:
|
|
34
|
+
config_before = ez_yaml.to_string(obj=configuration.config, options={})
|
|
35
|
+
debug(f"Config BEFORE transformations:\n{config_before}")
|
|
36
|
+
|
|
37
|
+
self.user_transformer.transform(configuration)
|
|
38
|
+
self.group_transformer.transform(configuration)
|
|
39
|
+
self.implicit_name_transformer.transform(configuration)
|
|
40
|
+
self.access_level_transformer.transform(configuration, last=True)
|
|
41
|
+
|
|
42
|
+
config_after = ez_yaml.to_string(obj=configuration.config, options={})
|
|
43
|
+
debug(f"Config AFTER transformations:\n{config_after}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ConfigurationTransformer(ABC):
|
|
47
|
+
def transform(self, configuration: Configuration, last: bool = False) -> None:
|
|
48
|
+
self._do_transform(configuration)
|
|
49
|
+
if last:
|
|
50
|
+
self.convert_to_simple_types(configuration)
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def _do_transform(self, configuration: Configuration) -> None:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def convert_to_simple_types(configuration: Configuration):
|
|
58
|
+
# we needed complex ruamel.yaml's types like ordereddict and CommentedSeq
|
|
59
|
+
# for transformations, but at the end convert them to simple dict and lists
|
|
60
|
+
# for easier to understand debug output and tests
|
|
61
|
+
|
|
62
|
+
config_yaml_string = ez_yaml.to_string(obj=configuration.config, options={})
|
|
63
|
+
simple_yaml_loader = YAML(typ="safe", pure=True)
|
|
64
|
+
configuration.config = simple_yaml_loader.load(config_yaml_string)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class UserTransformer(ConfigurationTransformer):
|
|
68
|
+
def __init__(self, gitlab: GitLab):
|
|
69
|
+
self.gitlab = gitlab
|
|
70
|
+
|
|
71
|
+
def _do_transform(self, configuration: Configuration) -> None:
|
|
72
|
+
logging_args = SimpleNamespace(quiet=False, verbose=False, debug=False)
|
|
73
|
+
log = ConsolePrinter(logging_args)
|
|
74
|
+
processor = Processor(log, configuration.config)
|
|
75
|
+
info("Getting user ids for users defined in protect_environments config")
|
|
76
|
+
try:
|
|
77
|
+
for node_coordinate in processor.get_nodes(
|
|
78
|
+
"projects_and_groups.*.protected_environments.*.deploy_access_levels.user",
|
|
79
|
+
mustexist=True,
|
|
80
|
+
):
|
|
81
|
+
user = node_coordinate.parent.pop("user")
|
|
82
|
+
|
|
83
|
+
node_coordinate.parent["user_id"] = self.gitlab._get_user_id(user)
|
|
84
|
+
except YAMLPathException as e:
|
|
85
|
+
# this just means that we haven't found any keys in YAML
|
|
86
|
+
# under the given path
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
info("Getting user ids for users defined in merge_requests_approval_rules config")
|
|
90
|
+
try:
|
|
91
|
+
for node_coordinate in processor.get_nodes(
|
|
92
|
+
"**.merge_requests_approval_rules.*.users",
|
|
93
|
+
mustexist=True,
|
|
94
|
+
):
|
|
95
|
+
user_ids = []
|
|
96
|
+
users = node_coordinate.parent.pop("users")
|
|
97
|
+
for user in users:
|
|
98
|
+
user_id = self.gitlab._get_user_id(user)
|
|
99
|
+
user_ids.append(user_id)
|
|
100
|
+
node_coordinate.parent["user_ids"] = user_ids
|
|
101
|
+
except YAMLPathException as e:
|
|
102
|
+
# this just means that we haven't found any keys in YAML
|
|
103
|
+
# under the given path
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class GroupTransformer(ConfigurationTransformer):
|
|
108
|
+
def __init__(self, gitlab: GitLab):
|
|
109
|
+
self.gitlab = gitlab
|
|
110
|
+
|
|
111
|
+
def _do_transform(self, configuration: Configuration) -> None:
|
|
112
|
+
logging_args = SimpleNamespace(quiet=False, verbose=False, debug=False)
|
|
113
|
+
processor = Processor(ConsolePrinter(logging_args), configuration.config)
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
for node_coordinate in processor.get_nodes(
|
|
117
|
+
"projects_and_groups.*.protected_environments.*.deploy_access_levels.group",
|
|
118
|
+
mustexist=True,
|
|
119
|
+
):
|
|
120
|
+
group = node_coordinate.parent.pop("group")
|
|
121
|
+
node_coordinate.parent["group_id"] = self.gitlab._get_group_id(group)
|
|
122
|
+
|
|
123
|
+
except YAMLPathException as e:
|
|
124
|
+
# this just means that we haven't found any keys in YAML
|
|
125
|
+
# under the given path
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
for node_coordinate in processor.get_nodes(
|
|
130
|
+
"**.merge_requests_approval_rules.*.groups",
|
|
131
|
+
mustexist=True,
|
|
132
|
+
):
|
|
133
|
+
group_ids = []
|
|
134
|
+
groups = node_coordinate.parent.pop("groups")
|
|
135
|
+
for group in groups:
|
|
136
|
+
group_id = self.gitlab._get_group_id(group)
|
|
137
|
+
group_ids.append(group_id)
|
|
138
|
+
node_coordinate.parent["group_ids"] = group_ids
|
|
139
|
+
except YAMLPathException:
|
|
140
|
+
# this just means that we haven't found any keys in YAML
|
|
141
|
+
# under the given path
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class ImplicitNameTransformer(ConfigurationTransformer):
|
|
146
|
+
"""
|
|
147
|
+
Creates a 'name' field that has the same value as the "scope" delimiter, e.g.:
|
|
148
|
+
|
|
149
|
+
...
|
|
150
|
+
blah: # start of the cfg scope
|
|
151
|
+
name: blah # name to be used
|
|
152
|
+
smth_else: <...>
|
|
153
|
+
|
|
154
|
+
It's redundant, so this can be done as :
|
|
155
|
+
|
|
156
|
+
...
|
|
157
|
+
foo: # a 'name' field will be created as -> name: foo
|
|
158
|
+
smth_else: <...>
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
def __init__(self, gitlab: GitLab):
|
|
162
|
+
# this transformer doesn't need to call gitlab
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
def _do_transform(self, configuration: Configuration) -> None:
|
|
166
|
+
logging_args = SimpleNamespace(quiet=False, verbose=False, debug=False)
|
|
167
|
+
processor = Processor(ConsolePrinter(logging_args), configuration.config)
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
for node_coordinate in processor.get_nodes(
|
|
171
|
+
"projects_and_groups.*.protected_environments.*",
|
|
172
|
+
mustexist=True,
|
|
173
|
+
):
|
|
174
|
+
if type(node_coordinate.node) not in [CommentedMap, dict]:
|
|
175
|
+
continue
|
|
176
|
+
|
|
177
|
+
node_coordinate.parent[node_coordinate.parentref]["name"] = node_coordinate.parentref
|
|
178
|
+
except YAMLPathException:
|
|
179
|
+
# this just means that we haven't found any keys in YAML
|
|
180
|
+
# under the given path
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class AccessLevelsTransformer(ConfigurationTransformer):
|
|
185
|
+
"""
|
|
186
|
+
Internally the app supports only numeric access levels, but for user-friendliness
|
|
187
|
+
this class allows providing them as strings and transforms these strings into
|
|
188
|
+
the appropriate numbers.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
def __init__(self, gitlab: GitLab):
|
|
192
|
+
# this transformer doesn't need to call gitlab
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
def _do_transform(self, configuration: Configuration):
|
|
196
|
+
logging_args = SimpleNamespace(quiet=False, verbose=False, debug=False)
|
|
197
|
+
processor = Processor(ConsolePrinter(logging_args), configuration.config)
|
|
198
|
+
|
|
199
|
+
# [.!<100] effectively means that the value is non-numerical
|
|
200
|
+
paths_to_hashes = [
|
|
201
|
+
# # branches, old syntax
|
|
202
|
+
"**.push_access_level[.!<100]",
|
|
203
|
+
"**.merge_access_level[.!<100]",
|
|
204
|
+
"**.unprotect_access_level[.!<100]",
|
|
205
|
+
# members & group members
|
|
206
|
+
"**.access_level[.!<100]",
|
|
207
|
+
"**.group_access[.!<100]",
|
|
208
|
+
# old syntax
|
|
209
|
+
"**.group_access_level[.!<100]",
|
|
210
|
+
# tags
|
|
211
|
+
"**.create_access_level[.!<100]",
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
for path in paths_to_hashes:
|
|
215
|
+
try:
|
|
216
|
+
for node_coordinate in processor.get_nodes(path, mustexist=True):
|
|
217
|
+
try:
|
|
218
|
+
access_level_string = str(node_coordinate.node)
|
|
219
|
+
node_coordinate.parent[node_coordinate.parentref] = AccessLevel.get_value(access_level_string)
|
|
220
|
+
except KeyError:
|
|
221
|
+
critical(
|
|
222
|
+
f"Configuration string '{access_level_string}' is not one of the valid access levels:"
|
|
223
|
+
f" {', '.join(AccessLevel.get_canonical_names())}"
|
|
224
|
+
)
|
|
225
|
+
sys.exit(EXIT_INVALID_INPUT)
|
|
226
|
+
except YAMLPathException:
|
|
227
|
+
# this just means that we haven't found any keys in YAML
|
|
228
|
+
# under the given path
|
|
229
|
+
pass
|
|
230
|
+
|
|
231
|
+
# these are different from the above, as they are elements of arrays,
|
|
232
|
+
# so we need different search query and an extra condition for
|
|
233
|
+
# transformation
|
|
234
|
+
paths_to_arrays = [
|
|
235
|
+
# # branches, new GitLab Premium syntax
|
|
236
|
+
"**.allowed_to_push.*.[access_level!<100]",
|
|
237
|
+
"**.allowed_to_merge.*.[access_level!<100]",
|
|
238
|
+
"**.allowed_to_unprotect.*.[access_level!<100]",
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
for path in paths_to_arrays:
|
|
242
|
+
try:
|
|
243
|
+
for node_coordinate in processor.get_nodes(path, mustexist=True):
|
|
244
|
+
if node_coordinate.parentref == "access_level":
|
|
245
|
+
try:
|
|
246
|
+
access_level_string = str(node_coordinate.node)
|
|
247
|
+
node_coordinate.parent[node_coordinate.parentref] = AccessLevel.get_value(
|
|
248
|
+
access_level_string
|
|
249
|
+
)
|
|
250
|
+
except KeyError:
|
|
251
|
+
critical(
|
|
252
|
+
f"Configuration string '{access_level_string}' is not one of the valid access levels:"
|
|
253
|
+
f" {', '.join(AccessLevel.get_canonical_names())}"
|
|
254
|
+
)
|
|
255
|
+
sys.exit(EXIT_INVALID_INPUT)
|
|
256
|
+
except YAMLPathException:
|
|
257
|
+
# this just means that we haven't found any keys in YAML
|
|
258
|
+
# under the given path
|
|
259
|
+
pass
|
gitlabform/constants.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# f.e. bad syntax in the config file ~= "it's your fault" 😅
|
|
2
|
+
EXIT_INVALID_INPUT = 1
|
|
3
|
+
# f.e. when requests to GitLab fail ~= "it's not your fault" 😎
|
|
4
|
+
EXIT_PROCESSING_ERROR = 2
|
|
5
|
+
|
|
6
|
+
# legacy single approval rule name
|
|
7
|
+
APPROVAL_RULE_NAME = "Approvers (configured using GitLabForm)"
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import inspect
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
from gitlab import Gitlab as GitlabClient, GraphQL
|
|
8
|
+
|
|
9
|
+
from gitlabform.gitlab.commits import GitLabCommits
|
|
10
|
+
from gitlabform.gitlab.group_badges import GitLabGroupBadges
|
|
11
|
+
from gitlabform.gitlab.group_ldap_links import GitLabGroupLDAPLinks
|
|
12
|
+
from gitlabform.gitlab.merge_requests import GitLabMergeRequests
|
|
13
|
+
from gitlabform.gitlab.pipelines import GitLabPipelines
|
|
14
|
+
from gitlabform.gitlab.project_badges import GitLabProjectBadges
|
|
15
|
+
from gitlabform.gitlab.project_deploy_keys import GitLabProjectDeployKeys
|
|
16
|
+
from gitlabform.gitlab.project_protected_environments import (
|
|
17
|
+
GitLabProjectProtectedEnvironments,
|
|
18
|
+
)
|
|
19
|
+
from gitlabform.gitlab.project_merge_requests_approvals import (
|
|
20
|
+
GitLabProjectMergeRequestsApprovals,
|
|
21
|
+
)
|
|
22
|
+
from gitlabform.gitlab.python_gitlab import PythonGitlab
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@enum.unique
|
|
26
|
+
class AccessLevel(enum.IntEnum):
|
|
27
|
+
NO_ACCESS = 0
|
|
28
|
+
MINIMAL = 5 # introduced in GitLab 13.5
|
|
29
|
+
GUEST = 10
|
|
30
|
+
PLANNER = 15 # introduced in GitLab 17.7
|
|
31
|
+
REPORTER = 20
|
|
32
|
+
DEVELOPER = 30
|
|
33
|
+
MAINTAINER = 40
|
|
34
|
+
OWNER = 50 # only for groups
|
|
35
|
+
ADMIN = 60
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def group_levels(cls) -> List[int]:
|
|
39
|
+
return [level.value for level in AccessLevel if level <= 50]
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def get_value(cls, name: str) -> int:
|
|
43
|
+
# for the above set of key names this is enough for an effectively fuzzy name matching
|
|
44
|
+
return AccessLevel[name.strip().upper().replace(" ", "_")].value
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def get_canonical_names(cls) -> List[str]:
|
|
48
|
+
return [level.name.lower().replace("_", " ") for level in AccessLevel]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class GitLab(
|
|
52
|
+
GitLabCommits,
|
|
53
|
+
GitLabMergeRequests,
|
|
54
|
+
GitLabGroupLDAPLinks,
|
|
55
|
+
GitLabGroupBadges,
|
|
56
|
+
GitLabPipelines,
|
|
57
|
+
GitLabProjectBadges,
|
|
58
|
+
GitLabProjectDeployKeys,
|
|
59
|
+
GitLabProjectProtectedEnvironments,
|
|
60
|
+
GitLabProjectMergeRequestsApprovals,
|
|
61
|
+
):
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class GitlabWrapper:
|
|
66
|
+
# Parameters accepted by python-gitlab's Gitlab.__init__
|
|
67
|
+
# Other config keys (like max_retries) are used elsewhere in gitlabform
|
|
68
|
+
# or passed to specific components like GraphQL
|
|
69
|
+
GITLAB_CLIENT_PARAMS = set(inspect.signature(GitlabClient.__init__).parameters.keys()) - {"self"}
|
|
70
|
+
|
|
71
|
+
# Parameters accepted by python-gitlab's GraphQL.__init__
|
|
72
|
+
GRAPHQL_PARAMS = set(inspect.signature(GraphQL.__init__).parameters.keys()) - {"self"}
|
|
73
|
+
|
|
74
|
+
def __init__(self, gitlabform: GitLab):
|
|
75
|
+
session = gitlabform.session
|
|
76
|
+
|
|
77
|
+
graphql_kwargs = {k: v for k, v in gitlabform.gitlab_config.items() if k in self.GRAPHQL_PARAMS}
|
|
78
|
+
graphql = GraphQL(**graphql_kwargs)
|
|
79
|
+
|
|
80
|
+
default_gitlab_kwargs = {
|
|
81
|
+
"retry_transient_errors": True,
|
|
82
|
+
}
|
|
83
|
+
renamed_gitlab_kwargs = {
|
|
84
|
+
# Bandit is used for security scanning and it complains about 'private_token' being
|
|
85
|
+
# a hardcoded secret. However, in this case we are just renaming a config key
|
|
86
|
+
# provided by the user to match the parameter name expected by python-gitlab.
|
|
87
|
+
# Hence, we can safely ignore this code security warning here.
|
|
88
|
+
"token": "private_token", # nosec B105
|
|
89
|
+
}
|
|
90
|
+
extra_gitlab_kwargs = {
|
|
91
|
+
**default_gitlab_kwargs,
|
|
92
|
+
**{
|
|
93
|
+
k: v
|
|
94
|
+
for k, v in gitlabform.gitlab_config.items()
|
|
95
|
+
if k not in renamed_gitlab_kwargs and k in self.GITLAB_CLIENT_PARAMS
|
|
96
|
+
},
|
|
97
|
+
**{renamed_gitlab_kwargs[k]: v for k, v in gitlabform.gitlab_config.items() if k in renamed_gitlab_kwargs},
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
self._gitlab: PythonGitlab = PythonGitlab(
|
|
101
|
+
api_version="4",
|
|
102
|
+
graphql=graphql,
|
|
103
|
+
session=session,
|
|
104
|
+
**extra_gitlab_kwargs,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def get_gitlab(self):
|
|
108
|
+
return self._gitlab
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from gitlabform.gitlab.core import GitLabCore
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GitLabCommits(GitLabCore):
|
|
5
|
+
def get_commit(self, project_and_group_name, sha):
|
|
6
|
+
return self._make_requests_to_api("projects/%s/repository/commits/%s", (project_and_group_name, sha))
|
|
7
|
+
|
|
8
|
+
def get_ahead_and_behind(self, project_and_group_name, protected_branch, feature_branch):
|
|
9
|
+
ahead = 0
|
|
10
|
+
behind = 0
|
|
11
|
+
|
|
12
|
+
response = self._make_requests_to_api(
|
|
13
|
+
"projects/%s/repository/compare?from=%s&to=%s",
|
|
14
|
+
(project_and_group_name, protected_branch, feature_branch),
|
|
15
|
+
)
|
|
16
|
+
if len(response) > 0:
|
|
17
|
+
ahead = len(response["commits"])
|
|
18
|
+
|
|
19
|
+
response = self._make_requests_to_api(
|
|
20
|
+
"projects/%s/repository/compare?from=%s&to=%s",
|
|
21
|
+
(project_and_group_name, feature_branch, protected_branch),
|
|
22
|
+
)
|
|
23
|
+
if len(response) > 0:
|
|
24
|
+
behind = len(response["commits"])
|
|
25
|
+
|
|
26
|
+
return ahead, behind
|
|
27
|
+
|
|
28
|
+
def get_last_commit(self, project_and_group_name, branch_name):
|
|
29
|
+
branch = self._make_requests_to_api("projects/%s/repository/branches/%s", (project_and_group_name, branch_name))
|
|
30
|
+
last_commit_hash = branch["commit"]["id"]
|
|
31
|
+
return self.get_commit(project_and_group_name, last_commit_hash)
|
|
32
|
+
|
|
33
|
+
def get_last_commit_attributes(self, project_and_group_name, branch):
|
|
34
|
+
commit = self.get_last_commit(project_and_group_name, branch)
|
|
35
|
+
|
|
36
|
+
# we want to read git's *commit date* instead of *author date*, so that "touching" the branch will invalidate
|
|
37
|
+
# its "getting older" counter
|
|
38
|
+
|
|
39
|
+
return commit["author_name"], commit["author_email"], commit["committed_date"]
|