gitlab-projects-settings 6.1.1__tar.gz → 6.2.0__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 (79) hide show
  1. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.gitlab-ci.yml +1 -1
  2. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/CHANGELOG.md +22 -1
  3. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/LICENSE +1 -1
  4. {gitlab_projects_settings-6.1.1/gitlab_projects_settings.egg-info → gitlab_projects_settings-6.2.0}/PKG-INFO +4 -3
  5. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/README.md +1 -1
  6. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0/gitlab_projects_settings.egg-info}/PKG-INFO +4 -3
  7. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/mkdocs.yml +1 -1
  8. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/cli/entrypoint.py +46 -19
  9. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/cli/main.py +3 -2
  10. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/features/gitlab.py +19 -5
  11. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.coveragerc +0 -0
  12. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.cz.yaml +0 -0
  13. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.gitattributes +0 -0
  14. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.gitignore +0 -0
  15. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.markdownlint.json +0 -0
  16. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.pre-commit-config.yaml +0 -0
  17. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.style.yapf +0 -0
  18. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.vscode/extensions.json +0 -0
  19. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/.vscode/settings.json +0 -0
  20. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/config/cliff.toml +0 -0
  21. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/containers/build/Dockerfile +0 -0
  22. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/containers/codestyle/Dockerfile +0 -0
  23. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/containers/deploy/Dockerfile +0 -0
  24. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/containers/pages/Dockerfile +0 -0
  25. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/containers/rehost/images.txt +0 -0
  26. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/containers/test-coverage-linux/Dockerfile +0 -0
  27. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/containers/test-coverage-windows/Dockerfile +0 -0
  28. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/containers/test-linux/Dockerfile +0 -0
  29. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/docs/.pages.in +0 -0
  30. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/docs/assets/images/favicon.png +0 -0
  31. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/docs/assets/scripts/mkdocs-exporter.js +0 -0
  32. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/docs/assets/stylesheets/custom.css +0 -0
  33. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/docs/assets/stylesheets/pdf.scss +0 -0
  34. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/docs/prepare.sh +0 -0
  35. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/docs/robots.txt +0 -0
  36. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/docs/templates/covers/back.html.j2 +0 -0
  37. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/docs/templates/covers/front.html.j2 +0 -0
  38. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/gitlab_projects_settings.egg-info/SOURCES.txt +0 -0
  39. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/gitlab_projects_settings.egg-info/dependency_links.txt +0 -0
  40. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/gitlab_projects_settings.egg-info/entry_points.txt +0 -0
  41. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/gitlab_projects_settings.egg-info/requires.txt +0 -0
  42. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/gitlab_projects_settings.egg-info/top_level.txt +0 -0
  43. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/mypy.ini +0 -0
  44. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/requirements/build.txt +0 -0
  45. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/requirements/coverage.txt +0 -0
  46. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/requirements/deploy.txt +0 -0
  47. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/requirements/pages.txt +0 -0
  48. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/requirements/quality.txt +0 -0
  49. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/requirements/runtime.txt +0 -0
  50. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/requirements/tests.txt +0 -0
  51. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/setup.cfg +0 -0
  52. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/setup.py +0 -0
  53. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/sonar-project.properties +0 -0
  54. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/__init__.py +0 -0
  55. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/__main__.py +0 -0
  56. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/cli/__init__.py +0 -0
  57. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/features/__init__.py +0 -0
  58. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/package/__init__.py +0 -0
  59. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/package/bundle.py +0 -0
  60. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/package/settings.py +0 -0
  61. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/package/updates.py +0 -0
  62. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/package/version.py +0 -0
  63. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/prints/__init__.py +0 -0
  64. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/prints/boxes.py +0 -0
  65. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/prints/colors.py +0 -0
  66. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/prints/themes.py +0 -0
  67. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/system/__init__.py +0 -0
  68. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/system/platform.py +0 -0
  69. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/types/__init__.py +0 -0
  70. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/types/environments.py +0 -0
  71. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/types/gitlab.py +0 -0
  72. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/types/namespaces.py +0 -0
  73. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/src/types/strings.py +0 -0
  74. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/tests/arguments/test.sh +0 -0
  75. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/tests/colors/test.sh +0 -0
  76. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/tests/macos/test.sh +0 -0
  77. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/tests/settings/test.sh +0 -0
  78. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/tests/versions/test.sh +0 -0
  79. {gitlab_projects_settings-6.1.1 → gitlab_projects_settings-6.2.0}/tests/windows/test.sh +0 -0
@@ -1,5 +1,5 @@
1
1
  include:
2
- - component: gitlab.com/RadianDevCore/tools/pre-commit-crocodile/commits@4.0.0
2
+ - component: gitlab.com/RadianDevCore/tools/pre-commit-crocodile/commits@4.1.0
3
3
  inputs:
4
4
  stage: prepare
5
5
 
@@ -1,7 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ <a name="6.2.0"></a>
4
+ ## [6.2.0](https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings/compare/6.1.1...6.2.0) (2025-05-31)
5
+
6
+ ### ✨ Features
7
+
8
+ - **entrypoint, gitlab:** detect legacy missing 'squash_option' attribute ([260c433](https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings/commit/260c4332a3ddf006ed8806e8b74f74d45edfc51c))
9
+
10
+ ### 🐛 Bug Fixes
11
+
12
+ - **gitlab:** avoid exceptions upon group last owner member removal ([fed6ef0](https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings/commit/fed6ef00a2cf22a38cf58de70579c7b7fec739a0))
13
+ - **main:** exclusive '--run-housekeeping' and '--prune-unreachable-objects' ([4a0a170](https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings/commit/4a0a1707816a4404fdf14e58e370f960f961a7e8))
14
+
15
+ ### 📚 Documentation
16
+
17
+ - **license, mkdocs:** raise copyright year to '2025' ([671d6e8](https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings/commit/671d6e80a70cc519cdd302c704cbb01cb307f6b2))
18
+
19
+ ### 🚀 CI
20
+
21
+ - **gitlab-ci:** migrate to 'pre-commit-crocodile/commits@4.1.0' ([63ebbc5](https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings/commit/63ebbc536f8252ecb5c0066a2deca5518c58487b))
22
+
23
+
3
24
  <a name="6.1.1"></a>
4
- ## [6.1.1](https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings/compare/6.1.0...6.1.1) (2025-02-11)
25
+ ## [6.1.1](https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings/compare/6.1.0...6.1.1) (2025-02-28)
5
26
 
6
27
  ### ✨ Features
7
28
 
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2024 Adrian DC
189
+ Copyright 2024-2025 Adrian DC
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: gitlab-projects-settings
3
- Version: 6.1.1
3
+ Version: 6.2.0
4
4
  Summary: Configure GitLab groups and projects settings automatically
5
5
  Home-page: https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings
6
6
  Author: Adrian DC
@@ -41,6 +41,7 @@ Dynamic: description-content-type
41
41
  Dynamic: home-page
42
42
  Dynamic: keywords
43
43
  Dynamic: license
44
+ Dynamic: license-file
44
45
  Dynamic: project-url
45
46
  Dynamic: requires-dist
46
47
  Dynamic: requires-python
@@ -122,7 +123,7 @@ usage: gitlab-projects-settings [-h] [--version] [--no-color] [--update-check] [
122
123
  [--disable-features FEATURES] [--enable-features FEATURES] [--reset-members]
123
124
  [--set-avatar FILE] [--set-description TEXT] [--update-descriptions]
124
125
  [--set-roles-create-projects [ROLE]] [--set-roles-create-subgroups [ROLE]]
125
- [--run-housekeeping] [--prune-unreachable-objects]
126
+ [--run-housekeeping | --prune-unreachable-objects]
126
127
  [--archive-projects | --unarchive-projects] [--delete-groups] [--delete-projects]
127
128
  [--protect-branches] [--protect-tags [LEVEL]] [--set-merge-method [METHOD]]
128
129
  [--set-merge-squash [SQUASH]] [--set-merge-pipelines [CHECK]]
@@ -74,7 +74,7 @@ usage: gitlab-projects-settings [-h] [--version] [--no-color] [--update-check] [
74
74
  [--disable-features FEATURES] [--enable-features FEATURES] [--reset-members]
75
75
  [--set-avatar FILE] [--set-description TEXT] [--update-descriptions]
76
76
  [--set-roles-create-projects [ROLE]] [--set-roles-create-subgroups [ROLE]]
77
- [--run-housekeeping] [--prune-unreachable-objects]
77
+ [--run-housekeeping | --prune-unreachable-objects]
78
78
  [--archive-projects | --unarchive-projects] [--delete-groups] [--delete-projects]
79
79
  [--protect-branches] [--protect-tags [LEVEL]] [--set-merge-method [METHOD]]
80
80
  [--set-merge-squash [SQUASH]] [--set-merge-pipelines [CHECK]]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: gitlab-projects-settings
3
- Version: 6.1.1
3
+ Version: 6.2.0
4
4
  Summary: Configure GitLab groups and projects settings automatically
5
5
  Home-page: https://gitlab.com/RadianDevCore/tools/gitlab-projects-settings
6
6
  Author: Adrian DC
@@ -41,6 +41,7 @@ Dynamic: description-content-type
41
41
  Dynamic: home-page
42
42
  Dynamic: keywords
43
43
  Dynamic: license
44
+ Dynamic: license-file
44
45
  Dynamic: project-url
45
46
  Dynamic: requires-dist
46
47
  Dynamic: requires-python
@@ -122,7 +123,7 @@ usage: gitlab-projects-settings [-h] [--version] [--no-color] [--update-check] [
122
123
  [--disable-features FEATURES] [--enable-features FEATURES] [--reset-members]
123
124
  [--set-avatar FILE] [--set-description TEXT] [--update-descriptions]
124
125
  [--set-roles-create-projects [ROLE]] [--set-roles-create-subgroups [ROLE]]
125
- [--run-housekeeping] [--prune-unreachable-objects]
126
+ [--run-housekeeping | --prune-unreachable-objects]
126
127
  [--archive-projects | --unarchive-projects] [--delete-groups] [--delete-projects]
127
128
  [--protect-branches] [--protect-tags [LEVEL]] [--set-merge-method [METHOD]]
128
129
  [--set-merge-squash [SQUASH]] [--set-merge-pipelines [CHECK]]
@@ -19,7 +19,7 @@ site_name: gitlab-projects-settings
19
19
  site_url: https://radiandevcore.gitlab.io/tools/gitlab-projects-settings
20
20
 
21
21
  # Project copyright
22
- copyright: Copyright &copy; 2024 - <a href="https://gitlab.com/AdrianDC">Adrian DC</a>
22
+ copyright: Copyright &copy; 2024-2025 - <a href="https://gitlab.com/AdrianDC">Adrian DC</a>
23
23
 
24
24
  # Excluded sources
25
25
  exclude_docs: |
@@ -467,7 +467,7 @@ class Entrypoint:
467
467
  ) -> Result:
468
468
 
469
469
  # Variables
470
- changed: bool
470
+ changed: Optional[bool]
471
471
 
472
472
  # Acquire project
473
473
  project = gitlab.project(criteria)
@@ -690,9 +690,15 @@ class Entrypoint:
690
690
  MergeRequestsMethod.ATTRIBUTE,
691
691
  MergeRequestsMethod.VALUES[options.set_merge_method],
692
692
  )
693
- print(f'{Colors.BOLD} - Set merge method: '
694
- f'{Colors.CYAN if changed else Colors.GREEN}{options.set_merge_method}'
695
- f'{Colors.RESET}')
693
+ if changed is None:
694
+ print(f'{Colors.BOLD} - Set merge method: '
695
+ f'{Colors.RED}Unknown feature'
696
+ f'{Colors.RESET}')
697
+ else:
698
+ print(f'{Colors.BOLD} - Set merge method: '
699
+ f'{Colors.CYAN if changed else Colors.GREEN}'
700
+ f'{options.set_merge_method}'
701
+ f'{Colors.RESET}')
696
702
  Platform.flush()
697
703
 
698
704
  # Set project merge squash
@@ -702,9 +708,15 @@ class Entrypoint:
702
708
  MergeRequestsSquash.ATTRIBUTE,
703
709
  MergeRequestsSquash.VALUES[options.set_merge_squash],
704
710
  )
705
- print(f'{Colors.BOLD} - Set merge squash: '
706
- f'{Colors.CYAN if changed else Colors.GREEN}{options.set_merge_squash}'
707
- f'{Colors.RESET}')
711
+ if changed is None:
712
+ print(f'{Colors.BOLD} - Set merge squash: '
713
+ f'{Colors.RED}Unknown feature'
714
+ f'{Colors.RESET}')
715
+ else:
716
+ print(f'{Colors.BOLD} - Set merge squash: '
717
+ f'{Colors.CYAN if changed else Colors.GREEN}'
718
+ f'{options.set_merge_squash}'
719
+ f'{Colors.RESET}')
708
720
  Platform.flush()
709
721
 
710
722
  # Set project merge pipelines
@@ -714,10 +726,15 @@ class Entrypoint:
714
726
  MergeRequestsPipelines.ATTRIBUTE,
715
727
  options.set_merge_pipelines,
716
728
  )
717
- print(f'{Colors.BOLD} - Set merge pipelines: '
718
- f'{Colors.CYAN if changed else Colors.GREEN}'
719
- f'{str(options.set_merge_pipelines).lower()}'
720
- f'{Colors.RESET}')
729
+ if changed is None:
730
+ print(f'{Colors.BOLD} - Set merge pipelines: '
731
+ f'{Colors.RED}Unknown feature'
732
+ f'{Colors.RESET}')
733
+ else:
734
+ print(f'{Colors.BOLD} - Set merge pipelines: '
735
+ f'{Colors.CYAN if changed else Colors.GREEN}'
736
+ f'{str(options.set_merge_pipelines).lower()}'
737
+ f'{Colors.RESET}')
721
738
  Platform.flush()
722
739
 
723
740
  # Set project merge skipped
@@ -727,10 +744,15 @@ class Entrypoint:
727
744
  MergeRequestsSkipped.ATTRIBUTE,
728
745
  options.set_merge_skipped,
729
746
  )
730
- print(f'{Colors.BOLD} - Set merge skipped: '
731
- f'{Colors.CYAN if changed else Colors.GREEN}'
732
- f'{str(options.set_merge_skipped).lower()}'
733
- f'{Colors.RESET}')
747
+ if changed is None:
748
+ print(f'{Colors.BOLD} - Set merge skipped: '
749
+ f'{Colors.RED}Unknown feature'
750
+ f'{Colors.RESET}')
751
+ else:
752
+ print(f'{Colors.BOLD} - Set merge skipped: '
753
+ f'{Colors.CYAN if changed else Colors.GREEN}'
754
+ f'{str(options.set_merge_skipped).lower()}'
755
+ f'{Colors.RESET}')
734
756
  Platform.flush()
735
757
 
736
758
  # Set project merge resolved
@@ -740,10 +762,15 @@ class Entrypoint:
740
762
  MergeRequestsResolved.ATTRIBUTE,
741
763
  options.set_merge_resolved,
742
764
  )
743
- print(f'{Colors.BOLD} - Set merge method: '
744
- f'{Colors.CYAN if changed else Colors.GREEN}'
745
- f'{str(options.set_merge_resolved).lower()}'
746
- f'{Colors.RESET}')
765
+ if changed is None:
766
+ print(f'{Colors.BOLD} - Set merge method: '
767
+ f'{Colors.RED}Unknown feature'
768
+ f'{Colors.RESET}')
769
+ else:
770
+ print(f'{Colors.BOLD} - Set merge method: '
771
+ f'{Colors.CYAN if changed else Colors.GREEN}'
772
+ f'{str(options.set_merge_resolved).lower()}'
773
+ f'{Colors.RESET}')
747
774
  Platform.flush()
748
775
 
749
776
  # Add path to CI/CD job token allowlist
@@ -257,13 +257,14 @@ def main() -> None:
257
257
 
258
258
  # Arguments advanced settings definitions
259
259
  group = parser.add_argument_group('advanced settings arguments')
260
- group.add_argument(
260
+ subgroup = group.add_mutually_exclusive_group()
261
+ subgroup.add_argument(
261
262
  '--run-housekeeping',
262
263
  dest='run_housekeeping',
263
264
  action='store_true',
264
265
  help='Run housekeeping of GitLab project or projects in groups',
265
266
  )
266
- group.add_argument(
267
+ subgroup.add_argument(
267
268
  '--prune-unreachable-objects',
268
269
  dest='prune_unreachable_objects',
269
270
  action='store_true',
@@ -2,11 +2,12 @@
2
2
 
3
3
  # Standard libraries
4
4
  from time import sleep
5
- from typing import cast, Dict, List, Union
5
+ from typing import cast, Dict, List, Optional, Union
6
6
 
7
7
  # Modules libraries
8
8
  from gitlab import Gitlab
9
9
  from gitlab.base import RESTObject, RESTObjectList
10
+ from gitlab.const import AccessLevel as GitLabAccessLevel
10
11
  from gitlab.exceptions import (
11
12
  GitlabDeleteError,
12
13
  GitlabGetError,
@@ -139,9 +140,18 @@ class GitLabFeature:
139
140
 
140
141
  # Remove group members
141
142
  group = self.group(criteria)
142
- for member in group.members.list(get_all=True):
143
+ for member in sorted(
144
+ group.members.list(get_all=True),
145
+ key=lambda item: item.access_level,
146
+ ):
143
147
  if not self.__dry_run:
144
- group.members.delete(member.id)
148
+ try:
149
+ group.members.delete(member.id)
150
+ except GitlabDeleteError as exception:
151
+ if member.access_level == GitLabAccessLevel.OWNER \
152
+ and len(group.members.list(get_all=True)) == 1:
153
+ continue
154
+ raise exception
145
155
 
146
156
  # Save group
147
157
  if not self.__dry_run:
@@ -792,13 +802,17 @@ class GitLabFeature:
792
802
  criteria: str,
793
803
  name: str,
794
804
  value: Union[bool, str],
795
- ) -> bool:
805
+ ) -> Optional[bool]:
796
806
 
797
807
  # Variables
798
808
  changed: bool = False
799
809
 
800
- # Set project attribute
810
+ # Check project attribute
801
811
  project = self.project(criteria)
812
+ if not self.__dry_run and not hasattr(project, name):
813
+ return None
814
+
815
+ # Set project attribute
802
816
  if not self.__dry_run and getattr(project, name) != value:
803
817
  setattr(project, name, value)
804
818
  changed = True