gitlabform 3.9.0__tar.gz → 3.9.2__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-3.9.0 → gitlabform-3.9.2}/PKG-INFO +9 -9
  2. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/lists/projects.py +109 -37
  3. gitlabform-3.9.2/gitlabform/processors/project/hooks_processor.py +70 -0
  4. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform.egg-info/PKG-INFO +9 -9
  5. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform.egg-info/requires.txt +8 -8
  6. {gitlabform-3.9.0 → gitlabform-3.9.2}/setup.py +8 -8
  7. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/conftest.py +23 -0
  8. gitlabform-3.9.2/tests/acceptance/standard/test_hooks.py +266 -0
  9. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_transfer_project.py +107 -0
  10. gitlabform-3.9.0/gitlabform/processors/project/hooks_processor.py +0 -66
  11. gitlabform-3.9.0/tests/acceptance/standard/test_hooks.py +0 -188
  12. {gitlabform-3.9.0 → gitlabform-3.9.2}/LICENSE +0 -0
  13. {gitlabform-3.9.0 → gitlabform-3.9.2}/README.md +0 -0
  14. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/__init__.py +0 -0
  15. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/configuration/__init__.py +0 -0
  16. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/configuration/common.py +0 -0
  17. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/configuration/core.py +0 -0
  18. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/configuration/groups.py +0 -0
  19. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/configuration/projects.py +0 -0
  20. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/configuration/transform.py +0 -0
  21. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/constants.py +0 -0
  22. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/__init__.py +0 -0
  23. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/branches.py +0 -0
  24. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/commits.py +0 -0
  25. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/core.py +0 -0
  26. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/group_badges.py +0 -0
  27. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/group_ldap_links.py +0 -0
  28. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/group_variables.py +0 -0
  29. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/groups.py +0 -0
  30. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/integrations.py +0 -0
  31. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/members.py +0 -0
  32. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/merge_requests.py +0 -0
  33. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/pipelines.py +0 -0
  34. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/project_badges.py +0 -0
  35. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/project_deploy_keys.py +0 -0
  36. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/project_merge_requests_approvals.py +0 -0
  37. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/project_protected_environments.py +0 -0
  38. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/projects.py +0 -0
  39. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/repositories.py +0 -0
  40. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/resource_groups.py +0 -0
  41. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/schedules.py +0 -0
  42. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/users.py +0 -0
  43. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/gitlab/variables.py +0 -0
  44. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/lists/__init__.py +0 -0
  45. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/lists/filter.py +0 -0
  46. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/lists/groups.py +0 -0
  47. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/output.py +0 -0
  48. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/__init__.py +0 -0
  49. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/abstract_processor.py +0 -0
  50. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/defining_keys.py +0 -0
  51. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/group/__init__.py +0 -0
  52. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/group/group_badges_processor.py +0 -0
  53. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/group/group_ldap_links_processor.py +0 -0
  54. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/group/group_members_processor.py +0 -0
  55. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/group/group_settings_processor.py +0 -0
  56. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/group/group_variables_processor.py +0 -0
  57. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/multiple_entities_processor.py +0 -0
  58. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/__init__.py +0 -0
  59. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/badges_processor.py +0 -0
  60. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/branches_processor.py +0 -0
  61. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/deploy_keys_processor.py +0 -0
  62. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/files_processor.py +0 -0
  63. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/integrations_processor.py +0 -0
  64. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/members_processor.py +0 -0
  65. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/merge_requests_approval_rules.py +0 -0
  66. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/merge_requests_approvals.py +0 -0
  67. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/project_processor.py +0 -0
  68. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/project_push_rules_processor.py +0 -0
  69. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/project_settings_processor.py +0 -0
  70. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/resource_groups_processor.py +0 -0
  71. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/schedules_processor.py +0 -0
  72. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/tags_processor.py +0 -0
  73. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/project/variables_processor.py +0 -0
  74. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/shared/__init__.py +0 -0
  75. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/shared/protected_environments_processor.py +0 -0
  76. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/single_entity_processor.py +0 -0
  77. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/util/__init__.py +0 -0
  78. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/util/branch_protector.py +0 -0
  79. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/util/decorators.py +0 -0
  80. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/processors/util/difference_logger.py +0 -0
  81. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/run.py +0 -0
  82. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform/util.py +0 -0
  83. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform.egg-info/SOURCES.txt +0 -0
  84. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform.egg-info/dependency_links.txt +0 -0
  85. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform.egg-info/entry_points.txt +0 -0
  86. {gitlabform-3.9.0 → gitlabform-3.9.2}/gitlabform.egg-info/top_level.txt +0 -0
  87. {gitlabform-3.9.0 → gitlabform-3.9.2}/setup.cfg +0 -0
  88. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/__init__.py +0 -0
  89. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/__init__.py +0 -0
  90. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/__init__.py +0 -0
  91. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_archive_project.py +0 -0
  92. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_badges.py +0 -0
  93. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_branches.py +0 -0
  94. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_deploy_keys.py +0 -0
  95. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_deploy_keys_all_projects.py +0 -0
  96. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_files.py +0 -0
  97. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_files_all.py +0 -0
  98. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_files_protected.py +0 -0
  99. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_files_templates.py +0 -0
  100. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_group_badges.py +0 -0
  101. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_group_members_case_insensitive.py +0 -0
  102. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_group_members_groups.py +0 -0
  103. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_group_members_users.py +0 -0
  104. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_group_settings.py +0 -0
  105. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_group_variables.py +0 -0
  106. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_inheritance_break.py +0 -0
  107. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_integrations.py +0 -0
  108. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_members.py +0 -0
  109. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_members_add_group.py +0 -0
  110. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_members_enforce.py +0 -0
  111. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_project_group_members_case_insensitive.py +0 -0
  112. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_project_members_case_insensitve.py +0 -0
  113. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_project_settings.py +0 -0
  114. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_resource_groups.py +0 -0
  115. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_running.py +0 -0
  116. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_schedules.py +0 -0
  117. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_tags.py +0 -0
  118. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_token_from_config.py +0 -0
  119. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/acceptance/standard/test_variables.py +0 -0
  120. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/__init__.py +0 -0
  121. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/configuration/__init__.py +0 -0
  122. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/configuration/test_case_sensitivity.py +0 -0
  123. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/configuration/test_inheritance_break_projects_and_groups.py +0 -0
  124. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/configuration/test_inheritance_break_subgroups.py +0 -0
  125. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/configuration/test_inheritance_break_validation.py +0 -0
  126. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/configuration/test_projects_and_groups.py +0 -0
  127. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/configuration/test_skip_groups_skip_projects.py +0 -0
  128. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/configuration/test_subgroups.py +0 -0
  129. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/configuration/test_yaml_version.py +0 -0
  130. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/processors/__init__.py +0 -0
  131. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/processors/test_abstract_processor.py +0 -0
  132. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/processors/test_branch_protector.py +0 -0
  133. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/processors/test_difference_logger.py +0 -0
  134. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/test_access_levels.py +0 -0
  135. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/test_non_empty_configs_provider.py +0 -0
  136. {gitlabform-3.9.0 → gitlabform-3.9.2}/tests/unit/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gitlabform
3
- Version: 3.9.0
3
+ Version: 3.9.2
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
@@ -28,22 +28,22 @@ Requires-Dist: Jinja2==3.1.3
28
28
  Requires-Dist: luddite==1.0.2
29
29
  Requires-Dist: MarkupSafe==2.1.5
30
30
  Requires-Dist: mergedeep==1.3.4
31
- Requires-Dist: packaging==23.2
31
+ Requires-Dist: packaging==24.0
32
32
  Requires-Dist: python-gitlab==4.4.0
33
33
  Requires-Dist: requests==2.31.0
34
34
  Requires-Dist: ruamel.yaml==0.17.21
35
- Requires-Dist: types-requests==2.31.0.20240125
35
+ Requires-Dist: types-requests==2.31.0.20240311
36
36
  Requires-Dist: yamlpath==3.8.2
37
37
  Provides-Extra: test
38
- Requires-Dist: coverage==7.4.1; extra == "test"
39
- Requires-Dist: cryptography==42.0.2; extra == "test"
38
+ Requires-Dist: coverage==7.4.4; extra == "test"
39
+ Requires-Dist: cryptography==42.0.5; extra == "test"
40
40
  Requires-Dist: deepdiff==6.7.1; extra == "test"
41
- Requires-Dist: mypy==1.8.0; extra == "test"
41
+ Requires-Dist: mypy==1.9.0; extra == "test"
42
42
  Requires-Dist: mypy-extensions==1.0.0; extra == "test"
43
43
  Requires-Dist: pre-commit==2.21.0; extra == "test"
44
- Requires-Dist: pytest==7.4.4; extra == "test"
45
- Requires-Dist: pytest-cov==4.1.0; extra == "test"
46
- Requires-Dist: pytest-rerunfailures==13.0; extra == "test"
44
+ Requires-Dist: pytest==8.1.1; extra == "test"
45
+ Requires-Dist: pytest-cov==5.0.0; extra == "test"
46
+ Requires-Dist: pytest-rerunfailures==14.0; extra == "test"
47
47
  Requires-Dist: xkcdpass==1.19.8; extra == "test"
48
48
  Provides-Extra: docs
49
49
  Requires-Dist: mkdocs; extra == "docs"
@@ -1,4 +1,4 @@
1
- from typing import Tuple
1
+ from typing import Optional, Tuple
2
2
  from logging import debug
3
3
  from cli_ui import fatal
4
4
 
@@ -50,27 +50,19 @@ class ProjectsProvider(GroupsProvider):
50
50
 
51
51
  except NotFoundException:
52
52
  debug("Could not find '%s'", target)
53
- debug(
54
- "Checking if it's a project that needs to be transferred from elsewhere"
55
- )
56
- project_transfer_source = self.configuration.config["projects_and_groups"][
53
+ if project_transfer_source := self._get_project_transfer_source_from_config(
57
54
  target
58
- ]["project"]["transfer_from"]
59
-
60
- if project_transfer_source:
61
- try:
62
- maybe_project = self.gitlab.get_project_case_insensitive(
63
- project_transfer_source
64
- )
65
- debug(
66
- "Found a project '%s' to be transferred to '%s'",
67
- maybe_project["path_with_namespace"],
68
- target,
69
- )
55
+ ):
56
+ if self._find_project_transfer_source_in_gitlab(
57
+ project_transfer_source
58
+ ):
70
59
  projects.add_requested([target])
71
- except NotFoundException:
72
- # it's a group or a subgroup - ignore it here
73
- pass
60
+ else:
61
+ fatal(
62
+ f"""Configuration contains project {target} to be transferred from {project_transfer_source}
63
+ but the source project cannot be found in GitLab!""",
64
+ exit_code=EXIT_INVALID_INPUT,
65
+ )
74
66
 
75
67
  return projects
76
68
 
@@ -85,21 +77,19 @@ class ProjectsProvider(GroupsProvider):
85
77
  projects.add_requested(projects_from_groups)
86
78
  projects.add_omitted(OmissionReason.ARCHIVED, archived_projects_from_groups)
87
79
 
88
- if target == "ALL_DEFINED":
89
- # in this case we also need to get the list of projects explicitly
90
- # defined in the configuration
91
-
92
- projects_from_configuration = self.configuration.get_projects()
80
+ projects_from_configuration = self.configuration.get_projects()
93
81
 
94
- # ...but we don't need to re-check for being archived projects that we
95
- # already got from groups
82
+ # TODO: this check should be case-insensitive
83
+ projects_from_configuration_not_from_groups = [
84
+ project
85
+ for project in projects_from_configuration
86
+ if project not in projects.requested
87
+ ]
96
88
 
97
- # TODO: this check should be case-insensitive
98
- projects_from_configuration_not_from_groups = [
99
- project
100
- for project in projects_from_configuration
101
- if project not in projects.requested
102
- ]
89
+ if target == "ALL_DEFINED":
90
+ # in this case we also need to get the list of projects explicitly
91
+ # defined in the configuration, but we don't need to re-check for
92
+ # being archived projects that we already got from groups
103
93
 
104
94
  archived_projects_from_configuration_not_from_groups = (
105
95
  self._verify_if_projects_exist_and_get_archived_projects(
@@ -112,6 +102,26 @@ class ProjectsProvider(GroupsProvider):
112
102
  OmissionReason.ARCHIVED,
113
103
  archived_projects_from_configuration_not_from_groups,
114
104
  )
105
+ else:
106
+ # in all other cases, we also need to look for projects in the config
107
+ # that are being transferred to a different namespace
108
+ for project in projects_from_configuration_not_from_groups:
109
+ # if the target is a group, and the project is not in the group
110
+ if (
111
+ len(groups.get_effective()) == 1
112
+ and target != "ALL"
113
+ and target != project.rsplit("/", 1)[0]
114
+ ):
115
+ debug(
116
+ "Ignore project '%s', since it's not in target group '%s",
117
+ project,
118
+ target,
119
+ )
120
+ else:
121
+ if (maybe_projects := self._get_single_project(project)) and len(
122
+ maybe_projects.get_effective()
123
+ ) != 0:
124
+ projects.add_requested([project])
115
125
 
116
126
  # TODO: consider checking for skipped earlier to avoid making requests for projects that will be skipped anyway
117
127
  projects.add_omitted(
@@ -130,10 +140,28 @@ class ProjectsProvider(GroupsProvider):
130
140
  if project_object["archived"]:
131
141
  archived.append(project_object["path_with_namespace"])
132
142
  except NotFoundException:
133
- fatal(
134
- f"Configuration contains project {project} but it cannot be found in GitLab!",
135
- exit_code=EXIT_INVALID_INPUT,
136
- )
143
+ debug("Could not find '%s'", project)
144
+ if project_transfer_source := self._get_project_transfer_source_from_config(
145
+ project
146
+ ):
147
+ source_project = self._find_project_transfer_source_in_gitlab(
148
+ project_transfer_source
149
+ )
150
+ if source_project:
151
+ if source_project["archived"]:
152
+ archived.append(project)
153
+ else:
154
+ fatal(
155
+ f"""Configuration contains project {project} to be transferred from {project_transfer_source}
156
+ but the source project cannot be found in GitLab!""",
157
+ exit_code=EXIT_INVALID_INPUT,
158
+ )
159
+ else:
160
+ fatal(
161
+ f"Configuration contains project {project} but it cannot be found in GitLab and it's not a project that is configured to be transferred from elsewhere.",
162
+ exit_code=EXIT_INVALID_INPUT,
163
+ )
164
+
137
165
  return archived
138
166
 
139
167
  def _get_all_and_archived_projects_from_groups(
@@ -165,3 +193,47 @@ class ProjectsProvider(GroupsProvider):
165
193
  skipped.append(project)
166
194
 
167
195
  return skipped
196
+
197
+ def _get_project_transfer_source_from_config(self, project: str) -> Optional[str]:
198
+ try:
199
+ debug(
200
+ "Checking if project '%s' needs to be transferred from elsewhere",
201
+ project,
202
+ )
203
+ project_transfer_source = self.configuration.config["projects_and_groups"][
204
+ project
205
+ ]["project"]["transfer_from"]
206
+ debug(
207
+ "Project '%s' needs to be transferred from '%s'",
208
+ project,
209
+ project_transfer_source,
210
+ )
211
+ except KeyError:
212
+ debug("'%s' is not a project needing to be transferred", project)
213
+ return None
214
+
215
+ return project_transfer_source
216
+
217
+ def _find_project_transfer_source_in_gitlab(
218
+ self, project_transfer_source: str
219
+ ) -> Optional[dict]:
220
+ try:
221
+ debug(
222
+ "Checking if project transfer source ('%s') exists in GitLab",
223
+ project_transfer_source,
224
+ )
225
+ maybe_project = self.gitlab.get_project_case_insensitive(
226
+ project_transfer_source
227
+ )
228
+ debug(
229
+ "Project transfer source ('%s') exists",
230
+ project_transfer_source,
231
+ )
232
+ except NotFoundException:
233
+ debug(
234
+ "Project transfer source ('%s') does not exist",
235
+ project_transfer_source,
236
+ )
237
+ return None
238
+
239
+ return maybe_project
@@ -0,0 +1,70 @@
1
+ from logging import debug
2
+ from typing import Dict, Any, List
3
+
4
+ from gitlab.base import RESTObject, RESTObjectList
5
+ from gitlab.v4.objects import Project
6
+
7
+ from gitlabform.gitlab import GitLab
8
+ from gitlabform.processors.abstract_processor import AbstractProcessor
9
+
10
+
11
+ class HooksProcessor(AbstractProcessor):
12
+ def __init__(self, gitlab: GitLab):
13
+ super().__init__("hooks", gitlab)
14
+
15
+ def _process_configuration(self, project_and_group: str, configuration: dict):
16
+ debug("Processing hooks...")
17
+ project: Project = self.gl.projects.get(project_and_group)
18
+ project_hooks: RESTObjectList | List[RESTObject] = project.hooks.list()
19
+ hooks_in_config: tuple[str, ...] = tuple(
20
+ x for x in sorted(configuration["hooks"]) if x != "enforce"
21
+ )
22
+
23
+ for hook in hooks_in_config:
24
+ hook_in_gitlab: RESTObject | None = next(
25
+ (h for h in project_hooks if h.url == hook), None
26
+ )
27
+ hook_config = {"url": hook}
28
+ hook_config.update(configuration["hooks"][hook])
29
+
30
+ hook_id = hook_in_gitlab.id if hook_in_gitlab else None
31
+
32
+ # Process hooks configured for deletion
33
+ if configuration.get("hooks|" + hook + "|delete"):
34
+ if hook_id:
35
+ debug(f"Deleting hook '{hook}'")
36
+ project.hooks.delete(hook_id)
37
+ debug(f"Deleted hook '{hook}'")
38
+ else:
39
+ debug(f"Not deleting hook '{hook}', because it doesn't exist")
40
+ continue
41
+
42
+ # Process new hook creation
43
+ if not hook_id:
44
+ debug(f"Creating hook '{hook}'")
45
+ created_hook: RESTObject = project.hooks.create(hook_config)
46
+ debug(f"Created hook: {created_hook}")
47
+ continue
48
+
49
+ # Processing existing hook updates
50
+ gl_hook: dict = hook_in_gitlab.asdict() if hook_in_gitlab else {}
51
+ if self._needs_update(gl_hook, hook_config):
52
+ debug(
53
+ f"The hook '{hook}' config is different from what's in gitlab OR it contains a token"
54
+ )
55
+ debug(f"Updating hook '{hook}'")
56
+ updated_hook: Dict[str, Any] = project.hooks.update(
57
+ hook_id, hook_config
58
+ )
59
+ debug(f"Updated hook: {updated_hook}")
60
+ else:
61
+ debug(f"Hook '{hook}' remains unchanged")
62
+
63
+ # Process hook config enforcements
64
+ if configuration.get("hooks|enforce"):
65
+ for gh in project_hooks:
66
+ if gh.url not in hooks_in_config:
67
+ debug(
68
+ f"Deleting hook '{gh.url}' currently setup in the project but it is not in the configuration and enforce is enabled"
69
+ )
70
+ project.hooks.delete(gh.id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gitlabform
3
- Version: 3.9.0
3
+ Version: 3.9.2
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
@@ -28,22 +28,22 @@ Requires-Dist: Jinja2==3.1.3
28
28
  Requires-Dist: luddite==1.0.2
29
29
  Requires-Dist: MarkupSafe==2.1.5
30
30
  Requires-Dist: mergedeep==1.3.4
31
- Requires-Dist: packaging==23.2
31
+ Requires-Dist: packaging==24.0
32
32
  Requires-Dist: python-gitlab==4.4.0
33
33
  Requires-Dist: requests==2.31.0
34
34
  Requires-Dist: ruamel.yaml==0.17.21
35
- Requires-Dist: types-requests==2.31.0.20240125
35
+ Requires-Dist: types-requests==2.31.0.20240311
36
36
  Requires-Dist: yamlpath==3.8.2
37
37
  Provides-Extra: test
38
- Requires-Dist: coverage==7.4.1; extra == "test"
39
- Requires-Dist: cryptography==42.0.2; extra == "test"
38
+ Requires-Dist: coverage==7.4.4; extra == "test"
39
+ Requires-Dist: cryptography==42.0.5; extra == "test"
40
40
  Requires-Dist: deepdiff==6.7.1; extra == "test"
41
- Requires-Dist: mypy==1.8.0; extra == "test"
41
+ Requires-Dist: mypy==1.9.0; extra == "test"
42
42
  Requires-Dist: mypy-extensions==1.0.0; extra == "test"
43
43
  Requires-Dist: pre-commit==2.21.0; extra == "test"
44
- Requires-Dist: pytest==7.4.4; extra == "test"
45
- Requires-Dist: pytest-cov==4.1.0; extra == "test"
46
- Requires-Dist: pytest-rerunfailures==13.0; extra == "test"
44
+ Requires-Dist: pytest==8.1.1; extra == "test"
45
+ Requires-Dist: pytest-cov==5.0.0; extra == "test"
46
+ Requires-Dist: pytest-rerunfailures==14.0; extra == "test"
47
47
  Requires-Dist: xkcdpass==1.19.8; extra == "test"
48
48
  Provides-Extra: docs
49
49
  Requires-Dist: mkdocs; extra == "docs"
@@ -5,11 +5,11 @@ Jinja2==3.1.3
5
5
  luddite==1.0.2
6
6
  MarkupSafe==2.1.5
7
7
  mergedeep==1.3.4
8
- packaging==23.2
8
+ packaging==24.0
9
9
  python-gitlab==4.4.0
10
10
  requests==2.31.0
11
11
  ruamel.yaml==0.17.21
12
- types-requests==2.31.0.20240125
12
+ types-requests==2.31.0.20240311
13
13
  yamlpath==3.8.2
14
14
 
15
15
  [docs]
@@ -17,13 +17,13 @@ mkdocs
17
17
  mkdocs-material
18
18
 
19
19
  [test]
20
- coverage==7.4.1
21
- cryptography==42.0.2
20
+ coverage==7.4.4
21
+ cryptography==42.0.5
22
22
  deepdiff==6.7.1
23
- mypy==1.8.0
23
+ mypy==1.9.0
24
24
  mypy-extensions==1.0.0
25
25
  pre-commit==2.21.0
26
- pytest==7.4.4
27
- pytest-cov==4.1.0
28
- pytest-rerunfailures==13.0
26
+ pytest==8.1.1
27
+ pytest-cov==5.0.0
28
+ pytest-rerunfailures==14.0
29
29
  xkcdpass==1.19.8
@@ -51,24 +51,24 @@ setup(
51
51
  "luddite==1.0.2",
52
52
  "MarkupSafe==2.1.5",
53
53
  "mergedeep==1.3.4",
54
- "packaging==23.2",
54
+ "packaging==24.0",
55
55
  "python-gitlab==4.4.0",
56
56
  "requests==2.31.0",
57
57
  "ruamel.yaml==0.17.21",
58
- "types-requests==2.31.0.20240125",
58
+ "types-requests==2.31.0.20240311",
59
59
  "yamlpath==3.8.2",
60
60
  ],
61
61
  extras_require={
62
62
  "test": [
63
- "coverage==7.4.1",
64
- "cryptography==42.0.2",
63
+ "coverage==7.4.4",
64
+ "cryptography==42.0.5",
65
65
  "deepdiff==6.7.1",
66
- "mypy==1.8.0",
66
+ "mypy==1.9.0",
67
67
  "mypy-extensions==1.0.0",
68
68
  "pre-commit==2.21.0", # not really for tests, but for development
69
- "pytest==7.4.4",
70
- "pytest-cov==4.1.0",
71
- "pytest-rerunfailures==13.0",
69
+ "pytest==8.1.1",
70
+ "pytest-cov==5.0.0",
71
+ "pytest-rerunfailures==14.0",
72
72
  "xkcdpass==1.19.8",
73
73
  ],
74
74
  "docs": [
@@ -99,6 +99,18 @@ def subgroup(gl: Gitlab, group: Group):
99
99
  gl.groups.delete(f"{group.full_path}/{subgroup_name}")
100
100
 
101
101
 
102
+ @pytest.fixture(scope="class")
103
+ def other_subgroup(gl: Gitlab, group: Group):
104
+ # TODO: deduplicate this - it's a copy and paste from the above fixture
105
+ subgroup_name = get_random_name("subgroup")
106
+ gitlab_subgroup = create_group(subgroup_name, group.id)
107
+
108
+ yield gitlab_subgroup
109
+
110
+ with allowed_codes(404):
111
+ gl.groups.delete(f"{group.full_path}/{subgroup_name}")
112
+
113
+
102
114
  @pytest.fixture(scope="class")
103
115
  def project(gl: Gitlab, group: Group):
104
116
  project_name = get_random_name("project")
@@ -119,6 +131,17 @@ def project_in_subgroup(gl: Gitlab, subgroup: Group):
119
131
  gitlab_project.delete()
120
132
 
121
133
 
134
+ @pytest.fixture(scope="class")
135
+ def project_in_other_subgroup(gl: Gitlab, other_subgroup: Group):
136
+ # TODO: deduplicate this - it's a copy and paste from the above fixture
137
+ project_name = get_random_name("project")
138
+ gitlab_project = create_project(other_subgroup, project_name)
139
+
140
+ yield gitlab_project
141
+
142
+ gitlab_project.delete()
143
+
144
+
122
145
  @pytest.fixture(scope="function")
123
146
  def project_for_function(gl: Gitlab, group: Group):
124
147
  project_name = get_random_name("project")