gitlab-projects-settings 2.1.0__tar.gz → 3.0.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.
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/.gitlab-ci.yml +37 -37
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/CHANGELOG.md +30 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/PKG-INFO +12 -4
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/README.md +11 -3
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/gitlab_projects_settings.egg-info/PKG-INFO +12 -4
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/gitlab_projects_settings.egg-info/SOURCES.txt +3 -1
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/cli/entrypoint.py +160 -49
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/cli/main.py +38 -2
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/features/gitlab.py +133 -37
- gitlab_projects_settings-3.0.0/src/types/__init__.py +0 -0
- gitlab_projects_settings-3.0.0/src/types/namespaces.py +33 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/.chglog/CHANGELOG.tpl.md +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/.chglog/changelog.sh +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/.chglog/config.yml +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/.gitignore +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/.markdownlint.json +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/.style.yapf +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/.vscode/extensions.json +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/.vscode/settings.json +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/LICENSE +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/gitlab_projects_settings.egg-info/dependency_links.txt +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/gitlab_projects_settings.egg-info/entry_points.txt +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/gitlab_projects_settings.egg-info/requires.txt +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/gitlab_projects_settings.egg-info/top_level.txt +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/mypy.ini +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/requirements/build.txt +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/requirements/deploy.txt +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/requirements/quality.txt +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/requirements/runtime.txt +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/setup.cfg +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/setup.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/__init__.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/__main__.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/cli/__init__.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/features/__init__.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/package/__init__.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/package/bundle.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/package/version.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/prints/__init__.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/prints/colors.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/system/__init__.py +0 -0
- {gitlab_projects_settings-2.1.0 → gitlab_projects_settings-3.0.0}/src/system/platform.py +0 -0
|
@@ -53,43 +53,6 @@ changelog:
|
|
|
53
53
|
rules:
|
|
54
54
|
- if: $CI_LOCAL
|
|
55
55
|
|
|
56
|
-
readme:
|
|
57
|
-
stage: development
|
|
58
|
-
image: local:silent
|
|
59
|
-
script:
|
|
60
|
-
- |
|
|
61
|
-
export COLUMNS=120
|
|
62
|
-
mkdir -p ./.tmp/
|
|
63
|
-
PACKAGE_NAME=$(grep '^PROJECT_NAME =' ./setup.py | cut -d"'" -f2)
|
|
64
|
-
{
|
|
65
|
-
echo ''
|
|
66
|
-
echo '```yaml'
|
|
67
|
-
alignments='[ ]\{2,\}'
|
|
68
|
-
argument='[[:alnum:]][[:alnum:]_-]\{0,\}'
|
|
69
|
-
letter='[[:alnum:]_]\{1\}'
|
|
70
|
-
parameter='[-]\{1,2\}[[:alnum:]][[:alnum:]_-]\{0,\}'
|
|
71
|
-
word='[[:alnum:]_-]\{1,\}'
|
|
72
|
-
"${PACKAGE_NAME}" --help \
|
|
73
|
-
| sed '1d; $d' \
|
|
74
|
-
| sed "s/^\( ${parameter} ${word}${alignments}\)/\1# /g" \
|
|
75
|
-
| sed "s/^\( ${parameter}${alignments}\)/\1# /g" \
|
|
76
|
-
| sed "s/^\( ${parameter}, ${parameter}${alignments}\)/\1# /g" \
|
|
77
|
-
| sed "s/^\( ${argument}${alignments}\)/\1# /g"
|
|
78
|
-
echo '```'
|
|
79
|
-
echo ''
|
|
80
|
-
} >./.tmp/README.help.tmp
|
|
81
|
-
- |
|
|
82
|
-
lead='^<!-- readme-help-start -->$'
|
|
83
|
-
tail='^<!-- readme-help-stop -->$'
|
|
84
|
-
sed -i "/${lead}/,/${tail}/{ /${lead}/{ p; r ./.tmp/README.help.tmp
|
|
85
|
-
}; /${tail}/p; d }" ./README.md | head -n 100
|
|
86
|
-
- rm -f ./.tmp/README.help.tmp
|
|
87
|
-
- |
|
|
88
|
-
git -c color.diff=always --no-pager diff ./README.md | grep --color=never '.' && echo ''
|
|
89
|
-
echo ' > README: Updated successfully'
|
|
90
|
-
rules:
|
|
91
|
-
- if: $CI_LOCAL
|
|
92
|
-
|
|
93
56
|
# =============================================================================
|
|
94
57
|
# Stage: prepare
|
|
95
58
|
|
|
@@ -192,6 +155,43 @@ install:
|
|
|
192
155
|
rules:
|
|
193
156
|
- if: $CI_LOCAL
|
|
194
157
|
|
|
158
|
+
readme:
|
|
159
|
+
stage: build
|
|
160
|
+
image: local:silent
|
|
161
|
+
script:
|
|
162
|
+
- |
|
|
163
|
+
export COLUMNS=120
|
|
164
|
+
mkdir -p ./.tmp/
|
|
165
|
+
PACKAGE_NAME=$(grep '^PROJECT_NAME =' ./setup.py | cut -d"'" -f2)
|
|
166
|
+
{
|
|
167
|
+
echo ''
|
|
168
|
+
echo '```yaml'
|
|
169
|
+
alignments='[ ]\{2,\}'
|
|
170
|
+
argument='[[:alnum:]][[:alnum:]_-]\{0,\}'
|
|
171
|
+
letter='[[:alnum:]_]\{1\}'
|
|
172
|
+
parameter='[-]\{1,2\}[[:alnum:]][[:alnum:]_-]\{0,\}'
|
|
173
|
+
word='[[:alnum:]_-]\{1,\}'
|
|
174
|
+
"${PACKAGE_NAME}" --help \
|
|
175
|
+
| sed '1d; $d' \
|
|
176
|
+
| sed "s/^\( ${parameter} ${word}${alignments}\)/\1# /g" \
|
|
177
|
+
| sed "s/^\( ${parameter}${alignments}\)/\1# /g" \
|
|
178
|
+
| sed "s/^\( ${parameter}, ${parameter}${alignments}\)/\1# /g" \
|
|
179
|
+
| sed "s/^\( ${argument}${alignments}\)/\1# /g"
|
|
180
|
+
echo '```'
|
|
181
|
+
echo ''
|
|
182
|
+
} >./.tmp/README.help.tmp
|
|
183
|
+
- |
|
|
184
|
+
lead='^<!-- readme-help-start -->$'
|
|
185
|
+
tail='^<!-- readme-help-stop -->$'
|
|
186
|
+
sed -i "/${lead}/,/${tail}/{ /${lead}/{ p; r ./.tmp/README.help.tmp
|
|
187
|
+
}; /${tail}/p; d }" ./README.md | head -n 100
|
|
188
|
+
- rm -f ./.tmp/README.help.tmp
|
|
189
|
+
- |
|
|
190
|
+
git -c color.diff=always --no-pager diff ./README.md | grep --color=never '.' && echo ''
|
|
191
|
+
echo ' > README: Updated successfully'
|
|
192
|
+
rules:
|
|
193
|
+
- if: $CI_LOCAL
|
|
194
|
+
|
|
195
195
|
# =============================================================================
|
|
196
196
|
# Stage: deploy
|
|
197
197
|
|
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
|
|
2
|
+
<a name="3.0.0"></a>
|
|
3
|
+
## [3.0.0](https://gitlab.com/AdrianDC/gitlab-projects-settings/compare/2.1.0...3.0.0) (2024-05-06)
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
* **entrypoint:** resolve support for private user namespaces
|
|
8
|
+
* **entrypoint:** detect if GitLab actions can continue
|
|
9
|
+
* **entrypoint:** enforce against missing '.description' values
|
|
10
|
+
* **gitlab:** enforce 'group_delete' usage in '--dry-run' mode
|
|
11
|
+
* **gitlab:** delay groups deletion by 10s and projects by 5s
|
|
12
|
+
* **gitlab:** get all branches and tags upon 'list()' calls
|
|
13
|
+
* **gitlab:** get all members in 'project_reset_members'
|
|
14
|
+
|
|
15
|
+
### Ci
|
|
16
|
+
|
|
17
|
+
* **gitlab-ci:** move 'readme' job after 'build' and local 'install'
|
|
18
|
+
|
|
19
|
+
### Cleanups
|
|
20
|
+
|
|
21
|
+
* **gitlab:** minor comments changes in 'project_reset_features'
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* **entrypoint:** preserve main group description if exists
|
|
26
|
+
* **entrypoint:** always flush progress output logs
|
|
27
|
+
* **gitlab:** detect multiple branches to keep 'Merge requests'
|
|
28
|
+
* **gitlab:** detect 'Token Access' usage for 'CI/CD' features
|
|
29
|
+
* **namespaces:** migrate 'Helper' class to 'Namespaces' class
|
|
30
|
+
|
|
31
|
+
|
|
2
32
|
<a name="2.1.0"></a>
|
|
3
33
|
## [2.1.0](https://gitlab.com/AdrianDC/gitlab-projects-settings/compare/2.0.2...2.1.0) (2024-04-28)
|
|
4
34
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: gitlab-projects-settings
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Configure GitLab groups and projects settings automatically
|
|
5
5
|
Home-page: https://gitlab.com/AdrianDC/gitlab-projects-settings
|
|
6
6
|
Author: Adrian DC
|
|
@@ -64,8 +64,9 @@ The following step is required before using the tool:
|
|
|
64
64
|
```yaml
|
|
65
65
|
usage: gitlab-projects-settings [-h] [--version] [-t TOKEN] [--dry-run] [--exclude-group] [--exclude-subgroups]
|
|
66
66
|
[--exclude-projects] [--reset-features] [--reset-members] [--set-avatar FILE]
|
|
67
|
-
[--set-description TEXT] [--update-description] [--
|
|
68
|
-
[--
|
|
67
|
+
[--set-description TEXT] [--update-description] [--run-housekeeping]
|
|
68
|
+
[--archive-project | --unarchive-project] [--delete-group] [--delete-project]
|
|
69
|
+
[--protect-branches] [--protect-tags LEVEL]
|
|
69
70
|
[gitlab] [path]
|
|
70
71
|
|
|
71
72
|
gitlab-projects-settings: Configure GitLab groups and projects settings automatically
|
|
@@ -90,13 +91,20 @@ general settings arguments:
|
|
|
90
91
|
--set-description TEXT # Set description of GitLab projects and groups
|
|
91
92
|
--update-description # Update description of GitLab projects and groups automatically
|
|
92
93
|
|
|
94
|
+
advanced settings arguments:
|
|
95
|
+
--run-housekeeping # Run housekeeping of project or projects GitLab in groups
|
|
96
|
+
--archive-project # Archive project or projects in GitLab groups
|
|
97
|
+
--unarchive-project # Unarchive project or projects in GitLab groups
|
|
98
|
+
--delete-group # Delete group or groups in GitLab groups
|
|
99
|
+
--delete-project # Delete project or projects in GitLab groups
|
|
100
|
+
|
|
93
101
|
repository settings arguments:
|
|
94
102
|
--protect-branches # Protect branches with default master/main, develop and staging
|
|
95
103
|
--protect-tags LEVEL # Protect tags at level [no-one,admins,maintainers,developers]
|
|
96
104
|
|
|
97
105
|
positional arguments:
|
|
98
106
|
gitlab # GitLab URL (default: https://gitlab.com)
|
|
99
|
-
path # GitLab group or project path
|
|
107
|
+
path # GitLab group, user namespace or project path
|
|
100
108
|
```
|
|
101
109
|
|
|
102
110
|
<!-- readme-help-stop -->
|
|
@@ -30,8 +30,9 @@ The following step is required before using the tool:
|
|
|
30
30
|
```yaml
|
|
31
31
|
usage: gitlab-projects-settings [-h] [--version] [-t TOKEN] [--dry-run] [--exclude-group] [--exclude-subgroups]
|
|
32
32
|
[--exclude-projects] [--reset-features] [--reset-members] [--set-avatar FILE]
|
|
33
|
-
[--set-description TEXT] [--update-description] [--
|
|
34
|
-
[--
|
|
33
|
+
[--set-description TEXT] [--update-description] [--run-housekeeping]
|
|
34
|
+
[--archive-project | --unarchive-project] [--delete-group] [--delete-project]
|
|
35
|
+
[--protect-branches] [--protect-tags LEVEL]
|
|
35
36
|
[gitlab] [path]
|
|
36
37
|
|
|
37
38
|
gitlab-projects-settings: Configure GitLab groups and projects settings automatically
|
|
@@ -56,13 +57,20 @@ general settings arguments:
|
|
|
56
57
|
--set-description TEXT # Set description of GitLab projects and groups
|
|
57
58
|
--update-description # Update description of GitLab projects and groups automatically
|
|
58
59
|
|
|
60
|
+
advanced settings arguments:
|
|
61
|
+
--run-housekeeping # Run housekeeping of project or projects GitLab in groups
|
|
62
|
+
--archive-project # Archive project or projects in GitLab groups
|
|
63
|
+
--unarchive-project # Unarchive project or projects in GitLab groups
|
|
64
|
+
--delete-group # Delete group or groups in GitLab groups
|
|
65
|
+
--delete-project # Delete project or projects in GitLab groups
|
|
66
|
+
|
|
59
67
|
repository settings arguments:
|
|
60
68
|
--protect-branches # Protect branches with default master/main, develop and staging
|
|
61
69
|
--protect-tags LEVEL # Protect tags at level [no-one,admins,maintainers,developers]
|
|
62
70
|
|
|
63
71
|
positional arguments:
|
|
64
72
|
gitlab # GitLab URL (default: https://gitlab.com)
|
|
65
|
-
path # GitLab group or project path
|
|
73
|
+
path # GitLab group, user namespace or project path
|
|
66
74
|
```
|
|
67
75
|
|
|
68
76
|
<!-- readme-help-stop -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: gitlab-projects-settings
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Configure GitLab groups and projects settings automatically
|
|
5
5
|
Home-page: https://gitlab.com/AdrianDC/gitlab-projects-settings
|
|
6
6
|
Author: Adrian DC
|
|
@@ -64,8 +64,9 @@ The following step is required before using the tool:
|
|
|
64
64
|
```yaml
|
|
65
65
|
usage: gitlab-projects-settings [-h] [--version] [-t TOKEN] [--dry-run] [--exclude-group] [--exclude-subgroups]
|
|
66
66
|
[--exclude-projects] [--reset-features] [--reset-members] [--set-avatar FILE]
|
|
67
|
-
[--set-description TEXT] [--update-description] [--
|
|
68
|
-
[--
|
|
67
|
+
[--set-description TEXT] [--update-description] [--run-housekeeping]
|
|
68
|
+
[--archive-project | --unarchive-project] [--delete-group] [--delete-project]
|
|
69
|
+
[--protect-branches] [--protect-tags LEVEL]
|
|
69
70
|
[gitlab] [path]
|
|
70
71
|
|
|
71
72
|
gitlab-projects-settings: Configure GitLab groups and projects settings automatically
|
|
@@ -90,13 +91,20 @@ general settings arguments:
|
|
|
90
91
|
--set-description TEXT # Set description of GitLab projects and groups
|
|
91
92
|
--update-description # Update description of GitLab projects and groups automatically
|
|
92
93
|
|
|
94
|
+
advanced settings arguments:
|
|
95
|
+
--run-housekeeping # Run housekeeping of project or projects GitLab in groups
|
|
96
|
+
--archive-project # Archive project or projects in GitLab groups
|
|
97
|
+
--unarchive-project # Unarchive project or projects in GitLab groups
|
|
98
|
+
--delete-group # Delete group or groups in GitLab groups
|
|
99
|
+
--delete-project # Delete project or projects in GitLab groups
|
|
100
|
+
|
|
93
101
|
repository settings arguments:
|
|
94
102
|
--protect-branches # Protect branches with default master/main, develop and staging
|
|
95
103
|
--protect-tags LEVEL # Protect tags at level [no-one,admins,maintainers,developers]
|
|
96
104
|
|
|
97
105
|
positional arguments:
|
|
98
106
|
gitlab # GitLab URL (default: https://gitlab.com)
|
|
99
|
-
path # GitLab group or project path
|
|
107
|
+
path # GitLab group, user namespace or project path
|
|
100
108
|
```
|
|
101
109
|
|
|
102
110
|
<!-- readme-help-stop -->
|
|
@@ -5,23 +5,25 @@ from argparse import Namespace
|
|
|
5
5
|
|
|
6
6
|
# Modules libraries
|
|
7
7
|
from gitlab.exceptions import GitlabGetError
|
|
8
|
-
from gitlab.v4.objects import Group, Project
|
|
8
|
+
from gitlab.v4.objects import Group as GitLabGroup, Project as GitLabProject, User as GitLabUser
|
|
9
9
|
|
|
10
10
|
# Components
|
|
11
11
|
from ..features.gitlab import GitLabFeature
|
|
12
12
|
from ..prints.colors import Colors
|
|
13
13
|
from ..system.platform import Platform
|
|
14
|
+
from ..types.namespaces import Namespaces
|
|
14
15
|
|
|
15
16
|
# Entrypoint class, pylint: disable=too-few-public-methods
|
|
16
17
|
class Entrypoint:
|
|
17
18
|
|
|
18
|
-
# CLI
|
|
19
|
+
# CLI, pylint: disable=too-many-branches
|
|
19
20
|
@staticmethod
|
|
20
21
|
def cli(options: Namespace) -> bool:
|
|
21
22
|
|
|
22
23
|
# Variables
|
|
23
|
-
group:
|
|
24
|
-
project:
|
|
24
|
+
group: GitLabGroup = None
|
|
25
|
+
project: GitLabProject = None
|
|
26
|
+
user: GitLabUser = None
|
|
25
27
|
|
|
26
28
|
# Header
|
|
27
29
|
print(' ')
|
|
@@ -46,14 +48,26 @@ class Entrypoint:
|
|
|
46
48
|
f'{Colors.RESET}')
|
|
47
49
|
print(' ')
|
|
48
50
|
Platform.flush()
|
|
49
|
-
except GitlabGetError:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
except GitlabGetError as exception:
|
|
52
|
+
try:
|
|
53
|
+
if '/' in options.path:
|
|
54
|
+
raise TypeError from exception
|
|
55
|
+
user = gitlab.user(options.path)
|
|
56
|
+
namespace = gitlab.namespace(user.namespace_id)
|
|
57
|
+
print(f'{Colors.BOLD} - GitLab user namespace: '
|
|
58
|
+
f'{Colors.GREEN}{namespace.full_path}'
|
|
59
|
+
f'{Colors.CYAN} ({namespace.name})'
|
|
60
|
+
f'{Colors.RESET}')
|
|
61
|
+
print(' ')
|
|
62
|
+
Platform.flush()
|
|
63
|
+
except (GitlabGetError, TypeError):
|
|
64
|
+
project = gitlab.project(options.path)
|
|
65
|
+
print(f'{Colors.BOLD} - GitLab project: '
|
|
66
|
+
f'{Colors.GREEN}{project.path_with_namespace}'
|
|
67
|
+
f'{Colors.CYAN} ({project.description})'
|
|
68
|
+
f'{Colors.RESET}')
|
|
69
|
+
print(' ')
|
|
70
|
+
Platform.flush()
|
|
57
71
|
|
|
58
72
|
# Handle single project
|
|
59
73
|
if project:
|
|
@@ -68,11 +82,12 @@ class Entrypoint:
|
|
|
68
82
|
|
|
69
83
|
# Handle group
|
|
70
84
|
if not options.exclude_group:
|
|
71
|
-
Entrypoint.group(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
85
|
+
if not Entrypoint.group(
|
|
86
|
+
options,
|
|
87
|
+
gitlab,
|
|
88
|
+
group.id,
|
|
89
|
+
):
|
|
90
|
+
return False
|
|
76
91
|
|
|
77
92
|
# Iterate through subgroups
|
|
78
93
|
if not options.exclude_subgroups:
|
|
@@ -85,12 +100,13 @@ class Entrypoint:
|
|
|
85
100
|
),
|
|
86
101
|
key=lambda item: item.full_path,
|
|
87
102
|
):
|
|
88
|
-
Entrypoint.group(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
103
|
+
if not Entrypoint.group(
|
|
104
|
+
options,
|
|
105
|
+
gitlab,
|
|
106
|
+
group_subgroup.id,
|
|
107
|
+
True,
|
|
108
|
+
):
|
|
109
|
+
return False
|
|
94
110
|
|
|
95
111
|
# Iterate through projects
|
|
96
112
|
if not options.exclude_projects:
|
|
@@ -103,11 +119,32 @@ class Entrypoint:
|
|
|
103
119
|
),
|
|
104
120
|
key=lambda item: item.path_with_namespace,
|
|
105
121
|
):
|
|
106
|
-
Entrypoint.project(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
)
|
|
122
|
+
if not Entrypoint.project(
|
|
123
|
+
options,
|
|
124
|
+
gitlab,
|
|
125
|
+
group_project.id,
|
|
126
|
+
):
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
# Handle user recursively
|
|
130
|
+
elif user:
|
|
131
|
+
|
|
132
|
+
# Iterate through projects
|
|
133
|
+
if not options.exclude_projects:
|
|
134
|
+
for user_project in sorted(
|
|
135
|
+
user.projects.list(
|
|
136
|
+
get_all=True,
|
|
137
|
+
order_by='path',
|
|
138
|
+
sort='asc',
|
|
139
|
+
),
|
|
140
|
+
key=lambda item: item.path_with_namespace,
|
|
141
|
+
):
|
|
142
|
+
if not Entrypoint.project(
|
|
143
|
+
options,
|
|
144
|
+
gitlab,
|
|
145
|
+
user_project.id,
|
|
146
|
+
):
|
|
147
|
+
return False
|
|
111
148
|
|
|
112
149
|
# Result
|
|
113
150
|
return True
|
|
@@ -119,13 +156,13 @@ class Entrypoint:
|
|
|
119
156
|
gitlab: GitLabFeature,
|
|
120
157
|
criteria: str,
|
|
121
158
|
subgroup: bool = False,
|
|
122
|
-
) ->
|
|
159
|
+
) -> bool:
|
|
123
160
|
|
|
124
161
|
# Acquire group
|
|
125
162
|
group = gitlab.group(criteria)
|
|
126
163
|
|
|
127
164
|
# Acquire parent group
|
|
128
|
-
parent_group:
|
|
165
|
+
parent_group: GitLabGroup = None
|
|
129
166
|
if group.parent_id:
|
|
130
167
|
parent_group = gitlab.group(group.parent_id)
|
|
131
168
|
|
|
@@ -142,6 +179,17 @@ class Entrypoint:
|
|
|
142
179
|
f'{Colors.YELLOW_LIGHT}{group.full_path} '
|
|
143
180
|
f'{Colors.CYAN}({group.description})'
|
|
144
181
|
f'{Colors.RESET}')
|
|
182
|
+
Platform.flush()
|
|
183
|
+
|
|
184
|
+
# Delete group
|
|
185
|
+
if options.delete_group:
|
|
186
|
+
gitlab.group_delete(criteria)
|
|
187
|
+
print(f'{Colors.BOLD} - Delete {group_type}: '
|
|
188
|
+
f'{Colors.GREEN}Success'
|
|
189
|
+
f'{Colors.RESET}')
|
|
190
|
+
print(' ')
|
|
191
|
+
Platform.flush()
|
|
192
|
+
return subgroup
|
|
145
193
|
|
|
146
194
|
# Set group description
|
|
147
195
|
if options.set_description:
|
|
@@ -149,27 +197,32 @@ class Entrypoint:
|
|
|
149
197
|
print(f'{Colors.BOLD} - Set description: '
|
|
150
198
|
f'{Colors.CYAN}{options.set_description}'
|
|
151
199
|
f'{Colors.RESET}')
|
|
200
|
+
Platform.flush()
|
|
152
201
|
|
|
153
202
|
# Update group description
|
|
154
203
|
elif options.update_description:
|
|
155
204
|
parent_description_text: str = ''
|
|
156
205
|
if parent_name:
|
|
157
|
-
parent_description_text = ' - ' +
|
|
158
|
-
|
|
159
|
-
|
|
206
|
+
parent_description_text = ' - ' + Namespaces.describe(
|
|
207
|
+
name=parent_name,
|
|
208
|
+
description=parent_description,
|
|
160
209
|
)
|
|
161
|
-
if not
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
210
|
+
if not group.description or subgroup and ( \
|
|
211
|
+
not parent_description_text or \
|
|
212
|
+
not group.description.endswith(f'{parent_description_text}') \
|
|
213
|
+
):
|
|
214
|
+
description = f'{Namespaces.describe(name=group.name)}' \
|
|
215
|
+
f'{parent_description_text}'
|
|
165
216
|
gitlab.group_set_description(criteria, description)
|
|
166
217
|
print(f'{Colors.BOLD} - Updated description: '
|
|
167
218
|
f'{Colors.CYAN}{description}'
|
|
168
219
|
f'{Colors.RESET}')
|
|
220
|
+
Platform.flush()
|
|
169
221
|
else:
|
|
170
222
|
print(f'{Colors.BOLD} - Kept description: '
|
|
171
223
|
f'{Colors.GREEN}{group.description}'
|
|
172
224
|
f'{Colors.RESET}')
|
|
225
|
+
Platform.flush()
|
|
173
226
|
|
|
174
227
|
# Reset group members
|
|
175
228
|
if options.reset_members:
|
|
@@ -177,6 +230,7 @@ class Entrypoint:
|
|
|
177
230
|
print(f'{Colors.BOLD} - Reset members: '
|
|
178
231
|
f'{Colors.GREEN}Success'
|
|
179
232
|
f'{Colors.RESET}')
|
|
233
|
+
Platform.flush()
|
|
180
234
|
|
|
181
235
|
# Set group avatar
|
|
182
236
|
if options.set_avatar:
|
|
@@ -184,30 +238,42 @@ class Entrypoint:
|
|
|
184
238
|
print(f'{Colors.BOLD} - Set avatar: '
|
|
185
239
|
f'{Colors.CYAN}{options.set_avatar}'
|
|
186
240
|
f'{Colors.RESET}')
|
|
241
|
+
Platform.flush()
|
|
187
242
|
|
|
188
243
|
# Footer
|
|
189
244
|
print(' ')
|
|
190
245
|
Platform.flush()
|
|
191
246
|
|
|
192
|
-
|
|
247
|
+
# Result
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
# Project, pylint: disable=too-many-branches,too-many-statements
|
|
193
251
|
@staticmethod
|
|
194
252
|
def project(
|
|
195
253
|
options: Namespace,
|
|
196
254
|
gitlab: GitLabFeature,
|
|
197
255
|
criteria: str,
|
|
198
|
-
) ->
|
|
256
|
+
) -> bool:
|
|
199
257
|
|
|
200
258
|
# Acquire project
|
|
201
259
|
project = gitlab.project(criteria)
|
|
202
260
|
|
|
203
|
-
# Acquire group
|
|
204
|
-
group = gitlab.group(project.namespace['id'])
|
|
205
|
-
|
|
206
261
|
# Show project details
|
|
207
262
|
print(f'{Colors.BOLD} - GitLab project: '
|
|
208
263
|
f'{Colors.YELLOW_LIGHT}{project.path_with_namespace} '
|
|
209
264
|
f'{Colors.CYAN}({project.description})'
|
|
210
265
|
f'{Colors.RESET}')
|
|
266
|
+
Platform.flush()
|
|
267
|
+
|
|
268
|
+
# Delete project
|
|
269
|
+
if options.delete_project:
|
|
270
|
+
gitlab.project_delete(criteria)
|
|
271
|
+
print(f'{Colors.BOLD} - Delete project: '
|
|
272
|
+
f'{Colors.GREEN}Success'
|
|
273
|
+
f'{Colors.RESET}')
|
|
274
|
+
print(' ')
|
|
275
|
+
Platform.flush()
|
|
276
|
+
return True
|
|
211
277
|
|
|
212
278
|
# Set project description
|
|
213
279
|
if options.set_description:
|
|
@@ -215,24 +281,34 @@ class Entrypoint:
|
|
|
215
281
|
print(f'{Colors.BOLD} - Set description: '
|
|
216
282
|
f'{Colors.CYAN}{options.set_description}'
|
|
217
283
|
f'{Colors.RESET}')
|
|
284
|
+
Platform.flush()
|
|
218
285
|
|
|
219
286
|
# Update project description
|
|
220
287
|
elif options.update_description:
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
288
|
+
namespace_description: str
|
|
289
|
+
if project.namespace['kind'] == 'user':
|
|
290
|
+
namespace = gitlab.namespace(project.namespace['id'])
|
|
291
|
+
namespace_description = namespace.name
|
|
292
|
+
else:
|
|
293
|
+
group = gitlab.group(project.namespace['id'])
|
|
294
|
+
namespace_description = Namespaces.describe(
|
|
295
|
+
name=group.name,
|
|
296
|
+
description=group.description,
|
|
297
|
+
)
|
|
298
|
+
if not project.description or \
|
|
299
|
+
not project.description.endswith(f' - {namespace_description}'):
|
|
300
|
+
description = f'{Namespaces.describe(name=project.name)}' \
|
|
301
|
+
f' - {namespace_description}'
|
|
228
302
|
gitlab.project_set_description(criteria, description)
|
|
229
303
|
print(f'{Colors.BOLD} - Updated description: '
|
|
230
304
|
f'{Colors.CYAN}{description}'
|
|
231
305
|
f'{Colors.RESET}')
|
|
306
|
+
Platform.flush()
|
|
232
307
|
else:
|
|
233
308
|
print(f'{Colors.BOLD} - Kept description: '
|
|
234
309
|
f'{Colors.GREEN}{project.description}'
|
|
235
310
|
f'{Colors.RESET}')
|
|
311
|
+
Platform.flush()
|
|
236
312
|
|
|
237
313
|
# Reset project features
|
|
238
314
|
if options.reset_features:
|
|
@@ -241,10 +317,12 @@ class Entrypoint:
|
|
|
241
317
|
print(f'{Colors.BOLD} - Reset features: '
|
|
242
318
|
f'{Colors.CYAN}{features}'
|
|
243
319
|
f'{Colors.RESET}')
|
|
320
|
+
Platform.flush()
|
|
244
321
|
else:
|
|
245
322
|
print(f'{Colors.BOLD} - Reset features: '
|
|
246
323
|
f'{Colors.GREEN}Already done'
|
|
247
324
|
f'{Colors.RESET}')
|
|
325
|
+
Platform.flush()
|
|
248
326
|
|
|
249
327
|
# Reset project members
|
|
250
328
|
if options.reset_members:
|
|
@@ -252,6 +330,7 @@ class Entrypoint:
|
|
|
252
330
|
print(f'{Colors.BOLD} - Reset members: '
|
|
253
331
|
f'{Colors.GREEN}Success'
|
|
254
332
|
f'{Colors.RESET}')
|
|
333
|
+
Platform.flush()
|
|
255
334
|
|
|
256
335
|
# Set project avatar
|
|
257
336
|
if options.set_avatar:
|
|
@@ -259,6 +338,31 @@ class Entrypoint:
|
|
|
259
338
|
print(f'{Colors.BOLD} - Set avatar: '
|
|
260
339
|
f'{Colors.CYAN}{options.set_avatar}'
|
|
261
340
|
f'{Colors.RESET}')
|
|
341
|
+
Platform.flush()
|
|
342
|
+
|
|
343
|
+
# Run project housekeeping
|
|
344
|
+
if options.run_housekeeping:
|
|
345
|
+
gitlab.project_run_housekeeping(criteria)
|
|
346
|
+
print(f'{Colors.BOLD} - Ran housekeeping: '
|
|
347
|
+
f'{Colors.GREEN}Success'
|
|
348
|
+
f'{Colors.RESET}')
|
|
349
|
+
Platform.flush()
|
|
350
|
+
|
|
351
|
+
# Archive project
|
|
352
|
+
if options.archive_project:
|
|
353
|
+
gitlab.project_set_archive(criteria, True)
|
|
354
|
+
print(f'{Colors.BOLD} - Archive project: '
|
|
355
|
+
f'{Colors.GREEN}Success'
|
|
356
|
+
f'{Colors.RESET}')
|
|
357
|
+
Platform.flush()
|
|
358
|
+
|
|
359
|
+
# Unarchive project
|
|
360
|
+
elif options.unarchive_project:
|
|
361
|
+
gitlab.project_set_archive(criteria, False)
|
|
362
|
+
print(f'{Colors.BOLD} - Unarchive project: '
|
|
363
|
+
f'{Colors.GREEN}Success'
|
|
364
|
+
f'{Colors.RESET}')
|
|
365
|
+
Platform.flush()
|
|
262
366
|
|
|
263
367
|
# Protect project branches
|
|
264
368
|
if options.protect_branches:
|
|
@@ -267,10 +371,12 @@ class Entrypoint:
|
|
|
267
371
|
print(f'{Colors.BOLD} - Protecting branches: '
|
|
268
372
|
f'{Colors.CYAN}{branches}'
|
|
269
373
|
f'{Colors.RESET}')
|
|
374
|
+
Platform.flush()
|
|
270
375
|
else:
|
|
271
376
|
print(f'{Colors.BOLD} - Protecting branches: '
|
|
272
377
|
f'{Colors.GREEN}Already done'
|
|
273
378
|
f'{Colors.RESET}')
|
|
379
|
+
Platform.flush()
|
|
274
380
|
|
|
275
381
|
# Protect project tags
|
|
276
382
|
if options.protect_tags:
|
|
@@ -280,11 +386,16 @@ class Entrypoint:
|
|
|
280
386
|
f'{Colors.CYAN}{tags}'
|
|
281
387
|
f'{Colors.GREEN} (level: {options.protect_tags})'
|
|
282
388
|
f'{Colors.RESET}')
|
|
389
|
+
Platform.flush()
|
|
283
390
|
else:
|
|
284
391
|
print(f'{Colors.BOLD} - Protecting tags: '
|
|
285
392
|
f'{Colors.GREEN}Already done'
|
|
286
393
|
f'{Colors.RESET}')
|
|
394
|
+
Platform.flush()
|
|
287
395
|
|
|
288
396
|
# Footer
|
|
289
397
|
print(' ')
|
|
290
398
|
Platform.flush()
|
|
399
|
+
|
|
400
|
+
# Result
|
|
401
|
+
return True
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
3
|
# Standard libraries
|
|
4
|
-
from argparse import (_ArgumentGroup, ArgumentParser, Namespace,
|
|
4
|
+
from argparse import (_ArgumentGroup, _MutuallyExclusiveGroup, ArgumentParser, Namespace,
|
|
5
|
+
RawTextHelpFormatter)
|
|
5
6
|
from os import environ
|
|
6
7
|
from shutil import get_terminal_size
|
|
7
8
|
from sys import exit as sys_exit
|
|
@@ -19,6 +20,7 @@ def main() -> None:
|
|
|
19
20
|
# Variables
|
|
20
21
|
group: _ArgumentGroup
|
|
21
22
|
result: bool = False
|
|
23
|
+
subgroup: _MutuallyExclusiveGroup
|
|
22
24
|
|
|
23
25
|
# Arguments creation
|
|
24
26
|
parser: ArgumentParser = ArgumentParser(
|
|
@@ -122,6 +124,40 @@ def main() -> None:
|
|
|
122
124
|
help='Update description of GitLab projects and groups automatically',
|
|
123
125
|
)
|
|
124
126
|
|
|
127
|
+
# Arguments advanced settings definitions
|
|
128
|
+
group = parser.add_argument_group('advanced settings arguments')
|
|
129
|
+
group.add_argument(
|
|
130
|
+
'--run-housekeeping',
|
|
131
|
+
dest='run_housekeeping',
|
|
132
|
+
action='store_true',
|
|
133
|
+
help='Run housekeeping of project or projects GitLab in groups',
|
|
134
|
+
)
|
|
135
|
+
subgroup = group.add_mutually_exclusive_group()
|
|
136
|
+
subgroup.add_argument(
|
|
137
|
+
'--archive-project',
|
|
138
|
+
dest='archive_project',
|
|
139
|
+
action='store_true',
|
|
140
|
+
help='Archive project or projects in GitLab groups',
|
|
141
|
+
)
|
|
142
|
+
subgroup.add_argument(
|
|
143
|
+
'--unarchive-project',
|
|
144
|
+
dest='unarchive_project',
|
|
145
|
+
action='store_true',
|
|
146
|
+
help='Unarchive project or projects in GitLab groups',
|
|
147
|
+
)
|
|
148
|
+
group.add_argument(
|
|
149
|
+
'--delete-group',
|
|
150
|
+
dest='delete_group',
|
|
151
|
+
action='store_true',
|
|
152
|
+
help='Delete group or groups in GitLab groups',
|
|
153
|
+
)
|
|
154
|
+
group.add_argument(
|
|
155
|
+
'--delete-project',
|
|
156
|
+
dest='delete_project',
|
|
157
|
+
action='store_true',
|
|
158
|
+
help='Delete project or projects in GitLab groups',
|
|
159
|
+
)
|
|
160
|
+
|
|
125
161
|
# Arguments repository settings definitions
|
|
126
162
|
group = parser.add_argument_group('repository settings arguments')
|
|
127
163
|
group.add_argument(
|
|
@@ -151,7 +187,7 @@ def main() -> None:
|
|
|
151
187
|
dest='path',
|
|
152
188
|
action='store',
|
|
153
189
|
nargs='?',
|
|
154
|
-
help='GitLab group or project path',
|
|
190
|
+
help='GitLab group, user namespace or project path',
|
|
155
191
|
)
|
|
156
192
|
|
|
157
193
|
# Arguments parser
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
3
|
# Standard libraries
|
|
4
|
+
from time import sleep
|
|
4
5
|
from typing import List
|
|
5
6
|
|
|
6
7
|
# Modules libraries
|
|
7
8
|
from gitlab import Gitlab
|
|
8
|
-
from gitlab.exceptions import GitlabListError
|
|
9
|
-
from gitlab.v4.objects import Group, Project
|
|
9
|
+
from gitlab.exceptions import GitlabGetError, GitlabListError
|
|
10
|
+
from gitlab.v4.objects import Group, Namespace, Project, User
|
|
10
11
|
|
|
11
12
|
# GitLabFeature class
|
|
12
13
|
class GitLabFeature:
|
|
@@ -25,12 +26,21 @@ class GitLabFeature:
|
|
|
25
26
|
def group(self, criteria: str) -> Group:
|
|
26
27
|
return self.__gitlab.groups.get(criteria)
|
|
27
28
|
|
|
29
|
+
# Group delete
|
|
30
|
+
def group_delete(self, criteria: str) -> None:
|
|
31
|
+
|
|
32
|
+
# Delete group
|
|
33
|
+
if not self.__dry_run:
|
|
34
|
+
group = self.group(criteria)
|
|
35
|
+
group.delete()
|
|
36
|
+
sleep(10)
|
|
37
|
+
|
|
28
38
|
# Group reset members
|
|
29
39
|
def group_reset_members(self, criteria: str) -> None:
|
|
30
40
|
|
|
31
41
|
# Remove group members
|
|
32
42
|
group = self.group(criteria)
|
|
33
|
-
for member in group.members.list():
|
|
43
|
+
for member in group.members.list(get_all=True):
|
|
34
44
|
if not self.__dry_run:
|
|
35
45
|
group.members.delete(member.id)
|
|
36
46
|
|
|
@@ -61,10 +71,23 @@ class GitLabFeature:
|
|
|
61
71
|
# Save group
|
|
62
72
|
group.save()
|
|
63
73
|
|
|
74
|
+
# Namespace
|
|
75
|
+
def namespace(self, criteria: str) -> Namespace:
|
|
76
|
+
return self.__gitlab.namespaces.get(criteria)
|
|
77
|
+
|
|
64
78
|
# Project
|
|
65
79
|
def project(self, criteria: str) -> Project:
|
|
66
80
|
return self.__gitlab.projects.get(criteria)
|
|
67
81
|
|
|
82
|
+
# Project delete
|
|
83
|
+
def project_delete(self, criteria: str) -> None:
|
|
84
|
+
|
|
85
|
+
# Delete project
|
|
86
|
+
project = self.project(criteria)
|
|
87
|
+
if not self.__dry_run:
|
|
88
|
+
project.delete()
|
|
89
|
+
sleep(5)
|
|
90
|
+
|
|
68
91
|
# Project protect branches
|
|
69
92
|
def project_protect_branches(self, criteria: str) -> List[str]:
|
|
70
93
|
|
|
@@ -72,14 +95,15 @@ class GitLabFeature:
|
|
|
72
95
|
result: List[str] = []
|
|
73
96
|
project = self.project(criteria)
|
|
74
97
|
try:
|
|
75
|
-
assert project.branches.list()
|
|
98
|
+
assert project.branches.list(get_all=True)
|
|
76
99
|
except (AssertionError, GitlabListError):
|
|
77
100
|
return result
|
|
78
101
|
|
|
79
102
|
# Acquire project, branches and protected branches
|
|
80
|
-
branches = [branch.name for branch in project.branches.list()]
|
|
103
|
+
branches = [branch.name for branch in project.branches.list(get_all=True)]
|
|
81
104
|
protectedbranches = [
|
|
82
|
-
protectedbranch.name
|
|
105
|
+
protectedbranch.name
|
|
106
|
+
for protectedbranch in project.protectedbranches.list(get_all=True)
|
|
83
107
|
]
|
|
84
108
|
|
|
85
109
|
# Protect main/master
|
|
@@ -132,7 +156,7 @@ class GitLabFeature:
|
|
|
132
156
|
result: List[str] = []
|
|
133
157
|
project = self.project(criteria)
|
|
134
158
|
try:
|
|
135
|
-
assert project.tags.list()
|
|
159
|
+
assert project.tags.list(get_all=True)
|
|
136
160
|
except (AssertionError, GitlabListError):
|
|
137
161
|
return result
|
|
138
162
|
|
|
@@ -151,11 +175,11 @@ class GitLabFeature:
|
|
|
151
175
|
|
|
152
176
|
# Acquire protected tags
|
|
153
177
|
protectedtags = [
|
|
154
|
-
protectedtag.name for protectedtag in project.protectedtags.list()
|
|
178
|
+
protectedtag.name for protectedtag in project.protectedtags.list(get_all=True)
|
|
155
179
|
]
|
|
156
180
|
|
|
157
181
|
# Update protected tags
|
|
158
|
-
for protectedtag in project.protectedtags.list():
|
|
182
|
+
for protectedtag in project.protectedtags.list(get_all=True):
|
|
159
183
|
protectedtag_level = protectedtag.create_access_levels[0]['access_level']
|
|
160
184
|
if protectedtag_level != 0 and (access_level == 0
|
|
161
185
|
or protectedtag_level < access_level):
|
|
@@ -169,7 +193,7 @@ class GitLabFeature:
|
|
|
169
193
|
result += [name]
|
|
170
194
|
|
|
171
195
|
# Protect unprotected tags
|
|
172
|
-
for tag in project.tags.list():
|
|
196
|
+
for tag in project.tags.list(get_all=True):
|
|
173
197
|
if tag.name not in protectedtags:
|
|
174
198
|
if not self.__dry_run:
|
|
175
199
|
project.protectedtags.create({
|
|
@@ -193,80 +217,145 @@ class GitLabFeature:
|
|
|
193
217
|
result: List[str] = []
|
|
194
218
|
project = self.__gitlab.projects.get(criteria, statistics=True)
|
|
195
219
|
|
|
196
|
-
# Disable unused
|
|
220
|
+
# Disable unused feature (Issues)
|
|
197
221
|
if project.issues_enabled and not project.issues.list(get_all=False):
|
|
198
222
|
project.issues_enabled = False
|
|
199
223
|
result += ['Issues']
|
|
224
|
+
|
|
225
|
+
# Disable unused feature (Repository)
|
|
200
226
|
try:
|
|
201
227
|
assert project.commits.list(get_all=False)
|
|
202
228
|
except (AssertionError, GitlabListError):
|
|
203
229
|
if project.repository_access_level != 'disabled':
|
|
204
230
|
project.repository_access_level = 'disabled'
|
|
205
231
|
result += ['Repository']
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
232
|
+
|
|
233
|
+
# Disable unused feature (Merge requests)
|
|
234
|
+
try:
|
|
235
|
+
if not project.mergerequests.list(get_all=False):
|
|
236
|
+
pass
|
|
237
|
+
elif len(project.branches.list(get_all=False)) > 1:
|
|
238
|
+
pass
|
|
239
|
+
else:
|
|
240
|
+
assert False
|
|
241
|
+
except (AssertionError, GitlabListError):
|
|
242
|
+
if project.merge_requests_enabled:
|
|
243
|
+
project.merge_requests_enabled = False
|
|
244
|
+
result += ['Merge requests']
|
|
245
|
+
|
|
246
|
+
# Disable unused feature (Forks)
|
|
210
247
|
if project.forking_access_level != 'disabled' and not project.forks.list(
|
|
211
248
|
get_all=False):
|
|
212
249
|
project.forking_access_level = 'disabled'
|
|
213
250
|
result += ['Forks']
|
|
251
|
+
|
|
252
|
+
# Disable unused feature (Git LFS)
|
|
214
253
|
if project.lfs_enabled and project.statistics['lfs_objects_size'] == 0:
|
|
215
254
|
project.lfs_enabled = False
|
|
216
255
|
result += ['Git LFS']
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
256
|
+
|
|
257
|
+
# Disable unused feature (CI/CD)
|
|
258
|
+
try:
|
|
259
|
+
if project.jobs.list(get_all=False):
|
|
260
|
+
pass
|
|
261
|
+
elif any('name' in item and item['name'] == '.gitlab-ci.yml'
|
|
262
|
+
for item in project.repository_tree(get_all=True)):
|
|
263
|
+
pass
|
|
264
|
+
elif any('path_with_namespace' in item
|
|
265
|
+
and item['path_with_namespace'] != project.path_with_namespace
|
|
266
|
+
for item in self.__gitlab.http_get(
|
|
267
|
+
f'/projects/{project.id}/job_token_scope/allowlist',
|
|
268
|
+
get_all=True,
|
|
269
|
+
)):
|
|
270
|
+
pass
|
|
271
|
+
else:
|
|
272
|
+
assert False
|
|
273
|
+
except (AssertionError, GitlabGetError, GitlabListError):
|
|
274
|
+
if project.jobs_enabled:
|
|
275
|
+
project.jobs_enabled = False
|
|
276
|
+
result += ['CI/CD']
|
|
277
|
+
|
|
278
|
+
# Disable unused feature (Container registry)
|
|
220
279
|
if project.container_registry_enabled and not project.repositories.list(
|
|
221
280
|
get_all=False):
|
|
222
281
|
project.container_registry_enabled = False
|
|
223
282
|
result += ['Container registry']
|
|
283
|
+
|
|
284
|
+
# Disable unused feature (Analytics)
|
|
224
285
|
if project.analytics_access_level != 'disabled':
|
|
225
286
|
project.analytics_access_level = 'disabled'
|
|
226
287
|
result += ['Analytics']
|
|
288
|
+
|
|
289
|
+
# Disable unused feature (Security and Compliance)
|
|
227
290
|
if project.security_and_compliance_access_level != 'disabled':
|
|
228
291
|
project.security_and_compliance_access_level = 'disabled'
|
|
229
292
|
result += ['Security and Compliance']
|
|
293
|
+
|
|
294
|
+
# Disable unused feature (Wiki)
|
|
230
295
|
if project.wiki_enabled and not project.wikis.list(get_all=False):
|
|
231
296
|
project.wiki_enabled = False
|
|
232
297
|
result += ['Wiki']
|
|
298
|
+
|
|
299
|
+
# Disable unused feature (Snippets)
|
|
233
300
|
if project.snippets_enabled and not project.snippets.list(get_all=False):
|
|
234
301
|
project.snippets_enabled = False
|
|
235
302
|
result += ['Snippets']
|
|
303
|
+
|
|
304
|
+
# Disable unused feature (Package registry)
|
|
236
305
|
if project.packages_enabled and not project.packages.list(get_all=False):
|
|
237
306
|
project.packages_enabled = False
|
|
238
307
|
result += ['Package registry']
|
|
308
|
+
|
|
309
|
+
# Disable unused feature (Model experiments)
|
|
239
310
|
if project.model_experiments_access_level != 'disabled':
|
|
240
311
|
project.model_experiments_access_level = 'disabled'
|
|
241
312
|
result += ['Model experiments']
|
|
313
|
+
|
|
314
|
+
# Disable unused feature (Model registry)
|
|
242
315
|
if project.model_registry_access_level != 'disabled':
|
|
243
316
|
project.model_registry_access_level = 'disabled'
|
|
244
317
|
result += ['Model registry']
|
|
318
|
+
|
|
319
|
+
# Disable unused feature (Pages)
|
|
245
320
|
if project.pages_access_level != 'disabled':
|
|
246
321
|
project.pages_access_level = 'disabled'
|
|
247
322
|
result += ['Pages']
|
|
323
|
+
|
|
324
|
+
# Disable unused feature (Monitor)
|
|
248
325
|
if project.monitor_access_level != 'disabled':
|
|
249
326
|
project.monitor_access_level = 'disabled'
|
|
250
327
|
result += ['Monitor']
|
|
328
|
+
|
|
329
|
+
# Disable unused feature (Environments)
|
|
251
330
|
try:
|
|
252
331
|
assert project.environments.list(get_all=False)
|
|
253
332
|
except (AssertionError, GitlabListError):
|
|
254
333
|
if project.environments_access_level != 'disabled':
|
|
255
334
|
project.environments_access_level = 'disabled'
|
|
256
335
|
result += ['Environments']
|
|
336
|
+
|
|
337
|
+
# Disable unused feature (Feature flags)
|
|
257
338
|
if project.feature_flags_access_level != 'disabled':
|
|
258
339
|
project.feature_flags_access_level = 'disabled'
|
|
259
340
|
result += ['Feature flags']
|
|
341
|
+
|
|
342
|
+
# Disable unused feature (Infrastructure)
|
|
260
343
|
if project.infrastructure_access_level != 'disabled':
|
|
261
344
|
project.infrastructure_access_level = 'disabled'
|
|
262
345
|
result += ['Infrastructure']
|
|
346
|
+
|
|
347
|
+
# Disable unused feature (Releases)
|
|
263
348
|
if project.releases_access_level != 'disabled' and not project.releases.list(
|
|
264
349
|
get_all=False):
|
|
265
350
|
project.releases_access_level = 'disabled'
|
|
266
351
|
result += ['Releases']
|
|
352
|
+
|
|
353
|
+
# Disable unused feature (Service Desk)
|
|
267
354
|
if project.service_desk_enabled:
|
|
268
355
|
project.service_desk_enabled = False
|
|
269
356
|
result += ['Service Desk']
|
|
357
|
+
|
|
358
|
+
# Disable unused feature (Auto DevOps)
|
|
270
359
|
if project.auto_devops_enabled:
|
|
271
360
|
project.auto_devops_enabled = False
|
|
272
361
|
result += ['Auto DevOps']
|
|
@@ -284,12 +373,33 @@ class GitLabFeature:
|
|
|
284
373
|
# Remove project members
|
|
285
374
|
if not self.__dry_run:
|
|
286
375
|
project = self.project(criteria)
|
|
287
|
-
for member in project.members.list():
|
|
376
|
+
for member in project.members.list(get_all=True):
|
|
288
377
|
project.members.delete(member.id)
|
|
289
378
|
|
|
290
379
|
# Save project
|
|
291
380
|
project.save()
|
|
292
381
|
|
|
382
|
+
# Project run housekeeping
|
|
383
|
+
def project_run_housekeeping(self, criteria: str) -> None:
|
|
384
|
+
|
|
385
|
+
# Run project housekeeping
|
|
386
|
+
if not self.__dry_run:
|
|
387
|
+
project = self.project(criteria)
|
|
388
|
+
project.housekeeping()
|
|
389
|
+
|
|
390
|
+
# Project set archive
|
|
391
|
+
def project_set_archive(self, criteria: str, enabled: bool) -> None:
|
|
392
|
+
|
|
393
|
+
# Archive project
|
|
394
|
+
if not self.__dry_run and enabled:
|
|
395
|
+
project = self.project(criteria)
|
|
396
|
+
project.archive()
|
|
397
|
+
|
|
398
|
+
# Unarchive project
|
|
399
|
+
elif not self.__dry_run:
|
|
400
|
+
project = self.project(criteria)
|
|
401
|
+
project.unarchive()
|
|
402
|
+
|
|
293
403
|
# Project set avatar
|
|
294
404
|
def project_set_avatar(self, criteria: str, file: str) -> None:
|
|
295
405
|
|
|
@@ -313,25 +423,11 @@ class GitLabFeature:
|
|
|
313
423
|
# Save project
|
|
314
424
|
project.save()
|
|
315
425
|
|
|
426
|
+
# User
|
|
427
|
+
def user(self, criteria: str) -> User:
|
|
428
|
+
return self.__gitlab.users.list(username=criteria)[0]
|
|
429
|
+
|
|
316
430
|
# URL
|
|
317
431
|
@property
|
|
318
432
|
def url(self) -> str:
|
|
319
433
|
return str(self.__gitlab.api_url)
|
|
320
|
-
|
|
321
|
-
# Helper class
|
|
322
|
-
class Helper:
|
|
323
|
-
|
|
324
|
-
# Capitalize
|
|
325
|
-
@staticmethod
|
|
326
|
-
def capitalize(text: str) -> str:
|
|
327
|
-
return f'{text[:1].capitalize()}{text[1:]}'
|
|
328
|
-
|
|
329
|
-
# Describe
|
|
330
|
-
@staticmethod
|
|
331
|
-
def describe(
|
|
332
|
-
description: str,
|
|
333
|
-
name: str,
|
|
334
|
-
) -> str:
|
|
335
|
-
if description:
|
|
336
|
-
return description
|
|
337
|
-
return GitLabFeature.Helper.capitalize(name)
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Standard libraries
|
|
4
|
+
from re import sub
|
|
5
|
+
|
|
6
|
+
# Namespaces class
|
|
7
|
+
class Namespaces:
|
|
8
|
+
|
|
9
|
+
# Capitalize
|
|
10
|
+
@staticmethod
|
|
11
|
+
def capitalize(text: str, words: bool = False) -> str:
|
|
12
|
+
|
|
13
|
+
# Capitalize words
|
|
14
|
+
if words:
|
|
15
|
+
return ' '.join(
|
|
16
|
+
Namespaces.capitalize(word, words=False) for word in text.split())
|
|
17
|
+
|
|
18
|
+
# Capitalize text
|
|
19
|
+
return f'{text[:1].capitalize()}{text[1:]}'
|
|
20
|
+
|
|
21
|
+
# Describe
|
|
22
|
+
@staticmethod
|
|
23
|
+
def describe(
|
|
24
|
+
name: str,
|
|
25
|
+
description: str = '',
|
|
26
|
+
) -> str:
|
|
27
|
+
|
|
28
|
+
# Use description
|
|
29
|
+
if description:
|
|
30
|
+
return description
|
|
31
|
+
|
|
32
|
+
# Adapt name
|
|
33
|
+
return Namespaces.capitalize(sub(r'[-_]', ' ', name), words=True)
|
|
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
|