Coverage for src/cli/entrypoint.py: 6.81%
382 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 21:20 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 21:20 +0000
1#!/usr/bin/env python3
3# Standard libraries
4from argparse import Namespace
5from enum import Enum
6import json
7from typing import Dict, List, Optional
8import urllib.parse
10# Modules libraries
11from gitlab.config import ConfigError, GitlabConfigParser
12from gitlab.exceptions import GitlabGetError
13from gitlab.v4.objects import (
14 Group as GitLabGroup,
15 Project as GitLabProject,
16 User as GitLabUser,
17)
18import questionary
20# Components
21from ..features.gitlab import GitLabFeature
22from ..prints.colors import Colors
23from ..prints.themes import Themes
24from ..system.platform import Platform
25from ..types.environments import Environments
26from ..types.gitlab import (
27 MergeRequestsMethod,
28 MergeRequestsPipelines,
29 MergeRequestsResolved,
30 MergeRequestsSkipped,
31 MergeRequestsSquash,
32 ProjectFeatures,
33)
34from ..types.namespaces import Namespaces
36# Entrypoint class, pylint: disable=too-few-public-methods
37class Entrypoint:
39 # Enumerations
40 Result = Enum('Result', [
41 'SUCCESS',
42 'FINALIZE',
43 'ERROR',
44 'CRITICAL',
45 ])
47 # CLI, pylint: disable=too-many-branches,too-many-locals,too-many-statements
48 @staticmethod
49 def cli(
50 options: Namespace,
51 environments: Environments,
52 ) -> Result:
54 # Variables
55 group: Optional[GitLabGroup] = None
56 progress_count: int
57 progress_index: int
58 project: Optional[GitLabProject] = None
59 result: Entrypoint.Result
60 user: Optional[GitLabUser] = None
62 # Header
63 print(' ')
65 # Parse URL variables
66 gitlab_splits: urllib.parse.SplitResult = urllib.parse.urlsplit(options.url_path)
67 gitlab_id: str = f'{gitlab_splits.netloc}'
68 gitlab_url: str = f'{gitlab_splits.scheme}://{gitlab_splits.netloc}'
69 gitlab_path: str = gitlab_splits.path.lstrip('/')
71 # Prepare credentials
72 private_token: str = environments.value('gitlab_token')
73 job_token: str = environments.value('ci_job_token')
74 ssl_verify: bool | str = True
76 # Parse configuration files
77 try:
78 config: GitlabConfigParser
79 if not private_token:
80 config = GitlabConfigParser(gitlab_id, options.configs)
81 private_token = str(config.private_token)
82 if ssl_verify and (not config.ssl_verify
83 or isinstance(config.ssl_verify, str)):
84 ssl_verify = config.ssl_verify
85 except ConfigError as e:
86 print(str(e))
88 # GitLab client
89 gitlab = GitLabFeature(
90 url=gitlab_url,
91 private_token=private_token,
92 job_token=job_token,
93 ssl_verify=ssl_verify,
94 dry_run=options.dry_run,
95 )
96 print(f'{Colors.BOLD} - GitLab host: '
97 f'{Colors.GREEN}{gitlab.url}'
98 f'{Colors.CYAN} ({gitlab.username})'
99 f'{Colors.RESET}')
100 Platform.flush()
102 # GitLab path
103 try:
104 group = gitlab.group(gitlab_path)
105 print(f'{Colors.BOLD} - GitLab group: '
106 f'{Colors.GREEN}{group.full_path}'
107 f'{Colors.CYAN} # {group.description if group.description else "/"}'
108 f'{Colors.RESET}')
109 print(' ')
110 Platform.flush()
111 except GitlabGetError as exception:
112 try:
113 if '/' in gitlab_path:
114 raise TypeError from exception
115 user = gitlab.user(gitlab_path)
116 namespace = gitlab.namespace(gitlab_path)
117 print(f'{Colors.BOLD} - GitLab user namespace: '
118 f'{Colors.GREEN}{namespace.full_path}'
119 f'{Colors.CYAN} ({namespace.name})'
120 f'{Colors.RESET}')
121 print(' ')
122 Platform.flush()
123 except (GitlabGetError, TypeError):
124 project = gitlab.project(gitlab_path)
125 print(
126 f'{Colors.BOLD} - GitLab project: '
127 f'{Colors.GREEN}{project.path_with_namespace}'
128 f'{Colors.CYAN} # {project.description if project.description else "/"}'
129 f'{Colors.RESET}')
130 print(' ')
131 Platform.flush()
133 # Handle available features
134 if options.available_features:
135 print(f'{Colors.BOLD} - GitLab project:'
136 f'{Colors.RESET}')
137 print(f'{Colors.BOLD} - Available features: '
138 f'{Colors.CYAN}\'{", ".join(ProjectFeatures.names())}\''
139 f'{Colors.RESET}')
140 Platform.flush()
141 return Entrypoint.Result.FINALIZE
143 # Handle single project
144 if project:
145 Entrypoint.project(
146 options,
147 gitlab,
148 project.path_with_namespace,
149 )
151 # Handle group recursively
152 elif group:
154 # Handle group
155 if not options.exclude_group:
156 result = Entrypoint.group(
157 options,
158 gitlab,
159 group.full_path,
160 )
161 if result in [
162 Entrypoint.Result.FINALIZE,
163 Entrypoint.Result.ERROR,
164 ]:
165 return result
167 # Iterate through subgroups
168 if not options.exclude_subgroups:
169 group_subgroups = sorted(
170 group.descendant_groups.list(
171 get_all=True,
172 include_subgroups=True,
173 order_by='path',
174 sort='asc',
175 ),
176 key=lambda item: item.full_path,
177 )
178 progress_count = len(group_subgroups)
179 progress_index = 0
180 for group_subgroup in group_subgroups:
181 progress_index += 1
182 result = Entrypoint.group(
183 options,
184 gitlab,
185 group_subgroup.full_path,
186 True,
187 progress_index=progress_index,
188 progress_count=progress_count,
189 )
190 if result in [
191 Entrypoint.Result.FINALIZE,
192 Entrypoint.Result.ERROR,
193 ]:
194 return result
196 # Iterate through projects
197 if not options.exclude_projects:
198 projects = sorted(
199 group.projects.list(
200 get_all=True,
201 with_shared=False,
202 include_subgroups=not options.exclude_subgroups,
203 order_by='path',
204 sort='asc',
205 ),
206 key=lambda item: item.path_with_namespace,
207 )
208 progress_count = len(projects)
209 progress_index = 0
210 for group_project in projects:
211 progress_index += 1
212 result = Entrypoint.project(
213 options,
214 gitlab,
215 group_project.path_with_namespace,
216 progress_index=progress_index,
217 progress_count=progress_count,
218 )
219 if result in [
220 Entrypoint.Result.FINALIZE,
221 Entrypoint.Result.ERROR,
222 ]:
223 return result
225 # Handle user recursively
226 elif user:
228 # Iterate through projects
229 if not options.exclude_projects:
230 user_projects = sorted(
231 user.projects.list(
232 get_all=True,
233 order_by='path',
234 sort='asc',
235 ),
236 key=lambda item: item.path_with_namespace,
237 )
238 progress_count = len(user_projects)
239 progress_index = 0
240 for user_project in user_projects:
241 progress_index += 1
242 result = Entrypoint.project(
243 options,
244 gitlab,
245 user_project.path_with_namespace,
246 progress_index=progress_index,
247 progress_count=progress_count,
248 )
249 if result in [
250 Entrypoint.Result.FINALIZE,
251 Entrypoint.Result.ERROR,
252 ]:
253 return result
255 # Result
256 return Entrypoint.Result.SUCCESS
258 # Confirm
259 @staticmethod
260 def confirm(
261 description: str,
262 text: str = '',
263 interactive: bool = True,
264 action: str = '',
265 indent: str = ' ',
266 ) -> bool:
268 # Header
269 print(
270 f'{Colors.BOLD}{indent}- {description}{": " if description else ""}Confirm \''
271 f'{Colors.RED}{text}'
272 f'{Colors.BOLD}\' {action}:'
273 f'{Colors.RESET}', end='')
274 Platform.flush()
276 # Confirm without user interaction
277 if not interactive:
278 print(f'{Colors.RED} Confirmed by parameters'
279 f'{Colors.RESET}')
280 Platform.flush()
281 return True
283 # Get user configuration
284 answer: bool = questionary.confirm(
285 message='',
286 default=False,
287 qmark='',
288 style=Themes.confirmation_style(),
289 auto_enter=True,
290 ).ask()
292 # Result
293 return answer
295 # Group, pylint: disable=too-many-arguments,too-many-locals,too-many-positional-arguments
296 @staticmethod
297 def group(
298 options: Namespace,
299 gitlab: GitLabFeature,
300 criteria: str,
301 subgroup: bool = False,
302 progress_index: int = 1,
303 progress_count: int = 1,
304 ) -> Result:
306 # Variables
307 changed: bool
309 # Acquire group
310 group = gitlab.group(criteria)
312 # Acquire parent group
313 parent_group: Optional[GitLabGroup] = None
314 if group.parent_id:
315 parent_group = gitlab.group(group.parent_id)
317 # Get parent description
318 parent_description: str = ''
319 parent_name: str = ''
320 if parent_group:
321 parent_description = parent_group.description
322 parent_name = parent_group.name
324 # Show group details
325 group_type = 'subgroup' if subgroup else 'group'
326 if subgroup:
327 print(f'{Colors.BOLD} - GitLab {group_type} ('
328 f'{Colors.GREEN}{progress_index}'
329 f'{Colors.RESET}/'
330 f'{Colors.CYAN}{progress_count}'
331 f'{Colors.BOLD}) : '
332 f'{Colors.YELLOW_LIGHT}{group.full_path}'
333 f'{Colors.CYAN} # {group.description if group.description else "/"}'
334 f'{Colors.RESET}')
335 else:
336 print(f'{Colors.BOLD} - GitLab {group_type}: '
337 f'{Colors.YELLOW_LIGHT}{group.full_path}'
338 f'{Colors.CYAN} # {group.description if group.description else "/"}'
339 f'{Colors.RESET}')
340 Platform.flush()
342 # Delete group after validation
343 if options.delete_group:
344 if not Entrypoint.confirm(
345 f'Delete {group_type}',
346 group.full_path,
347 not options.confirm,
348 'deletion',
349 ):
350 print(' ')
351 Platform.flush()
352 return Entrypoint.Result.SUCCESS
354 # Delete group
355 gitlab.group_delete(criteria)
356 print(f'{Colors.BOLD} - Delete {group_type}: '
357 f'{Colors.GREEN}Success'
358 f'{Colors.RESET}')
359 print(' ')
360 Platform.flush()
361 return Entrypoint.Result.SUCCESS if subgroup else Entrypoint.Result.FINALIZE
363 # Set group description
364 if options.set_description:
365 changed = gitlab.group_set_description(
366 criteria,
367 options.set_description,
368 )
369 print(f'{Colors.BOLD} - Set description: '
370 f'{Colors.CYAN if changed else Colors.GREEN}{options.set_description}'
371 f'{Colors.RESET}')
372 Platform.flush()
374 # Update group description
375 elif options.update_description:
376 parent_description_text: str = ''
377 if parent_name:
378 parent_description_text = ' - ' + Namespaces.describe(
379 name=parent_name,
380 description=parent_description,
381 )
382 if not group.description or subgroup and ( \
383 not parent_description_text or \
384 not group.description.endswith(f'{parent_description_text}') \
385 ):
386 description = f'{Namespaces.describe(name=group.name)}' \
387 f'{parent_description_text}'
388 changed = gitlab.group_set_description(
389 criteria,
390 description,
391 )
392 print(f'{Colors.BOLD} - Updated description: '
393 f'{Colors.CYAN if changed else Colors.GREEN}{description}'
394 f'{Colors.RESET}')
395 Platform.flush()
396 else:
397 print(f'{Colors.BOLD} - Kept description: '
398 f'{Colors.GREEN}{group.description}'
399 f'{Colors.RESET}')
400 Platform.flush()
402 # Reset group members
403 if options.reset_members:
404 gitlab.group_reset_members(criteria)
405 print(f'{Colors.BOLD} - Reset members: '
406 f'{Colors.GREEN}Success'
407 f'{Colors.RESET}')
408 Platform.flush()
410 # Set group avatar
411 if options.set_avatar:
412 gitlab.group_set_avatar(
413 criteria,
414 options.set_avatar,
415 )
416 print(f'{Colors.BOLD} - Set avatar: '
417 f'{Colors.CYAN}{options.set_avatar}'
418 f'{Colors.RESET}')
419 Platform.flush()
421 # Set group roles create projects
422 if options.set_roles_create_projects:
423 changed = gitlab.group_set_roles_create_projects(
424 criteria,
425 options.set_roles_create_projects,
426 )
427 print(
428 f'{Colors.BOLD} - Set roles allowed to create projects: '
429 f'{Colors.CYAN if changed else Colors.GREEN}{options.set_roles_create_projects}'
430 f'{Colors.RESET}')
431 Platform.flush()
433 # Set group roles create subgroups
434 if options.set_roles_create_subgroups:
435 changed = gitlab.group_set_roles_create_subgroups(
436 criteria,
437 options.set_roles_create_subgroups,
438 )
439 print(
440 f'{Colors.BOLD} - Set roles allowed to create subgroups: '
441 f'{Colors.CYAN if changed else Colors.GREEN}{options.set_roles_create_subgroups}'
442 f'{Colors.RESET}')
443 Platform.flush()
445 # Get group labels
446 if options.get_group_labels:
447 labels_objects = gitlab.group_get_labels(criteria)
448 if labels_objects:
449 print(f'{Colors.BOLD} - Get labels to JSON: '
450 f'{Colors.RESET}', end='')
451 labels_list: List[Dict[str, str]] = []
452 for label in labels_objects:
453 labels_list += [{
454 'name': label.name,
455 'description': label.description,
456 'text_color': label.text_color,
457 'color': label.color,
458 }]
459 print(json.dumps(labels_list))
460 Platform.flush()
462 # Set group labels
463 elif options.set_group_labels:
464 print(f'{Colors.BOLD} - Set labels from JSON: '
465 f'{Colors.RESET}', end='')
466 labels_names: List[str] = []
467 labels_json = json.loads(options.set_group_labels)
468 for label_json in labels_json:
469 label_set = gitlab.group_set_label(
470 criteria,
471 name=label_json['name'],
472 description=label_json['description'],
473 text_color=label_json['text_color'],
474 color=label_json['color'],
475 )
476 labels_names += [label_set.name]
477 print(f'{Colors.CYAN}{", ".join(labels_names)}'
478 f'{Colors.RESET}')
479 Platform.flush()
481 # Dump group object
482 if options.dump:
483 print(' ')
484 print(group.to_json())
486 # Footer
487 print(' ')
488 Platform.flush()
490 # Result
491 return Entrypoint.Result.SUCCESS
493 # Project, pylint: disable=too-many-branches,too-many-locals,too-many-statements
494 @staticmethod
495 def project(
496 options: Namespace,
497 gitlab: GitLabFeature,
498 criteria: str,
499 progress_index: int = 1,
500 progress_count: int = 1,
501 ) -> Result:
503 # Variables
504 changed: Optional[bool]
506 # Acquire project
507 project = gitlab.project(criteria)
509 # Show project details
510 print(f'{Colors.BOLD} - GitLab project ('
511 f'{Colors.GREEN}{progress_index}'
512 f'{Colors.RESET}/'
513 f'{Colors.CYAN}{progress_count}'
514 f'{Colors.BOLD}) : '
515 f'{Colors.YELLOW_LIGHT}{project.path_with_namespace}'
516 f'{Colors.CYAN} # {project.description if project.description else "/"}'
517 f'{Colors.RESET}')
518 Platform.flush()
520 # Delete project after validation
521 if options.delete_project:
522 if not Entrypoint.confirm(
523 'Delete project',
524 project.path_with_namespace,
525 not options.confirm,
526 'deletion',
527 ):
528 print(' ')
529 Platform.flush()
530 return Entrypoint.Result.SUCCESS
532 # Delete project
533 gitlab.project_delete(criteria)
534 print(f'{Colors.BOLD} - Delete project: '
535 f'{Colors.GREEN}Success'
536 f'{Colors.RESET}')
537 print(' ')
538 Platform.flush()
539 return Entrypoint.Result.SUCCESS
541 # Set project description
542 if options.set_description:
543 changed = gitlab.project_set_description(
544 criteria,
545 options.set_description,
546 )
547 print(f'{Colors.BOLD} - Set description: '
548 f'{Colors.CYAN if changed else Colors.GREEN}{options.set_description}'
549 f'{Colors.RESET}')
550 Platform.flush()
552 # Update project description
553 elif options.update_description:
554 namespace_description: str
555 if project.namespace['kind'] == 'user':
556 namespace = gitlab.namespace(project.namespace['id'])
557 namespace_description = namespace.name
558 else:
559 group = gitlab.group(project.namespace['id'])
560 namespace_description = Namespaces.describe(
561 name=group.name,
562 description=group.description,
563 )
564 if not project.description or \
565 not project.description.endswith(f' - {namespace_description}'):
566 description = f'{Namespaces.describe(name=project.name)}' \
567 f' - {namespace_description}'
568 changed = gitlab.project_set_description(
569 criteria,
570 description,
571 )
572 print(f'{Colors.BOLD} - Updated description: '
573 f'{Colors.CYAN if changed else Colors.GREEN}{description}'
574 f'{Colors.RESET}')
575 Platform.flush()
576 else:
577 print(f'{Colors.BOLD} - Kept description: '
578 f'{Colors.GREEN}{project.description}'
579 f'{Colors.RESET}')
580 Platform.flush()
582 # Reset project features
583 if options.reset_features is not None:
584 features = ', '.join(
585 gitlab.project_features_reset(
586 criteria,
587 GitLabFeature.project_features_parse(options.reset_features),
588 ))
589 if features:
590 print(f'{Colors.BOLD} - Reset features: '
591 f'{Colors.CYAN}{features}'
592 f'{Colors.RESET}')
593 Platform.flush()
594 else:
595 print(f'{Colors.BOLD} - Reset features: '
596 f'{Colors.GREEN}Already done'
597 f'{Colors.RESET}')
598 Platform.flush()
600 # Disable project features
601 if options.disable_features:
602 features = ', '.join(
603 gitlab.project_features_disable(
604 criteria,
605 GitLabFeature.project_features_parse(options.disable_features),
606 ))
607 if features:
608 print(f'{Colors.BOLD} - Disable features: '
609 f'{Colors.CYAN}{features}'
610 f'{Colors.RESET}')
611 Platform.flush()
612 else:
613 print(f'{Colors.BOLD} - Disable features: '
614 f'{Colors.GREEN}Already done'
615 f'{Colors.RESET}')
616 Platform.flush()
618 # Enable project features
619 if options.enable_features:
620 features = ', '.join(
621 gitlab.project_features_enable(
622 criteria,
623 GitLabFeature.project_features_parse(options.enable_features),
624 ))
625 if features:
626 print(f'{Colors.BOLD} - Enable features: '
627 f'{Colors.CYAN}{features}'
628 f'{Colors.RESET}')
629 Platform.flush()
630 else:
631 print(f'{Colors.BOLD} - Enable features: '
632 f'{Colors.GREEN}Already done'
633 f'{Colors.RESET}')
634 Platform.flush()
636 # Reset project members
637 if options.reset_members:
638 gitlab.project_reset_members(criteria)
639 print(f'{Colors.BOLD} - Reset members: '
640 f'{Colors.GREEN}Success'
641 f'{Colors.RESET}')
642 Platform.flush()
644 # Set project avatar
645 if options.set_avatar:
646 gitlab.project_set_avatar(
647 criteria,
648 options.set_avatar,
649 )
650 print(f'{Colors.BOLD} - Set avatar: '
651 f'{Colors.CYAN}{options.set_avatar}'
652 f'{Colors.RESET}')
653 Platform.flush()
655 # Run project housekeeping
656 if options.run_housekeeping:
657 gitlab.project_run_housekeeping(criteria)
658 print(f'{Colors.BOLD} - Ran housekeeping: '
659 f'{Colors.GREEN}Success'
660 f'{Colors.RESET}')
661 Platform.flush()
663 # Prune project unreachable objects
664 if options.prune_unreachable_objects:
665 gitlab.project_prune_unreachable_objects(criteria)
666 print(f'{Colors.BOLD} - Pruned unreachable objects: '
667 f'{Colors.GREEN}Success'
668 f'{Colors.RESET}')
669 Platform.flush()
671 # Archive project
672 if options.archive_project:
673 changed = gitlab.project_set_archive(
674 criteria,
675 True,
676 )
677 print(f'{Colors.BOLD} - Archive project: '
678 f'{Colors.CYAN if changed else Colors.GREEN}'
679 f'{"Success" if changed else "Already done"}'
680 f'{Colors.RESET}')
681 Platform.flush()
683 # Unarchive project
684 elif options.unarchive_project:
685 changed = gitlab.project_set_archive(
686 criteria,
687 False,
688 )
689 print(f'{Colors.BOLD} - Unarchive project: '
690 f'{Colors.CYAN if changed else Colors.GREEN}'
691 f'{"Success" if changed else "Already done"}'
692 f'{Colors.RESET}')
693 Platform.flush()
695 # Show archived project
696 elif project.archived:
697 print(f'{Colors.BOLD} - Project inactive: '
698 f'{Colors.YELLOW_LIGHT}Archived'
699 f'{Colors.RESET}')
700 Platform.flush()
702 # Protect project branches
703 if options.protect_branches:
704 branches = ', '.join(gitlab.project_protect_branches(criteria))
705 if branches:
706 print(f'{Colors.BOLD} - Protecting branches: '
707 f'{Colors.CYAN}{branches}'
708 f'{Colors.RESET}')
709 Platform.flush()
710 else:
711 print(f'{Colors.BOLD} - Protecting branches: '
712 f'{Colors.GREEN}Already done'
713 f'{Colors.RESET}')
714 Platform.flush()
716 # Protect project tags
717 if options.protect_tags:
718 tags = ', '.join(gitlab.project_protect_tags(criteria, options.protect_tags))
719 if tags:
720 print(f'{Colors.BOLD} - Protecting tags: '
721 f'{Colors.CYAN}{tags}'
722 f'{Colors.GREEN} (level: {options.protect_tags})'
723 f'{Colors.RESET}')
724 Platform.flush()
725 else:
726 print(f'{Colors.BOLD} - Protecting tags: '
727 f'{Colors.GREEN}Already done'
728 f'{Colors.RESET}')
729 Platform.flush()
731 # Set project merge method
732 if options.set_merge_method is not None:
733 changed = gitlab.project_set_attribute(
734 criteria,
735 MergeRequestsMethod.ATTRIBUTE,
736 MergeRequestsMethod.VALUES[options.set_merge_method],
737 )
738 if changed is None:
739 print(f'{Colors.BOLD} - Set merge method: '
740 f'{Colors.RED}Unknown feature'
741 f'{Colors.RESET}')
742 else:
743 print(f'{Colors.BOLD} - Set merge method: '
744 f'{Colors.CYAN if changed else Colors.GREEN}'
745 f'{options.set_merge_method}'
746 f'{Colors.RESET}')
747 Platform.flush()
749 # Set project merge squash
750 if options.set_merge_squash is not None:
751 changed = gitlab.project_set_attribute(
752 criteria,
753 MergeRequestsSquash.ATTRIBUTE,
754 MergeRequestsSquash.VALUES[options.set_merge_squash],
755 )
756 if changed is None:
757 print(f'{Colors.BOLD} - Set merge squash: '
758 f'{Colors.RED}Unknown feature'
759 f'{Colors.RESET}')
760 else:
761 print(f'{Colors.BOLD} - Set merge squash: '
762 f'{Colors.CYAN if changed else Colors.GREEN}'
763 f'{options.set_merge_squash}'
764 f'{Colors.RESET}')
765 Platform.flush()
767 # Set project merge pipelines
768 if options.set_merge_pipelines is not None:
769 changed = gitlab.project_set_attribute(
770 criteria,
771 MergeRequestsPipelines.ATTRIBUTE,
772 options.set_merge_pipelines,
773 )
774 if changed is None:
775 print(f'{Colors.BOLD} - Set merge pipelines: '
776 f'{Colors.RED}Unknown feature'
777 f'{Colors.RESET}')
778 else:
779 print(f'{Colors.BOLD} - Set merge pipelines: '
780 f'{Colors.CYAN if changed else Colors.GREEN}'
781 f'{str(options.set_merge_pipelines).lower()}'
782 f'{Colors.RESET}')
783 Platform.flush()
785 # Set project merge skipped
786 if options.set_merge_skipped is not None:
787 changed = gitlab.project_set_attribute(
788 criteria,
789 MergeRequestsSkipped.ATTRIBUTE,
790 options.set_merge_skipped,
791 )
792 if changed is None:
793 print(f'{Colors.BOLD} - Set merge skipped: '
794 f'{Colors.RED}Unknown feature'
795 f'{Colors.RESET}')
796 else:
797 print(f'{Colors.BOLD} - Set merge skipped: '
798 f'{Colors.CYAN if changed else Colors.GREEN}'
799 f'{str(options.set_merge_skipped).lower()}'
800 f'{Colors.RESET}')
801 Platform.flush()
803 # Set project merge resolved
804 if options.set_merge_resolved is not None:
805 changed = gitlab.project_set_attribute(
806 criteria,
807 MergeRequestsResolved.ATTRIBUTE,
808 options.set_merge_resolved,
809 )
810 if changed is None:
811 print(f'{Colors.BOLD} - Set merge method: '
812 f'{Colors.RED}Unknown feature'
813 f'{Colors.RESET}')
814 else:
815 print(f'{Colors.BOLD} - Set merge method: '
816 f'{Colors.CYAN if changed else Colors.GREEN}'
817 f'{str(options.set_merge_resolved).lower()}'
818 f'{Colors.RESET}')
819 Platform.flush()
821 # Add path to CI/CD job token allowlist
822 if options.add_jobs_token_allowlist:
823 gitlab.project_job_allowlist_add(
824 criteria,
825 options.add_jobs_token_allowlist,
826 )
827 print(f'{Colors.BOLD} - Added to CI/CD job token allowlist: '
828 f'{Colors.CYAN}{options.add_jobs_token_allowlist}'
829 f'{Colors.RESET}')
830 Platform.flush()
832 # Remove path from CI/CD job token allowlist
833 if options.remove_jobs_token_allowlist:
834 gitlab.project_job_allowlist_remove(
835 criteria,
836 options.remove_jobs_token_allowlist,
837 )
838 print(f'{Colors.BOLD} - Removed from CI/CD job token allowlist: '
839 f'{Colors.CYAN}{options.remove_jobs_token_allowlist}'
840 f'{Colors.RESET}')
841 Platform.flush()
843 # Erase project jobs artifacts and traces
844 if options.erase_jobs_contents:
845 gitlab.project_erase_jobs_contents(criteria)
846 print(f'{Colors.BOLD} - Erased CI/CD jobs artifacts and traces: '
847 f'{Colors.GREEN}Success'
848 f'{Colors.RESET}')
849 Platform.flush()
851 # Erase project jobs artifacts
852 elif options.erase_jobs_artifacts:
853 gitlab.project_erase_jobs_artifacts(criteria)
854 print(f'{Colors.BOLD} - Erased CI/CD jobs artifacts: '
855 f'{Colors.GREEN}Success'
856 f'{Colors.RESET}')
857 Platform.flush()
859 # Get project issues boards
860 if options.get_project_issues_boards:
861 issues_boards_objects = gitlab.project_get_issues_boards(criteria)
862 if issues_boards_objects:
863 print(f'{Colors.BOLD} - Get issues boards to JSON: '
864 f'{Colors.RESET}', end='')
865 issues_boards_list: List[Dict[str, bool | str | List[Dict[str, str]]
866 | None]] = []
867 for board in issues_boards_objects:
868 board_lists: List[Dict[str, str]] = []
869 for board_list in board.lists.list(get_all=True):
870 board_lists += [{
871 'label': board_list.label['name'],
872 'position': board_list.position,
873 }]
874 issues_boards_list += [{
875 'name': board.name,
876 'hide_backlog_list': board.hide_backlog_list,
877 'hide_closed_list': board.hide_closed_list,
878 'lists': board_lists,
879 }]
880 print(json.dumps(issues_boards_list))
881 Platform.flush()
883 # Set project issues boards
884 elif options.set_project_issues_boards:
885 print(f'{Colors.BOLD} - Set issues boards from JSON: '
886 f'{Colors.RESET}', end='')
887 if not project.archived:
888 issues_boards_names: List[str] = []
889 issues_boards_json = json.loads(options.set_project_issues_boards)
890 for board_json in issues_boards_json:
891 board_set = gitlab.project_set_issues_board(
892 criteria, name=board_json['name'],
893 hide_backlog_list=board_json['hide_backlog_list'],
894 hide_closed_list=board_json['hide_closed_list'], lists=[{
895 'label': board_list['label'],
896 'position': board_list['position'],
897 } for board_list in board_json['lists']])
898 issues_boards_names += [board_set.name]
899 print(f'{Colors.CYAN}{", ".join(issues_boards_names)}'
900 f'{Colors.RESET}')
901 else:
902 print(f'{Colors.RED}Archived project are read-only'
903 f'{Colors.RESET}')
904 Platform.flush()
906 # Get project labels
907 if options.get_project_labels:
908 labels_objects = gitlab.project_get_labels(criteria)
909 if labels_objects:
910 print(f'{Colors.BOLD} - Get labels to JSON: '
911 f'{Colors.RESET}', end='')
912 labels_list: List[Dict[str, str | None]] = []
913 for label in labels_objects:
914 labels_list += [{
915 'name': label.name,
916 'description': label.description,
917 'text_color': label.text_color,
918 'color': label.color,
919 'priority': label.priority,
920 }]
921 print(json.dumps(labels_list))
922 Platform.flush()
924 # Set project labels
925 elif options.set_project_labels:
926 print(f'{Colors.BOLD} - Set labels from JSON: '
927 f'{Colors.RESET}', end='')
928 if not project.archived:
929 labels_names: List[str] = []
930 labels_json = json.loads(options.set_project_labels)
931 for label_json in labels_json:
932 label_set = gitlab.project_set_label(
933 criteria,
934 name=label_json['name'],
935 description=label_json['description'],
936 text_color=label_json['text_color'],
937 color=label_json['color'],
938 priority=label_json['priority'],
939 )
940 labels_names += [label_set.name]
941 print(f'{Colors.CYAN}{", ".join(labels_names)}'
942 f'{Colors.RESET}')
943 else:
944 print(f'{Colors.RED}Archived project are read-only'
945 f'{Colors.RESET}')
946 Platform.flush()
948 # Dump project object
949 if options.dump:
950 print(' ')
951 print(project.to_json())
953 # Footer
954 print(' ')
955 Platform.flush()
957 # Result
958 return Entrypoint.Result.SUCCESS