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,171 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from logging import debug, info, critical
|
|
3
|
+
|
|
4
|
+
import abc
|
|
5
|
+
from typing import Callable, Union, Any
|
|
6
|
+
from gitlabform.constants import EXIT_INVALID_INPUT
|
|
7
|
+
from gitlabform.gitlab import GitLab
|
|
8
|
+
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
9
|
+
from gitlabform.processors.defining_keys import AbstractKey
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MultipleEntitiesProcessor(AbstractProcessor, metaclass=abc.ABCMeta):
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
configuration_name: str,
|
|
16
|
+
gitlab: GitLab,
|
|
17
|
+
list_method_name: Union[str, Callable[[str], list]],
|
|
18
|
+
add_method_name: Union[str, Callable[[str, Any], None]],
|
|
19
|
+
delete_method_name: Union[str, Callable[[str, dict], None]],
|
|
20
|
+
defining: AbstractKey,
|
|
21
|
+
required_to_create_or_update: AbstractKey,
|
|
22
|
+
edit_method_name: Union[str, Callable[[str, dict, dict], None], None] = None,
|
|
23
|
+
):
|
|
24
|
+
"""
|
|
25
|
+
:param configuration_name: The key in the YAML cfg file that determines the start of this Entity's section
|
|
26
|
+
:param gitlab: Gitlab instance
|
|
27
|
+
:param list_method_name: Retrieves the entities in question
|
|
28
|
+
:param add_method_name: Creates a new entity in Gitlab
|
|
29
|
+
:param delete_method_name: Removes the entity from Gitlab
|
|
30
|
+
:param defining: An expression that tells which fields of the entity in GitLab are identifying it.
|
|
31
|
+
:param required_to_create_or_update: Entries which (according to the API documentation) are required fields
|
|
32
|
+
:param edit_method_name: Edits the existing entity in Gitlab
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
super().__init__(configuration_name, gitlab)
|
|
36
|
+
self.list_method: Callable = (
|
|
37
|
+
getattr(self.gitlab, list_method_name) if (isinstance(list_method_name, str)) else list_method_name
|
|
38
|
+
)
|
|
39
|
+
self.add_method: Callable = (
|
|
40
|
+
getattr(self.gitlab, add_method_name) if (isinstance(add_method_name, str)) else add_method_name
|
|
41
|
+
)
|
|
42
|
+
self.delete_method: Callable = (
|
|
43
|
+
getattr(self.gitlab, delete_method_name) if (isinstance(delete_method_name, str)) else delete_method_name
|
|
44
|
+
)
|
|
45
|
+
self.defining: AbstractKey = defining
|
|
46
|
+
self.required_to_create_or_update: AbstractKey = required_to_create_or_update
|
|
47
|
+
|
|
48
|
+
if edit_method_name:
|
|
49
|
+
self.edit_method: Union[Callable, None] = (
|
|
50
|
+
getattr(self.gitlab, edit_method_name) if (isinstance(edit_method_name, str)) else edit_method_name
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
self.edit_method = None
|
|
54
|
+
|
|
55
|
+
def _process_configuration(self, project_or_group: str, configuration: dict):
|
|
56
|
+
entities_in_configuration = configuration[self.configuration_name]
|
|
57
|
+
if "enforce" in entities_in_configuration:
|
|
58
|
+
enforce = entities_in_configuration["enforce"]
|
|
59
|
+
del entities_in_configuration["enforce"]
|
|
60
|
+
else:
|
|
61
|
+
enforce = False
|
|
62
|
+
|
|
63
|
+
# TODO: move/convert this to a configuration validation phase
|
|
64
|
+
self._find_duplicates(project_or_group, entities_in_configuration)
|
|
65
|
+
|
|
66
|
+
entities_in_gitlab = {}
|
|
67
|
+
i = 1
|
|
68
|
+
for entity_in_gitlab in self.list_method(project_or_group):
|
|
69
|
+
entities_in_gitlab[str(i)] = entity_in_gitlab
|
|
70
|
+
i += 1
|
|
71
|
+
|
|
72
|
+
debug(f"{self.configuration_name} BEFORE: ^^^")
|
|
73
|
+
|
|
74
|
+
# group entities into 3 groups:
|
|
75
|
+
# a) only in gitlab,
|
|
76
|
+
# b) in both configuration and gitlab (matching defining keys),
|
|
77
|
+
# c) only in configuration,
|
|
78
|
+
|
|
79
|
+
entities_only_in_gitlab = {
|
|
80
|
+
entity_name: entity_config
|
|
81
|
+
for (entity_name, entity_config) in entities_in_gitlab.items()
|
|
82
|
+
if not self._is_in(entity_config, entities_in_configuration)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
entities_in_both = {
|
|
86
|
+
entity_name: entity_config
|
|
87
|
+
for (entity_name, entity_config) in entities_in_configuration.items()
|
|
88
|
+
if self._is_in(entity_config, entities_in_gitlab)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
entities_only_in_configuration = {
|
|
92
|
+
entity_name: entity_config
|
|
93
|
+
for (entity_name, entity_config) in entities_in_configuration.items()
|
|
94
|
+
if not self._is_in(entity_config, entities_in_gitlab)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# if "enforce", then delete a)
|
|
98
|
+
|
|
99
|
+
if enforce:
|
|
100
|
+
for entity_name, entity_config in entities_only_in_gitlab.items():
|
|
101
|
+
# no need to validate if we have what's needed to delete as we got the entities from gitlab
|
|
102
|
+
info(
|
|
103
|
+
f"Deleting entity no {entity_name} of {self.configuration_name} in {project_or_group} "
|
|
104
|
+
f"as it's not in config and enforce is set to true."
|
|
105
|
+
)
|
|
106
|
+
self.delete_method(project_or_group, entity_config)
|
|
107
|
+
|
|
108
|
+
# update b), if needed (or delete them if marked as "delete")
|
|
109
|
+
|
|
110
|
+
for entity_name, entity_config in entities_in_both.items():
|
|
111
|
+
entity_in_gitlab = self._is_in(entity_config, entities_in_gitlab)
|
|
112
|
+
if entity_config.get("delete", False):
|
|
113
|
+
self._validate_required_to_delete(project_or_group, entity_name, entity_config)
|
|
114
|
+
info(f"Deleting {entity_name} of {self.configuration_name} in {project_or_group}")
|
|
115
|
+
self.delete_method(project_or_group, entity_in_gitlab)
|
|
116
|
+
elif self._needs_update(entity_in_gitlab, entity_config):
|
|
117
|
+
self._validate_required_to_create_or_update(project_or_group, entity_name, entity_config)
|
|
118
|
+
if self.edit_method:
|
|
119
|
+
info(f"Editing {entity_name} of {self.configuration_name} in {project_or_group}")
|
|
120
|
+
self.edit_method(project_or_group, entity_in_gitlab, entity_config)
|
|
121
|
+
debug(f"{self.configuration_name} AFTER: ^^^")
|
|
122
|
+
else:
|
|
123
|
+
info(f" * Recreating {entity_name} of {self.configuration_name} in {project_or_group}")
|
|
124
|
+
self.delete_method(project_or_group, entity_in_gitlab)
|
|
125
|
+
self.add_method(project_or_group, entity_config)
|
|
126
|
+
debug(f"{self.configuration_name} AFTER: ^^^")
|
|
127
|
+
else:
|
|
128
|
+
info(f" * {entity_name} of {self.configuration_name} in {project_or_group} doesn't need an update.")
|
|
129
|
+
|
|
130
|
+
# add c) (or do nothing if marked as "delete")
|
|
131
|
+
|
|
132
|
+
for entity_name, entity_config in entities_only_in_configuration.items():
|
|
133
|
+
self._validate_required_to_create_or_update(project_or_group, entity_name, entity_config)
|
|
134
|
+
info(f" * Adding {entity_name} of {self.configuration_name} in {project_or_group}")
|
|
135
|
+
self.add_method(project_or_group, entity_config)
|
|
136
|
+
debug(f"{self.configuration_name} AFTER: ^^^")
|
|
137
|
+
|
|
138
|
+
def _find_duplicates(self, project_or_group: str, entities_in_configuration: dict):
|
|
139
|
+
for first_key, first_value in entities_in_configuration.items():
|
|
140
|
+
for second_key, second_value in entities_in_configuration.items():
|
|
141
|
+
if first_key != second_key:
|
|
142
|
+
if self.defining.matches(first_value, second_value):
|
|
143
|
+
critical(
|
|
144
|
+
f"Entities {first_key} and {second_key} in {self.configuration_name} for {project_or_group}"
|
|
145
|
+
f" are the same in terms of their defining keys: {self.defining.explain()}"
|
|
146
|
+
)
|
|
147
|
+
sys.exit(EXIT_INVALID_INPUT)
|
|
148
|
+
|
|
149
|
+
def _validate_required_to_create_or_update(self, project_or_group: str, entity_name: str, entity_dict: dict):
|
|
150
|
+
if not self.required_to_create_or_update.contains(entity_dict):
|
|
151
|
+
critical(
|
|
152
|
+
f"Entity {entity_name} in {self.configuration_name} for {project_or_group}"
|
|
153
|
+
f" doesn't have some of its keys required to create or update:"
|
|
154
|
+
f" {self.required_to_create_or_update.explain()}"
|
|
155
|
+
)
|
|
156
|
+
sys.exit(EXIT_INVALID_INPUT)
|
|
157
|
+
|
|
158
|
+
def _validate_required_to_delete(self, project_or_group: str, entity_name: str, entity_dict: dict):
|
|
159
|
+
if not self.defining.contains(entity_dict):
|
|
160
|
+
critical(
|
|
161
|
+
f"Entity {entity_name} in {self.configuration_name} for {project_or_group}"
|
|
162
|
+
f" doesn't have some of its defining keys required to delete it: {self.defining.explain()}"
|
|
163
|
+
)
|
|
164
|
+
sys.exit(EXIT_INVALID_INPUT)
|
|
165
|
+
|
|
166
|
+
def _is_in(self, entity: dict, dict_of_entities: dict):
|
|
167
|
+
for entity_name, entity_config in dict_of_entities.items():
|
|
168
|
+
if self.defining.matches(entity, entity_config):
|
|
169
|
+
return entity_config
|
|
170
|
+
|
|
171
|
+
return False
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from gitlabform.configuration import Configuration
|
|
4
|
+
from gitlabform.gitlab import GitLab
|
|
5
|
+
from gitlabform.processors import AbstractProcessors
|
|
6
|
+
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
7
|
+
from gitlabform.processors.project.badges_processor import BadgesProcessor
|
|
8
|
+
from gitlabform.processors.project.branches_processor import BranchesProcessor
|
|
9
|
+
from gitlabform.processors.project.deploy_keys_processor import DeployKeysProcessor
|
|
10
|
+
from gitlabform.processors.project.files_processor import FilesProcessor
|
|
11
|
+
from gitlabform.processors.project.hooks_processor import HooksProcessor
|
|
12
|
+
from gitlabform.processors.project.integrations_processor import IntegrationsProcessor
|
|
13
|
+
from gitlabform.processors.project.job_token_scope_processor import (
|
|
14
|
+
JobTokenScopeProcessor,
|
|
15
|
+
)
|
|
16
|
+
from gitlabform.processors.project.members_processor import MembersProcessor
|
|
17
|
+
from gitlabform.processors.project.merge_requests_approval_rules import (
|
|
18
|
+
MergeRequestsApprovalRules,
|
|
19
|
+
)
|
|
20
|
+
from gitlabform.processors.project.merge_requests_approvals import (
|
|
21
|
+
MergeRequestsApprovals,
|
|
22
|
+
)
|
|
23
|
+
from gitlabform.processors.project.project_labels_processor import (
|
|
24
|
+
ProjectLabelsProcessor,
|
|
25
|
+
)
|
|
26
|
+
from gitlabform.processors.project.project_processor import ProjectProcessor
|
|
27
|
+
from gitlabform.processors.project.project_push_rules_processor import (
|
|
28
|
+
ProjectPushRulesProcessor,
|
|
29
|
+
)
|
|
30
|
+
from gitlabform.processors.project.project_settings_processor import (
|
|
31
|
+
ProjectSettingsProcessor,
|
|
32
|
+
)
|
|
33
|
+
from gitlabform.processors.project.resource_groups_processor import (
|
|
34
|
+
ResourceGroupsProcessor,
|
|
35
|
+
)
|
|
36
|
+
from gitlabform.processors.project.schedules_processor import SchedulesProcessor
|
|
37
|
+
from gitlabform.processors.project.tags_processor import TagsProcessor
|
|
38
|
+
from gitlabform.processors.project.project_variables_processor import ProjectVariablesProcessor
|
|
39
|
+
from gitlabform.processors.shared.protected_environments_processor import (
|
|
40
|
+
ProtectedEnvironmentsProcessor,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
from gitlabform.processors.project.project_security_settings import (
|
|
44
|
+
ProjectSecuritySettingsProcessor,
|
|
45
|
+
)
|
|
46
|
+
from gitlabform.processors.project.remote_mirrors_processor import RemoteMirrorsProcessor
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ProjectProcessors(AbstractProcessors):
|
|
50
|
+
def __init__(self, gitlab: GitLab, config: Configuration, strict: bool):
|
|
51
|
+
super().__init__(gitlab, config, strict)
|
|
52
|
+
self.processors: List[AbstractProcessor] = [
|
|
53
|
+
# Order of processors matter. GitLabForm will process config sections
|
|
54
|
+
# in the order listed below. Settings that are related to each other,
|
|
55
|
+
# should be ordered accordingly. For example, branch protection or MR
|
|
56
|
+
# approvals can configure specific users, but the user must be a
|
|
57
|
+
# member of the project. So, project membership must be processed
|
|
58
|
+
# before those processors.
|
|
59
|
+
ProjectProcessor(gitlab),
|
|
60
|
+
ProjectSettingsProcessor(gitlab, strict),
|
|
61
|
+
ProjectSecuritySettingsProcessor(gitlab),
|
|
62
|
+
MembersProcessor(gitlab),
|
|
63
|
+
ProjectPushRulesProcessor(gitlab),
|
|
64
|
+
ProjectLabelsProcessor(gitlab),
|
|
65
|
+
JobTokenScopeProcessor(gitlab),
|
|
66
|
+
DeployKeysProcessor(gitlab),
|
|
67
|
+
ProjectVariablesProcessor(gitlab),
|
|
68
|
+
BranchesProcessor(gitlab, strict),
|
|
69
|
+
TagsProcessor(gitlab, strict),
|
|
70
|
+
IntegrationsProcessor(gitlab),
|
|
71
|
+
FilesProcessor(gitlab, config, strict),
|
|
72
|
+
HooksProcessor(gitlab),
|
|
73
|
+
SchedulesProcessor(gitlab),
|
|
74
|
+
BadgesProcessor(gitlab),
|
|
75
|
+
ResourceGroupsProcessor(gitlab),
|
|
76
|
+
ProtectedEnvironmentsProcessor(gitlab),
|
|
77
|
+
MergeRequestsApprovals(gitlab),
|
|
78
|
+
MergeRequestsApprovalRules(gitlab),
|
|
79
|
+
RemoteMirrorsProcessor(gitlab),
|
|
80
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from gitlabform.gitlab import GitLab
|
|
2
|
+
from gitlabform.processors.defining_keys import Key, And
|
|
3
|
+
from gitlabform.processors.multiple_entities_processor import MultipleEntitiesProcessor
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BadgesProcessor(MultipleEntitiesProcessor):
|
|
7
|
+
def __init__(self, gitlab: GitLab):
|
|
8
|
+
super().__init__(
|
|
9
|
+
"badges",
|
|
10
|
+
gitlab,
|
|
11
|
+
list_method_name="get_project_badges",
|
|
12
|
+
add_method_name="add_project_badge",
|
|
13
|
+
delete_method_name="delete_project_badge",
|
|
14
|
+
defining=Key("name"),
|
|
15
|
+
required_to_create_or_update=And(Key("name"), Key("link_url"), Key("image_url")),
|
|
16
|
+
edit_method_name="edit_project_badge",
|
|
17
|
+
)
|