gitlabform 4.0.2__tar.gz → 4.0.4__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.
Files changed (136) hide show
  1. {gitlabform-4.0.2 → gitlabform-4.0.4}/PKG-INFO +3 -3
  2. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/multiple_entities_processor.py +0 -16
  3. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/branches_processor.py +24 -35
  4. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/files_processor.py +4 -5
  5. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform.egg-info/PKG-INFO +3 -3
  6. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform.egg-info/requires.txt +2 -2
  7. {gitlabform-4.0.2 → gitlabform-4.0.4}/setup.py +2 -2
  8. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_branches.py +35 -0
  9. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_files.py +36 -0
  10. {gitlabform-4.0.2 → gitlabform-4.0.4}/LICENSE +0 -0
  11. {gitlabform-4.0.2 → gitlabform-4.0.4}/README.md +0 -0
  12. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/__init__.py +0 -0
  13. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/configuration/__init__.py +0 -0
  14. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/configuration/common.py +0 -0
  15. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/configuration/core.py +0 -0
  16. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/configuration/groups.py +0 -0
  17. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/configuration/projects.py +0 -0
  18. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/configuration/transform.py +0 -0
  19. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/constants.py +0 -0
  20. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/__init__.py +0 -0
  21. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/commits.py +0 -0
  22. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/core.py +0 -0
  23. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/group_badges.py +0 -0
  24. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/group_ldap_links.py +0 -0
  25. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/group_variables.py +0 -0
  26. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/groups.py +0 -0
  27. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/merge_requests.py +0 -0
  28. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/pipelines.py +0 -0
  29. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/project_badges.py +0 -0
  30. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/project_deploy_keys.py +0 -0
  31. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/project_merge_requests_approvals.py +0 -0
  32. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/project_protected_environments.py +0 -0
  33. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/projects.py +0 -0
  34. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/python_gitlab.py +0 -0
  35. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/users.py +0 -0
  36. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/gitlab/variables.py +0 -0
  37. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/lists/__init__.py +0 -0
  38. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/lists/filter.py +0 -0
  39. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/lists/groups.py +0 -0
  40. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/lists/projects.py +0 -0
  41. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/output.py +0 -0
  42. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/__init__.py +0 -0
  43. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/abstract_processor.py +0 -0
  44. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/application/__init__.py +0 -0
  45. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/application/application_settings_processor.py +0 -0
  46. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/defining_keys.py +0 -0
  47. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/group/__init__.py +0 -0
  48. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/group/group_badges_processor.py +0 -0
  49. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/group/group_labels_processor.py +0 -0
  50. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/group/group_ldap_links_processor.py +0 -0
  51. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/group/group_members_processor.py +0 -0
  52. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/group/group_saml_links_processor.py +0 -0
  53. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/group/group_settings_processor.py +0 -0
  54. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/group/group_variables_processor.py +0 -0
  55. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/__init__.py +0 -0
  56. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/badges_processor.py +0 -0
  57. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/deploy_keys_processor.py +0 -0
  58. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/hooks_processor.py +0 -0
  59. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/integrations_processor.py +0 -0
  60. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/job_token_scope_processor.py +0 -0
  61. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/members_processor.py +0 -0
  62. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/merge_requests_approval_rules.py +0 -0
  63. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/merge_requests_approvals.py +0 -0
  64. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/project_labels_processor.py +0 -0
  65. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/project_processor.py +0 -0
  66. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/project_push_rules_processor.py +0 -0
  67. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/project_settings_processor.py +0 -0
  68. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/resource_groups_processor.py +0 -0
  69. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/schedules_processor.py +0 -0
  70. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/tags_processor.py +0 -0
  71. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/project/variables_processor.py +0 -0
  72. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/shared/__init__.py +0 -0
  73. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/shared/protected_environments_processor.py +0 -0
  74. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/single_entity_processor.py +0 -0
  75. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/util/__init__.py +0 -0
  76. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/util/decorators.py +0 -0
  77. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/util/difference_logger.py +0 -0
  78. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/processors/util/labels_processor.py +0 -0
  79. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/run.py +0 -0
  80. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform/util.py +0 -0
  81. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform.egg-info/SOURCES.txt +0 -0
  82. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform.egg-info/dependency_links.txt +0 -0
  83. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform.egg-info/entry_points.txt +0 -0
  84. {gitlabform-4.0.2 → gitlabform-4.0.4}/gitlabform.egg-info/top_level.txt +0 -0
  85. {gitlabform-4.0.2 → gitlabform-4.0.4}/setup.cfg +0 -0
  86. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/__init__.py +0 -0
  87. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/__init__.py +0 -0
  88. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/conftest.py +0 -0
  89. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/__init__.py +0 -0
  90. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_application_settings.py +0 -0
  91. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_archive_project.py +0 -0
  92. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_badges.py +0 -0
  93. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_deploy_keys.py +0 -0
  94. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_deploy_keys_all_projects.py +0 -0
  95. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_files_templates.py +0 -0
  96. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_group_badges.py +0 -0
  97. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_group_labels.py +0 -0
  98. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_group_members_case_insensitive.py +0 -0
  99. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_group_members_groups.py +0 -0
  100. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_group_members_users.py +0 -0
  101. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_group_settings.py +0 -0
  102. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_group_variables.py +0 -0
  103. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_hooks.py +0 -0
  104. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_integrations.py +0 -0
  105. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_job_token_scope.py +0 -0
  106. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_members.py +0 -0
  107. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_members_add_group.py +0 -0
  108. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_members_enforce.py +0 -0
  109. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_project_group_members_case_insensitive.py +0 -0
  110. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_project_labels.py +0 -0
  111. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_project_members_case_insensitve.py +0 -0
  112. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_project_settings.py +0 -0
  113. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_resource_groups.py +0 -0
  114. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_running.py +0 -0
  115. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_schedules.py +0 -0
  116. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_tags.py +0 -0
  117. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_token_from_config.py +0 -0
  118. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_transfer_project.py +0 -0
  119. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/acceptance/standard/test_variables.py +0 -0
  120. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/__init__.py +0 -0
  121. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/configuration/__init__.py +0 -0
  122. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/configuration/test_case_sensitivity.py +0 -0
  123. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/configuration/test_inheritance_break_projects_and_groups.py +0 -0
  124. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/configuration/test_inheritance_break_subgroups.py +0 -0
  125. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/configuration/test_inheritance_break_validation.py +0 -0
  126. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/configuration/test_projects_and_groups.py +0 -0
  127. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/configuration/test_skip_groups_skip_projects.py +0 -0
  128. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/configuration/test_subgroups.py +0 -0
  129. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/configuration/test_yaml_version.py +0 -0
  130. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/processors/__init__.py +0 -0
  131. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/processors/test_abstract_processor.py +0 -0
  132. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/processors/test_difference_logger.py +0 -0
  133. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/processors/test_schedules_processor_extended_cron_pattern.py +0 -0
  134. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/test_access_levels.py +0 -0
  135. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/test_non_empty_configs_provider.py +0 -0
  136. {gitlabform-4.0.2 → gitlabform-4.0.4}/tests/unit/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gitlabform
3
- Version: 4.0.2
3
+ Version: 4.0.4
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
@@ -31,7 +31,7 @@ Requires-Dist: ruamel.yaml==0.17.21
31
31
  Requires-Dist: types-requests==2.32.0.20241016
32
32
  Requires-Dist: yamlpath==3.8.2
33
33
  Provides-Extra: test
34
- Requires-Dist: coverage==7.6.4; extra == "test"
34
+ Requires-Dist: coverage==7.6.8; extra == "test"
35
35
  Requires-Dist: cryptography==43.0.3; extra == "test"
36
36
  Requires-Dist: deepdiff==8.0.1; extra == "test"
37
37
  Requires-Dist: mypy==1.13.0; extra == "test"
@@ -39,7 +39,7 @@ Requires-Dist: mypy-extensions==1.0.0; extra == "test"
39
39
  Requires-Dist: pre-commit==2.21.0; extra == "test"
40
40
  Requires-Dist: pytest==8.3.3; extra == "test"
41
41
  Requires-Dist: pytest-cov==6.0.0; extra == "test"
42
- Requires-Dist: pytest-rerunfailures==14.0; extra == "test"
42
+ Requires-Dist: pytest-rerunfailures==15.0; extra == "test"
43
43
  Requires-Dist: xkcdpass==1.19.9; extra == "test"
44
44
  Provides-Extra: docs
45
45
  Requires-Dist: mkdocs; extra == "docs"
@@ -4,7 +4,6 @@ from cli_ui import fatal
4
4
 
5
5
  import abc
6
6
  from typing import Callable, Union, Any
7
- import time
8
7
  from gitlabform.constants import EXIT_INVALID_INPUT
9
8
  from gitlabform.gitlab import GitLab
10
9
  from gitlabform.processors.abstract_processor import AbstractProcessor
@@ -70,21 +69,6 @@ class MultipleEntitiesProcessor(AbstractProcessor, metaclass=abc.ABCMeta):
70
69
  else:
71
70
  enforce = False
72
71
 
73
- if (
74
- "members" in configuration
75
- and ("protected_environments" or "merge_requests_approval_rules")
76
- in configuration
77
- ):
78
- # When gitlabform needs to update project membership and also configure
79
- # settings that are dependent on the members (i.e. environment protection
80
- # or MR approval rule), there seems to be an issue in GitLab. Automated
81
- # acceptance tests in gitlabform creates new user and adds to the project
82
- # followed by configuring other setting. In that scenario need to wait a little
83
- # before calling GitLab's REST API for processing other settings. Otherwise
84
- # the users are not available to be configured in other settings and it
85
- # does not return any error either"
86
- time.sleep(2)
87
-
88
72
  # TODO: move/convert this to a configuration validation phase
89
73
  self._find_duplicates(project_or_group, entities_in_configuration)
90
74
 
@@ -1,7 +1,7 @@
1
1
  from typing import Optional
2
2
  from logging import debug, info
3
3
  from cli_ui import warning, fatal
4
- from gitlab.v4.objects import Project, ProjectBranch, ProjectProtectedBranch
4
+ from gitlab.v4.objects import Project, ProjectProtectedBranch
5
5
  from gitlab import (
6
6
  GitlabGetError,
7
7
  GitlabDeleteError,
@@ -10,7 +10,6 @@ from gitlab import (
10
10
  from gitlabform.constants import EXIT_INVALID_INPUT, EXIT_PROCESSING_ERROR
11
11
  from gitlabform.gitlab import GitLab
12
12
  from gitlabform.processors.abstract_processor import AbstractProcessor
13
- import time
14
13
 
15
14
 
16
15
  class BranchesProcessor(AbstractProcessor):
@@ -19,18 +18,6 @@ class BranchesProcessor(AbstractProcessor):
19
18
  self.strict = strict
20
19
 
21
20
  def _process_configuration(self, project_and_group: str, configuration: dict):
22
- if "members" in configuration:
23
- # When gitlabform needs to update project membership and also
24
- # configure branch protection, there seems to be a race condition
25
- # or delay in GitLab. Automated acceptance tests in gitlabform
26
- # creates new user and adds to the project followed by configuring
27
- # branch protection setting. In that scenario need to wait a little
28
- # before calling GitLab's REST API for branch protection. Otherwise
29
- # the API returns error code 422 with an message like "Push access
30
- # levels user is not a member of the project"
31
-
32
- time.sleep(2)
33
-
34
21
  project: Project = self.gl.get_project_by_path_cached(project_and_group)
35
22
 
36
23
  for branch in sorted(configuration["branches"]):
@@ -46,20 +33,20 @@ class BranchesProcessor(AbstractProcessor):
46
33
  """
47
34
  Process branch protection according to gitlabform config.
48
35
  """
49
- branch: Optional[ProjectBranch] = None
50
36
  protected_branch: Optional[ProjectProtectedBranch] = None
51
37
 
52
- try:
53
- branch = project.branches.get(branch_name)
54
- except GitlabGetError:
55
- message = f"Branch '{branch_name}' not found when trying to processing it to be protected/unprotected!"
56
- if self.strict:
57
- fatal(
58
- message,
59
- exit_code=EXIT_INVALID_INPUT,
60
- )
61
- else:
62
- warning(message)
38
+ if not self.is_branch_name_wildcard(branch_name):
39
+ try:
40
+ branch = project.branches.get(branch_name)
41
+ except GitlabGetError:
42
+ message = f"Branch '{branch_name}' not found when processing it to be protected/unprotected!"
43
+ if self.strict:
44
+ fatal(
45
+ message,
46
+ exit_code=EXIT_INVALID_INPUT,
47
+ )
48
+ else:
49
+ warning(message)
63
50
 
64
51
  try:
65
52
  protected_branch = project.protectedbranches.get(branch_name)
@@ -67,7 +54,7 @@ class BranchesProcessor(AbstractProcessor):
67
54
  message = f"The branch '{branch_name}' is not protected!"
68
55
  debug(message)
69
56
 
70
- if branch and branch_config.get("protected"):
57
+ if branch_config.get("protected"):
71
58
  if protected_branch and self._needs_update(
72
59
  protected_branch.attributes, branch_config
73
60
  ):
@@ -79,27 +66,25 @@ class BranchesProcessor(AbstractProcessor):
79
66
  # protected branch is not the same. So, removing existing branch protection and
80
67
  # reconfiguring branch protection seems simpler.
81
68
  self.unprotect_branch(protected_branch)
82
- self.protect_branch(project, branch, branch_config)
69
+ self.protect_branch(project, branch_name, branch_config)
83
70
  elif not protected_branch:
84
- self.protect_branch(project, branch, branch_config)
71
+ self.protect_branch(project, branch_name, branch_config)
85
72
  elif protected_branch and not branch_config.get("protected"):
86
73
  self.unprotect_branch(protected_branch)
87
74
 
88
- def protect_branch(
89
- self, project: Project, branch: ProjectBranch, branch_config: dict
90
- ):
75
+ def protect_branch(self, project: Project, branch_name: str, branch_config: dict):
91
76
  """
92
77
  Protect a branch using given config.
93
78
  Raise exception if running in strict mode.
94
79
  """
95
80
 
96
- debug("Setting branch '%s' as protected", branch.name)
81
+ debug("Setting branch '%s' as protected", branch_name)
97
82
 
98
83
  try:
99
- project.protectedbranches.create({"name": branch.name, **branch_config})
84
+ project.protectedbranches.create({"name": branch_name, **branch_config})
100
85
  except GitlabCreateError as e:
101
86
  message = (
102
- f"Protecting branch '{branch.name}' failed! Error '{e.error_message}"
87
+ f"Protecting branch '{branch_name}' failed! Error '{e.error_message}"
103
88
  )
104
89
 
105
90
  if self.strict:
@@ -149,3 +134,7 @@ class BranchesProcessor(AbstractProcessor):
149
134
  item["group_id"] = self.gl.get_group_id(item.pop("group"))
150
135
 
151
136
  return branch_config
137
+
138
+ @staticmethod
139
+ def is_branch_name_wildcard(branch):
140
+ return "*" in branch or "?" in branch
@@ -135,10 +135,12 @@ class FilesProcessor(AbstractProcessor):
135
135
  )
136
136
 
137
137
  try:
138
+ # Returns base64 encoded content: https://python-gitlab.readthedocs.io/en/stable/gl_objects/projects.html#project-files
138
139
  repo_file: ProjectFile = project.files.get(
139
140
  file_path=file, ref=branch.name
140
141
  )
141
- current_content = repo_file.content
142
+ decoded_file: bytes = repo_file.decode()
143
+ current_content: str = decoded_file.decode("utf-8")
142
144
 
143
145
  if current_content != new_content:
144
146
  if configuration.get("files|" + file + "|overwrite"):
@@ -243,12 +245,9 @@ class FilesProcessor(AbstractProcessor):
243
245
  # ...and protect the branch again after the operation
244
246
  if configuration.get("branches|" + branch.name + "|protected"):
245
247
  debug("> Protecting the branch again.")
246
- project_branch: ProjectBranch = project.branches.get(
247
- branch.name
248
- )
249
248
  branch_config: dict = configuration["branches"][branch.name]
250
249
  self.branch_processor.protect_branch(
251
- project, project_branch, branch_config
250
+ project, branch.name, branch_config
252
251
  )
253
252
 
254
253
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gitlabform
3
- Version: 4.0.2
3
+ Version: 4.0.4
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
@@ -31,7 +31,7 @@ Requires-Dist: ruamel.yaml==0.17.21
31
31
  Requires-Dist: types-requests==2.32.0.20241016
32
32
  Requires-Dist: yamlpath==3.8.2
33
33
  Provides-Extra: test
34
- Requires-Dist: coverage==7.6.4; extra == "test"
34
+ Requires-Dist: coverage==7.6.8; extra == "test"
35
35
  Requires-Dist: cryptography==43.0.3; extra == "test"
36
36
  Requires-Dist: deepdiff==8.0.1; extra == "test"
37
37
  Requires-Dist: mypy==1.13.0; extra == "test"
@@ -39,7 +39,7 @@ Requires-Dist: mypy-extensions==1.0.0; extra == "test"
39
39
  Requires-Dist: pre-commit==2.21.0; extra == "test"
40
40
  Requires-Dist: pytest==8.3.3; extra == "test"
41
41
  Requires-Dist: pytest-cov==6.0.0; extra == "test"
42
- Requires-Dist: pytest-rerunfailures==14.0; extra == "test"
42
+ Requires-Dist: pytest-rerunfailures==15.0; extra == "test"
43
43
  Requires-Dist: xkcdpass==1.19.9; extra == "test"
44
44
  Provides-Extra: docs
45
45
  Requires-Dist: mkdocs; extra == "docs"
@@ -17,7 +17,7 @@ mkdocs
17
17
  mkdocs-material
18
18
 
19
19
  [test]
20
- coverage==7.6.4
20
+ coverage==7.6.8
21
21
  cryptography==43.0.3
22
22
  deepdiff==8.0.1
23
23
  mypy==1.13.0
@@ -25,5 +25,5 @@ mypy-extensions==1.0.0
25
25
  pre-commit==2.21.0
26
26
  pytest==8.3.3
27
27
  pytest-cov==6.0.0
28
- pytest-rerunfailures==14.0
28
+ pytest-rerunfailures==15.0
29
29
  xkcdpass==1.19.9
@@ -56,7 +56,7 @@ setup(
56
56
  ],
57
57
  extras_require={
58
58
  "test": [
59
- "coverage==7.6.4",
59
+ "coverage==7.6.8",
60
60
  "cryptography==43.0.3",
61
61
  "deepdiff==8.0.1",
62
62
  "mypy==1.13.0",
@@ -64,7 +64,7 @@ setup(
64
64
  "pre-commit==2.21.0", # not really for tests, but for development
65
65
  "pytest==8.3.3",
66
66
  "pytest-cov==6.0.0",
67
- "pytest-rerunfailures==14.0",
67
+ "pytest-rerunfailures==15.0",
68
68
  "xkcdpass==1.19.9",
69
69
  ],
70
70
  "docs": [
@@ -60,6 +60,41 @@ class TestBranches:
60
60
  assert merge_access_user_ids is None
61
61
  assert unprotect_access_level is None
62
62
 
63
+ def test__protect_with_wildcard(self, project):
64
+ branch_wildcard = "r-*"
65
+ config_protect_branch = f"""
66
+ projects_and_groups:
67
+ {project.path_with_namespace}:
68
+ branches:
69
+ "{branch_wildcard}":
70
+ protected: true
71
+ push_access_level: {AccessLevel.NO_ACCESS.value}
72
+ merge_access_level: {AccessLevel.MAINTAINER.value}
73
+ unprotect_access_level: {AccessLevel.MAINTAINER.value}
74
+ """
75
+
76
+ run_gitlabform(config_protect_branch, project.path_with_namespace)
77
+
78
+ (
79
+ push_access_levels,
80
+ merge_access_levels,
81
+ push_access_user_ids,
82
+ merge_access_user_ids,
83
+ _,
84
+ _,
85
+ unprotect_access_level,
86
+ ) = get_only_branch_access_levels(project, branch_wildcard)
87
+ assert push_access_levels == [AccessLevel.NO_ACCESS.value]
88
+ assert merge_access_levels == [AccessLevel.MAINTAINER.value]
89
+ assert push_access_user_ids == []
90
+ assert merge_access_user_ids == []
91
+ assert unprotect_access_level is AccessLevel.MAINTAINER.value
92
+
93
+ wildcard_matching_branch = project.branches.create(
94
+ {"branch": "r-1", "ref": "main"}
95
+ )
96
+ assert wildcard_matching_branch.protected is True
97
+
63
98
  def test__config_with_access_level_names(self, project, branch):
64
99
  config_with_access_levels_names = f"""
65
100
  projects_and_groups:
@@ -91,6 +91,42 @@ class TestFiles:
91
91
  # check if main stays protected after the file update
92
92
  assert the_branch.protected is True
93
93
 
94
+ def test__does_not_commit_file_if_content_matches(self, project_for_function):
95
+ set_file_specific_branch = f"""
96
+ projects_and_groups:
97
+ {project_for_function.path_with_namespace}:
98
+ branches:
99
+ main:
100
+ protected: true
101
+ push_access_level: maintainer
102
+ merge_access_level: developer
103
+ unprotect_access_level: maintainer
104
+ files:
105
+ "README.md":
106
+ overwrite: true
107
+ branches:
108
+ - main
109
+ content: {DEFAULT_README}
110
+ """
111
+
112
+ run_gitlabform(
113
+ set_file_specific_branch, project_for_function.path_with_namespace
114
+ )
115
+
116
+ the_branch = project_for_function.branches.get("main")
117
+ assert not any(
118
+ [
119
+ text in the_branch.commit["message"]
120
+ for text in ["Automated", "made by gitlabform"]
121
+ ]
122
+ )
123
+
124
+ project_file = project_for_function.files.get(ref="main", file_path="README.md")
125
+ assert project_file.decode().decode("utf-8") == DEFAULT_README
126
+
127
+ # check if main stays protected after the file update
128
+ assert the_branch.protected is True
129
+
94
130
  def test__set_file_strongly_protected_branch(self, project, no_access_branch):
95
131
  set_file_specific_branch = f"""
96
132
  projects_and_groups:
File without changes
File without changes
File without changes
File without changes
File without changes