gitlabform 4.2.4__tar.gz → 4.2.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.2.4 → gitlabform-4.2.6}/PKG-INFO +15 -15
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/python_gitlab.py +1 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_hooks_processor.py +2 -1
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_members_processor.py +12 -5
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/hooks_processor.py +2 -1
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/integrations_processor.py +10 -1
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/members_processor.py +15 -30
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/schedules_processor.py +1 -1
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/util/labels_processor.py +34 -13
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform.egg-info/PKG-INFO +15 -15
- gitlabform-4.2.6/gitlabform.egg-info/requires.txt +31 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/pyproject.toml +15 -15
- gitlabform-4.2.4/gitlabform.egg-info/requires.txt +0 -31
- {gitlabform-4.2.4 → gitlabform-4.2.6}/LICENSE +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/README.md +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/configuration/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/configuration/common.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/configuration/core.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/configuration/groups.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/configuration/projects.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/configuration/transform.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/constants.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/commits.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/core.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/group_badges.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/group_ldap_links.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/group_variables.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/groups.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/merge_requests.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/pipelines.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/project_badges.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/project_deploy_keys.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/project_merge_requests_approvals.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/project_protected_environments.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/project_security_settings.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/projects.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/gitlab/variables.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/lists/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/lists/filter.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/lists/groups.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/lists/projects.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/output.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/abstract_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/application/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/application/application_settings_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/defining_keys.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_badges_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_labels_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_ldap_links_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_push_rules_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_saml_links_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_settings_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_variables_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/multiple_entities_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/badges_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/branches_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/deploy_keys_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/files_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/job_token_scope_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/merge_requests_approval_rules.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/merge_requests_approvals.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/project_labels_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/project_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/project_push_rules_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/project_security_settings.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/project_settings_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/resource_groups_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/tags_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/variables_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/shared/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/shared/protected_environments_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/single_entity_processor.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/util/__init__.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/util/decorators.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/util/difference_logger.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/run.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/util.py +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform.egg-info/SOURCES.txt +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform.egg-info/dependency_links.txt +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform.egg-info/entry_points.txt +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform.egg-info/top_level.txt +0 -0
- {gitlabform-4.2.4 → gitlabform-4.2.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlabform
|
|
3
|
-
Version: 4.2.
|
|
3
|
+
Version: 4.2.6
|
|
4
4
|
Summary: 🏗 Specialized configuration as a code tool for GitLab projects, groups and more using hierarchical configuration written in YAML
|
|
5
5
|
Author: Greg Dubicki and Contributors
|
|
6
6
|
Project-URL: Homepage, https://gitlabform.github.io/gitlabform/
|
|
@@ -20,7 +20,7 @@ Classifier: Topic :: Software Development :: Version Control :: Git
|
|
|
20
20
|
Requires-Python: >=3.12.0
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: certifi==2025.
|
|
23
|
+
Requires-Dist: certifi==2025.6.15
|
|
24
24
|
Requires-Dist: cli-ui==0.19.0
|
|
25
25
|
Requires-Dist: ez-yaml==1.2.0
|
|
26
26
|
Requires-Dist: Jinja2==3.1.6
|
|
@@ -28,23 +28,23 @@ Requires-Dist: luddite==1.0.4
|
|
|
28
28
|
Requires-Dist: MarkupSafe==3.0.2
|
|
29
29
|
Requires-Dist: mergedeep==1.3.4
|
|
30
30
|
Requires-Dist: packaging==25.0
|
|
31
|
-
Requires-Dist: python-gitlab==
|
|
32
|
-
Requires-Dist: python-gitlab[graphql]==
|
|
33
|
-
Requires-Dist: requests==2.32.
|
|
31
|
+
Requires-Dist: python-gitlab==6.1.0
|
|
32
|
+
Requires-Dist: python-gitlab[graphql]==6.1.0
|
|
33
|
+
Requires-Dist: requests==2.32.4
|
|
34
34
|
Requires-Dist: ruamel.yaml==0.17.21
|
|
35
|
-
Requires-Dist: types-requests==2.32.
|
|
36
|
-
Requires-Dist: types-setuptools==
|
|
35
|
+
Requires-Dist: types-requests==2.32.4.20250611
|
|
36
|
+
Requires-Dist: types-setuptools==80.9.0.20250529
|
|
37
37
|
Requires-Dist: yamlpath==3.8.2
|
|
38
38
|
Provides-Extra: test
|
|
39
|
-
Requires-Dist: coverage==7.
|
|
40
|
-
Requires-Dist: cryptography==
|
|
41
|
-
Requires-Dist: deepdiff==8.
|
|
42
|
-
Requires-Dist: mypy==1.
|
|
43
|
-
Requires-Dist: mypy-extensions==1.
|
|
39
|
+
Requires-Dist: coverage==7.9.2; extra == "test"
|
|
40
|
+
Requires-Dist: cryptography==45.0.5; extra == "test"
|
|
41
|
+
Requires-Dist: deepdiff==8.5.0; extra == "test"
|
|
42
|
+
Requires-Dist: mypy==1.16.1; extra == "test"
|
|
43
|
+
Requires-Dist: mypy-extensions==1.1.0; extra == "test"
|
|
44
44
|
Requires-Dist: pre-commit==2.21.0; extra == "test"
|
|
45
|
-
Requires-Dist: pytest==8.
|
|
46
|
-
Requires-Dist: pytest-cov==6.
|
|
47
|
-
Requires-Dist: pytest-rerunfailures==15.
|
|
45
|
+
Requires-Dist: pytest==8.4.1; extra == "test"
|
|
46
|
+
Requires-Dist: pytest-cov==6.2.1; extra == "test"
|
|
47
|
+
Requires-Dist: pytest-rerunfailures==15.1; extra == "test"
|
|
48
48
|
Requires-Dist: xkcdpass==1.20.0; extra == "test"
|
|
49
49
|
Provides-Extra: docs
|
|
50
50
|
Requires-Dist: mkdocs; extra == "docs"
|
|
@@ -96,6 +96,7 @@ class PythonGitlab(Gitlab):
|
|
|
96
96
|
# Gitlab API will only ever return 0 or 1 entry when GETting using `username` attribute
|
|
97
97
|
# https://docs.gitlab.com/ee/api/users.html#for-non-administrator-users
|
|
98
98
|
# so will always be list[RESTObject] and never RESTObjectList from python-gitlab's api
|
|
99
|
+
# username list is case-insensitive on the gitlab api
|
|
99
100
|
users: list[RESTObject] = self.users.list(username=username) # type: ignore
|
|
100
101
|
|
|
101
102
|
if len(users) == 0:
|
|
@@ -3,6 +3,7 @@ from typing import Dict, Any, List
|
|
|
3
3
|
|
|
4
4
|
from gitlab.base import RESTObject, RESTObjectList
|
|
5
5
|
from gitlab.v4.objects import Group
|
|
6
|
+
from gitlab.v4.objects import GroupHook
|
|
6
7
|
|
|
7
8
|
from gitlabform.gitlab import GitLab
|
|
8
9
|
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
@@ -15,7 +16,7 @@ class GroupHooksProcessor(AbstractProcessor):
|
|
|
15
16
|
def _process_configuration(self, group_path_and_name: str, configuration: dict):
|
|
16
17
|
debug("Processing group hooks...")
|
|
17
18
|
group: Group = self.gl.get_group_by_path_cached(group_path_and_name)
|
|
18
|
-
group_hooks:
|
|
19
|
+
group_hooks: list[GroupHook] = group.hooks.list(get_all=True)
|
|
19
20
|
|
|
20
21
|
hooks_in_config: tuple[str, ...] = tuple(x for x in sorted(configuration["group_hooks"]) if x != "enforce")
|
|
21
22
|
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_members_processor.py
RENAMED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
from logging import debug,
|
|
1
|
+
from logging import debug, error
|
|
2
2
|
from typing import Dict, Tuple
|
|
3
3
|
|
|
4
|
-
import gitlab
|
|
5
4
|
from cli_ui import fatal, error, debug as verbose
|
|
6
5
|
|
|
7
6
|
from gitlabform.constants import EXIT_INVALID_INPUT
|
|
8
7
|
from gitlabform.gitlab import GitLab, AccessLevel
|
|
9
8
|
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
10
9
|
from gitlab.v4.objects import Group, GroupMember, User
|
|
11
|
-
from gitlab import GitlabDeleteError, GitlabGetError
|
|
10
|
+
from gitlab import GitlabDeleteError, GitlabError, GitlabGetError
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class GroupMembersProcessor(AbstractProcessor):
|
|
@@ -102,7 +101,11 @@ class GroupMembersProcessor(AbstractProcessor):
|
|
|
102
101
|
# to ensure that the group has the expected access level
|
|
103
102
|
self._unshare(group_being_processed, share_with_group_id)
|
|
104
103
|
|
|
105
|
-
|
|
104
|
+
try:
|
|
105
|
+
group_being_processed.share(share_with_group_id, group_access_to_set, expires_at_to_set)
|
|
106
|
+
except GitlabError as e:
|
|
107
|
+
error(f"Error processing {share_with_group_path}, {e.error_message}")
|
|
108
|
+
raise e
|
|
106
109
|
|
|
107
110
|
else:
|
|
108
111
|
debug(
|
|
@@ -111,7 +114,11 @@ class GroupMembersProcessor(AbstractProcessor):
|
|
|
111
114
|
)
|
|
112
115
|
|
|
113
116
|
share_with_group_id = self.gl.get_group_id(share_with_group_path)
|
|
114
|
-
|
|
117
|
+
try:
|
|
118
|
+
group_being_processed.share(share_with_group_id, group_access_to_set, expires_at_to_set)
|
|
119
|
+
except GitlabError as e:
|
|
120
|
+
error(f"Error processing {share_with_group_path}, {e.error_message}")
|
|
121
|
+
raise e
|
|
115
122
|
|
|
116
123
|
if enforce_group_members:
|
|
117
124
|
# remove groups not configured explicitly
|
|
@@ -3,6 +3,7 @@ from typing import Dict, Any, List
|
|
|
3
3
|
|
|
4
4
|
from gitlab.base import RESTObject, RESTObjectList
|
|
5
5
|
from gitlab.v4.objects import Project
|
|
6
|
+
from gitlab.v4.objects import ProjectHook
|
|
6
7
|
|
|
7
8
|
from gitlabform.gitlab import GitLab
|
|
8
9
|
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
@@ -15,7 +16,7 @@ class HooksProcessor(AbstractProcessor):
|
|
|
15
16
|
def _process_configuration(self, project_and_group: str, configuration: dict):
|
|
16
17
|
debug("Processing hooks...")
|
|
17
18
|
project: Project = self.gl.get_project_by_path_cached(project_and_group)
|
|
18
|
-
project_hooks:
|
|
19
|
+
project_hooks: list[ProjectHook] = project.hooks.list(get_all=True)
|
|
19
20
|
|
|
20
21
|
hooks_in_config: tuple[str, ...] = tuple(x for x in sorted(configuration["hooks"]) if x != "enforce")
|
|
21
22
|
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/integrations_processor.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from cli_ui import debug as verbose
|
|
2
2
|
|
|
3
|
+
from gitlab.exceptions import GitlabDeleteError
|
|
3
4
|
from gitlab.v4.objects import Project, ProjectIntegration
|
|
4
5
|
from gitlabform.gitlab import GitLab
|
|
5
6
|
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
@@ -18,7 +19,15 @@ class IntegrationsProcessor(AbstractProcessor):
|
|
|
18
19
|
|
|
19
20
|
if configured_integrations[integration].get("delete"):
|
|
20
21
|
verbose(f"Deleting integration: {integration}")
|
|
21
|
-
|
|
22
|
+
try:
|
|
23
|
+
gl_integration.delete()
|
|
24
|
+
except GitlabDeleteError as e:
|
|
25
|
+
# If we get a 404 the integration does not exist, so we can ignore the error
|
|
26
|
+
if e.response_code == 404:
|
|
27
|
+
verbose(f"Integration {integration} does not exist, skipping deletion.")
|
|
28
|
+
else:
|
|
29
|
+
verbose(f"Failed to delete integration {integration}: {e}")
|
|
30
|
+
raise
|
|
22
31
|
else:
|
|
23
32
|
verbose(f"Setting integration: {integration}")
|
|
24
33
|
project.integrations.update(integration, configured_integrations[integration])
|
|
@@ -49,15 +49,10 @@ class MembersProcessor(AbstractProcessor):
|
|
|
49
49
|
and expires_at == current_groups[common_group_name]["expires_at"]
|
|
50
50
|
and access_level == current_groups[common_group_name]["group_access_level"]
|
|
51
51
|
):
|
|
52
|
-
verbose(
|
|
53
|
-
|
|
54
|
-
common_group_name,
|
|
55
|
-
)
|
|
56
|
-
verbose(
|
|
57
|
-
"Current settings for '%s' are: %s" % (common_group_name, current_groups[common_group_name])
|
|
58
|
-
)
|
|
52
|
+
verbose(f"Ignoring group '{common_group_name}' as it is already a member")
|
|
53
|
+
verbose(f"Current settings for '{common_group_name}' are: {current_groups[common_group_name]}")
|
|
59
54
|
else:
|
|
60
|
-
verbose("Setting group '
|
|
55
|
+
verbose(f"Setting group '{common_group_name}' as a member")
|
|
61
56
|
access = access_level
|
|
62
57
|
expiry = expires_at
|
|
63
58
|
|
|
@@ -86,7 +81,6 @@ class MembersProcessor(AbstractProcessor):
|
|
|
86
81
|
enforce_members: bool,
|
|
87
82
|
keep_bots: bool,
|
|
88
83
|
):
|
|
89
|
-
|
|
90
84
|
project: Project = self.gl.get_project_by_path_cached(project_and_group)
|
|
91
85
|
|
|
92
86
|
current_members = self._get_members_from_project(project)
|
|
@@ -97,10 +91,11 @@ class MembersProcessor(AbstractProcessor):
|
|
|
97
91
|
for user in users:
|
|
98
92
|
info(f"Processing user '{user}'...")
|
|
99
93
|
|
|
100
|
-
|
|
101
|
-
if
|
|
94
|
+
gitlab_user = self.gl.get_user_by_username_cached(user)
|
|
95
|
+
if gitlab_user is None:
|
|
102
96
|
warning(f"Could not find user '{user}' in Gitlab, skipping...")
|
|
103
97
|
continue
|
|
98
|
+
user_id = gitlab_user.id
|
|
104
99
|
|
|
105
100
|
expires_at = users[user]["expires_at"].strftime("%Y-%m-%d") if "expires_at" in users[user] else None
|
|
106
101
|
access_level = users[user]["access_level"] if "access_level" in users[user] else None
|
|
@@ -128,33 +123,23 @@ class MembersProcessor(AbstractProcessor):
|
|
|
128
123
|
and access_level == current_member.access_level
|
|
129
124
|
and member_role_id == member_role_id_before
|
|
130
125
|
):
|
|
131
|
-
verbose(
|
|
132
|
-
|
|
133
|
-
common_username,
|
|
134
|
-
)
|
|
135
|
-
verbose(
|
|
136
|
-
"Current settings for '%s' are: %s" % (common_username, current_members[common_username])
|
|
137
|
-
)
|
|
126
|
+
verbose(f"Nothing to change for user '{common_username}' - same config now as to set.")
|
|
127
|
+
verbose(f"Current settings for '{common_username}' are: {current_members[common_username]}")
|
|
138
128
|
else:
|
|
139
129
|
verbose(
|
|
140
|
-
"Editing user '
|
|
141
|
-
common_username,
|
|
130
|
+
f"Editing user '{common_username}' membership to change their access level or expires at",
|
|
142
131
|
)
|
|
143
|
-
update_data = {
|
|
144
|
-
"user_id": common_username,
|
|
145
|
-
"access_level": access_level,
|
|
146
|
-
"member_role_id": member_role_id_before,
|
|
147
|
-
}
|
|
148
132
|
|
|
133
|
+
project_member = project.members.get(id=user_id)
|
|
134
|
+
project_member.access_level = access_level
|
|
135
|
+
project_member.member_role_id = member_role_id_before
|
|
149
136
|
if expires_at:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
project.members.update(new_data=update_data)
|
|
137
|
+
project_member.expires_at = expires_at
|
|
138
|
+
project_member.save()
|
|
153
139
|
|
|
154
140
|
else:
|
|
155
141
|
verbose(
|
|
156
|
-
"Adding user '
|
|
157
|
-
common_username,
|
|
142
|
+
f"Adding user '{common_username}' who previously was not a member.",
|
|
158
143
|
)
|
|
159
144
|
create_data = {
|
|
160
145
|
"user_id": user_id,
|
|
@@ -24,7 +24,7 @@ class SchedulesProcessor(AbstractProcessor):
|
|
|
24
24
|
configured_schedules.pop("enforce")
|
|
25
25
|
|
|
26
26
|
project: Project = self.gl.get_project_by_path_cached(project_and_group)
|
|
27
|
-
existing_schedules:
|
|
27
|
+
existing_schedules: list[ProjectPipelineSchedule] = project.pipelineschedules.list(get_all=True)
|
|
28
28
|
|
|
29
29
|
schedule_ids_by_description: Dict = self._group_schedule_ids_by_description(existing_schedules)
|
|
30
30
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from cli_ui import debug as verbose, info
|
|
2
|
-
from typing import Dict, List, Callable
|
|
1
|
+
from cli_ui import debug as verbose, info, warning
|
|
2
|
+
from typing import Dict, List, Callable, Union
|
|
3
3
|
|
|
4
|
+
from gitlab.base import RESTObjectList, RESTObject
|
|
4
5
|
from gitlab.v4.objects import Group, Project, ProjectLabel, GroupLabel
|
|
5
6
|
|
|
6
7
|
|
|
@@ -15,7 +16,8 @@ class LabelsProcessor:
|
|
|
15
16
|
needs_update: Callable, # self._needs_update passed from AbstractProcessor called process_labels
|
|
16
17
|
):
|
|
17
18
|
# Only get Labels created directly on the project/group
|
|
18
|
-
|
|
19
|
+
existing_group_labels = group_or_project.labels.list(get_all=True, include_ancestor_groups=False)
|
|
20
|
+
existing_group_and_parent_labels = group_or_project.labels.list(get_all=True)
|
|
19
21
|
existing_label_keys: List = []
|
|
20
22
|
|
|
21
23
|
if isinstance(group_or_project, Group):
|
|
@@ -25,7 +27,7 @@ class LabelsProcessor:
|
|
|
25
27
|
|
|
26
28
|
gitlab_labels_to_delete: List = []
|
|
27
29
|
|
|
28
|
-
for label_to_update in
|
|
30
|
+
for label_to_update in existing_group_labels:
|
|
29
31
|
label_name_in_gl = label_to_update.name
|
|
30
32
|
updated_label = False
|
|
31
33
|
verbose(f"Checking if {label_name_in_gl} is in Configuration to update or delete")
|
|
@@ -33,9 +35,7 @@ class LabelsProcessor:
|
|
|
33
35
|
for key, configured_label in configured_labels.items():
|
|
34
36
|
configured_label_name = configured_label.get("name")
|
|
35
37
|
# Key in YAML may not match the "name" value in Gitlab or YAML, so we must match on both
|
|
36
|
-
if (
|
|
37
|
-
configured_label_name is not None and label_name_in_gl == configured_label_name
|
|
38
|
-
) or label_name_in_gl == key:
|
|
38
|
+
if self.configured_label_matches_gitlab_label(configured_label_name, key, label_name_in_gl):
|
|
39
39
|
# label exists in GL, so update
|
|
40
40
|
existing_label_keys.append(key)
|
|
41
41
|
updated_label = True
|
|
@@ -65,10 +65,19 @@ class LabelsProcessor:
|
|
|
65
65
|
self.get_label(group_or_project, label_to_delete).delete()
|
|
66
66
|
|
|
67
67
|
# add new labels
|
|
68
|
+
|
|
68
69
|
for label_key in configured_labels.keys():
|
|
69
70
|
if label_key not in existing_label_keys:
|
|
70
71
|
info(f"Creating new label with key: {label_key}, on {parent_object_type}")
|
|
71
|
-
self.create_new_label(
|
|
72
|
+
self.create_new_label(
|
|
73
|
+
configured_labels, group_or_project, label_key, parent_object_type, existing_group_and_parent_labels
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def configured_label_matches_gitlab_label(configured_label_name: str, key: str, label_name_in_gl: str):
|
|
78
|
+
return (
|
|
79
|
+
configured_label_name is not None and label_name_in_gl == configured_label_name
|
|
80
|
+
) or label_name_in_gl == key
|
|
72
81
|
|
|
73
82
|
@staticmethod
|
|
74
83
|
def get_label(group_or_project, listed_label) -> GroupLabel | ProjectLabel:
|
|
@@ -88,13 +97,25 @@ class LabelsProcessor:
|
|
|
88
97
|
|
|
89
98
|
full_label.save()
|
|
90
99
|
|
|
91
|
-
@staticmethod
|
|
92
100
|
def create_new_label(
|
|
101
|
+
self,
|
|
93
102
|
configured_labels,
|
|
94
103
|
group_or_project: Group | Project,
|
|
95
|
-
|
|
104
|
+
label_key: str,
|
|
96
105
|
parent_object_type: str,
|
|
106
|
+
existing_group_and_parent_labels: Union[List[GroupLabel], List[ProjectLabel]],
|
|
97
107
|
):
|
|
98
|
-
label = configured_labels.get(
|
|
99
|
-
|
|
100
|
-
|
|
108
|
+
label = configured_labels.get(label_key)
|
|
109
|
+
configured_label_name = label.get("name")
|
|
110
|
+
found_existing_label = False
|
|
111
|
+
for existing_label in existing_group_and_parent_labels:
|
|
112
|
+
if self.configured_label_matches_gitlab_label(configured_label_name, label_key, existing_label.name):
|
|
113
|
+
warning(
|
|
114
|
+
f"Label {existing_label.name} already exists either in {group_or_project.name} or on Parent Group, so will not create"
|
|
115
|
+
)
|
|
116
|
+
found_existing_label = True
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
if not found_existing_label:
|
|
120
|
+
info(f"Adding label with key: {label_key} to {parent_object_type}")
|
|
121
|
+
group_or_project.labels.create({"name": label_key, **label})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlabform
|
|
3
|
-
Version: 4.2.
|
|
3
|
+
Version: 4.2.6
|
|
4
4
|
Summary: 🏗 Specialized configuration as a code tool for GitLab projects, groups and more using hierarchical configuration written in YAML
|
|
5
5
|
Author: Greg Dubicki and Contributors
|
|
6
6
|
Project-URL: Homepage, https://gitlabform.github.io/gitlabform/
|
|
@@ -20,7 +20,7 @@ Classifier: Topic :: Software Development :: Version Control :: Git
|
|
|
20
20
|
Requires-Python: >=3.12.0
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: certifi==2025.
|
|
23
|
+
Requires-Dist: certifi==2025.6.15
|
|
24
24
|
Requires-Dist: cli-ui==0.19.0
|
|
25
25
|
Requires-Dist: ez-yaml==1.2.0
|
|
26
26
|
Requires-Dist: Jinja2==3.1.6
|
|
@@ -28,23 +28,23 @@ Requires-Dist: luddite==1.0.4
|
|
|
28
28
|
Requires-Dist: MarkupSafe==3.0.2
|
|
29
29
|
Requires-Dist: mergedeep==1.3.4
|
|
30
30
|
Requires-Dist: packaging==25.0
|
|
31
|
-
Requires-Dist: python-gitlab==
|
|
32
|
-
Requires-Dist: python-gitlab[graphql]==
|
|
33
|
-
Requires-Dist: requests==2.32.
|
|
31
|
+
Requires-Dist: python-gitlab==6.1.0
|
|
32
|
+
Requires-Dist: python-gitlab[graphql]==6.1.0
|
|
33
|
+
Requires-Dist: requests==2.32.4
|
|
34
34
|
Requires-Dist: ruamel.yaml==0.17.21
|
|
35
|
-
Requires-Dist: types-requests==2.32.
|
|
36
|
-
Requires-Dist: types-setuptools==
|
|
35
|
+
Requires-Dist: types-requests==2.32.4.20250611
|
|
36
|
+
Requires-Dist: types-setuptools==80.9.0.20250529
|
|
37
37
|
Requires-Dist: yamlpath==3.8.2
|
|
38
38
|
Provides-Extra: test
|
|
39
|
-
Requires-Dist: coverage==7.
|
|
40
|
-
Requires-Dist: cryptography==
|
|
41
|
-
Requires-Dist: deepdiff==8.
|
|
42
|
-
Requires-Dist: mypy==1.
|
|
43
|
-
Requires-Dist: mypy-extensions==1.
|
|
39
|
+
Requires-Dist: coverage==7.9.2; extra == "test"
|
|
40
|
+
Requires-Dist: cryptography==45.0.5; extra == "test"
|
|
41
|
+
Requires-Dist: deepdiff==8.5.0; extra == "test"
|
|
42
|
+
Requires-Dist: mypy==1.16.1; extra == "test"
|
|
43
|
+
Requires-Dist: mypy-extensions==1.1.0; extra == "test"
|
|
44
44
|
Requires-Dist: pre-commit==2.21.0; extra == "test"
|
|
45
|
-
Requires-Dist: pytest==8.
|
|
46
|
-
Requires-Dist: pytest-cov==6.
|
|
47
|
-
Requires-Dist: pytest-rerunfailures==15.
|
|
45
|
+
Requires-Dist: pytest==8.4.1; extra == "test"
|
|
46
|
+
Requires-Dist: pytest-cov==6.2.1; extra == "test"
|
|
47
|
+
Requires-Dist: pytest-rerunfailures==15.1; extra == "test"
|
|
48
48
|
Requires-Dist: xkcdpass==1.20.0; extra == "test"
|
|
49
49
|
Provides-Extra: docs
|
|
50
50
|
Requires-Dist: mkdocs; extra == "docs"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
certifi==2025.6.15
|
|
2
|
+
cli-ui==0.19.0
|
|
3
|
+
ez-yaml==1.2.0
|
|
4
|
+
Jinja2==3.1.6
|
|
5
|
+
luddite==1.0.4
|
|
6
|
+
MarkupSafe==3.0.2
|
|
7
|
+
mergedeep==1.3.4
|
|
8
|
+
packaging==25.0
|
|
9
|
+
python-gitlab==6.1.0
|
|
10
|
+
python-gitlab[graphql]==6.1.0
|
|
11
|
+
requests==2.32.4
|
|
12
|
+
ruamel.yaml==0.17.21
|
|
13
|
+
types-requests==2.32.4.20250611
|
|
14
|
+
types-setuptools==80.9.0.20250529
|
|
15
|
+
yamlpath==3.8.2
|
|
16
|
+
|
|
17
|
+
[docs]
|
|
18
|
+
mkdocs
|
|
19
|
+
mkdocs-material
|
|
20
|
+
|
|
21
|
+
[test]
|
|
22
|
+
coverage==7.9.2
|
|
23
|
+
cryptography==45.0.5
|
|
24
|
+
deepdiff==8.5.0
|
|
25
|
+
mypy==1.16.1
|
|
26
|
+
mypy-extensions==1.1.0
|
|
27
|
+
pre-commit==2.21.0
|
|
28
|
+
pytest==8.4.1
|
|
29
|
+
pytest-cov==6.2.1
|
|
30
|
+
pytest-rerunfailures==15.1
|
|
31
|
+
xkcdpass==1.20.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gitlabform"
|
|
7
|
-
version = "4.2.
|
|
7
|
+
version = "4.2.6"
|
|
8
8
|
authors = [{ name = "Greg Dubicki and Contributors" }]
|
|
9
9
|
description = "🏗 Specialized configuration as a code tool for GitLab projects, groups and more using hierarchical configuration written in YAML"
|
|
10
10
|
keywords = ["cli", "yaml", "gitlab", "configuration-as-code"]
|
|
@@ -23,7 +23,7 @@ classifiers = [
|
|
|
23
23
|
]
|
|
24
24
|
requires-python = ">=3.12.0"
|
|
25
25
|
dependencies = [
|
|
26
|
-
"certifi==2025.
|
|
26
|
+
"certifi==2025.6.15",
|
|
27
27
|
"cli-ui==0.19.0",
|
|
28
28
|
"ez-yaml==1.2.0",
|
|
29
29
|
"Jinja2==3.1.6",
|
|
@@ -31,12 +31,12 @@ dependencies = [
|
|
|
31
31
|
"MarkupSafe==3.0.2",
|
|
32
32
|
"mergedeep==1.3.4",
|
|
33
33
|
"packaging==25.0",
|
|
34
|
-
"python-gitlab==
|
|
35
|
-
"python-gitlab[graphql]==
|
|
36
|
-
"requests==2.32.
|
|
34
|
+
"python-gitlab==6.1.0",
|
|
35
|
+
"python-gitlab[graphql]==6.1.0",
|
|
36
|
+
"requests==2.32.4",
|
|
37
37
|
"ruamel.yaml==0.17.21",
|
|
38
|
-
"types-requests==2.32.
|
|
39
|
-
"types-setuptools==
|
|
38
|
+
"types-requests==2.32.4.20250611",
|
|
39
|
+
"types-setuptools==80.9.0.20250529",
|
|
40
40
|
"yamlpath==3.8.2",
|
|
41
41
|
]
|
|
42
42
|
|
|
@@ -52,15 +52,15 @@ content-type = "text/markdown"
|
|
|
52
52
|
|
|
53
53
|
[project.optional-dependencies]
|
|
54
54
|
test = [
|
|
55
|
-
"coverage==7.
|
|
56
|
-
"cryptography==
|
|
57
|
-
"deepdiff==8.
|
|
58
|
-
"mypy==1.
|
|
59
|
-
"mypy-extensions==1.
|
|
55
|
+
"coverage==7.9.2",
|
|
56
|
+
"cryptography==45.0.5",
|
|
57
|
+
"deepdiff==8.5.0",
|
|
58
|
+
"mypy==1.16.1",
|
|
59
|
+
"mypy-extensions==1.1.0",
|
|
60
60
|
"pre-commit==2.21.0",
|
|
61
|
-
"pytest==8.
|
|
62
|
-
"pytest-cov==6.
|
|
63
|
-
"pytest-rerunfailures==15.
|
|
61
|
+
"pytest==8.4.1",
|
|
62
|
+
"pytest-cov==6.2.1",
|
|
63
|
+
"pytest-rerunfailures==15.1",
|
|
64
64
|
"xkcdpass==1.20.0",
|
|
65
65
|
]
|
|
66
66
|
docs = ["mkdocs", "mkdocs-material"]
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
certifi==2025.1.31
|
|
2
|
-
cli-ui==0.19.0
|
|
3
|
-
ez-yaml==1.2.0
|
|
4
|
-
Jinja2==3.1.6
|
|
5
|
-
luddite==1.0.4
|
|
6
|
-
MarkupSafe==3.0.2
|
|
7
|
-
mergedeep==1.3.4
|
|
8
|
-
packaging==25.0
|
|
9
|
-
python-gitlab==5.6.0
|
|
10
|
-
python-gitlab[graphql]==5.6.0
|
|
11
|
-
requests==2.32.3
|
|
12
|
-
ruamel.yaml==0.17.21
|
|
13
|
-
types-requests==2.32.0.20250328
|
|
14
|
-
types-setuptools==78.1.0.20250329
|
|
15
|
-
yamlpath==3.8.2
|
|
16
|
-
|
|
17
|
-
[docs]
|
|
18
|
-
mkdocs
|
|
19
|
-
mkdocs-material
|
|
20
|
-
|
|
21
|
-
[test]
|
|
22
|
-
coverage==7.8.0
|
|
23
|
-
cryptography==44.0.2
|
|
24
|
-
deepdiff==8.4.2
|
|
25
|
-
mypy==1.15.0
|
|
26
|
-
mypy-extensions==1.0.0
|
|
27
|
-
pre-commit==2.21.0
|
|
28
|
-
pytest==8.3.5
|
|
29
|
-
pytest-cov==6.1.1
|
|
30
|
-
pytest-rerunfailures==15.0
|
|
31
|
-
xkcdpass==1.20.0
|
|
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
|
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_ldap_links_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_push_rules_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_saml_links_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_settings_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/group/group_variables_processor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/deploy_keys_processor.py
RENAMED
|
File without changes
|
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/job_token_scope_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/merge_requests_approval_rules.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/merge_requests_approvals.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/project_labels_processor.py
RENAMED
|
File without changes
|
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/project_push_rules_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/project_security_settings.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.6}/gitlabform/processors/project/project_settings_processor.py
RENAMED
|
File without changes
|
{gitlabform-4.2.4 → gitlabform-4.2.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
|