Coverage for src/gitlabracadabra/objects/project.py: 80%
116 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-14 23:10 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-14 23:10 +0200
1#
2# Copyright (C) 2019-2025 Mathieu Parent <math.parent@gmail.com>
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Lesser General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
17import logging
18from http import HTTPStatus
19from typing import ClassVar
21from gitlab.exceptions import GitlabListError
23from gitlabracadabra.gitlab.access_levels import access_level_value
24from gitlabracadabra.mixins.boards import BoardsMixin
25from gitlabracadabra.mixins.groups import GroupsMixin
26from gitlabracadabra.mixins.image_mirrors import ImageMirrorsMixin
27from gitlabracadabra.mixins.labels import LabelsMixin
28from gitlabracadabra.mixins.members import MembersMixin
29from gitlabracadabra.mixins.milestones import MilestonesMixin
30from gitlabracadabra.mixins.mirrors import MirrorsMixin
31from gitlabracadabra.mixins.package_mirrors import PackageMirrorsMixin
32from gitlabracadabra.mixins.pipeline_schedules import PipelineSchedulesMixin
33from gitlabracadabra.mixins.protected_branches import ProtectedBranchesMixin
34from gitlabracadabra.mixins.rename_branches import RenameBranchesMixin
35from gitlabracadabra.mixins.variables import VariablesMixin
36from gitlabracadabra.mixins.webhooks import WebhooksMixin
37from gitlabracadabra.objects.object import GitLabracadabraObject
39logger = logging.getLogger(__name__)
42class GitLabracadabraProject(
43 BoardsMixin,
44 GroupsMixin,
45 ImageMirrorsMixin,
46 LabelsMixin,
47 MembersMixin,
48 MilestonesMixin,
49 MirrorsMixin,
50 PackageMirrorsMixin,
51 PipelineSchedulesMixin,
52 ProtectedBranchesMixin,
53 RenameBranchesMixin,
54 VariablesMixin,
55 WebhooksMixin,
56 GitLabracadabraObject,
57):
58 EXAMPLE_YAML_HEADER: ClassVar[str] = "mygroup/myproject:\n"
59 DOC: ClassVar[list[str]] = [
60 "# Project lifecycle",
61 "gitlab_id",
62 "create_object",
63 "delete_object",
64 "initialize_with_readme",
65 "repository_object_format",
66 "# Manage",
67 "## Members",
68 "members",
69 "unknown_members",
70 "groups",
71 "unknown_groups",
72 "## Labels",
73 "labels",
74 "unknown_labels",
75 "# Plan",
76 "## Issue boards",
77 "boards",
78 "unknown_boards",
79 "unknown_board_lists",
80 "## Milestones",
81 "milestones",
82 "unknown_milestones",
83 # '## Iterations',
84 "# Code",
85 "## Branches",
86 "branches",
87 "rename_branches",
88 "# Build",
89 "## Pipeline schedules",
90 "pipeline_schedules",
91 "unknown_pipeline_schedules",
92 "unknown_pipeline_schedule_variables",
93 "# Settings",
94 "## General Settings",
95 "### Naming, description, topics",
96 "name",
97 "description",
98 "topics",
99 # 'avatar', # FIXME: Gitlabracadabra
100 "### Visibility, project features, permissions",
101 "visibility",
102 "request_access_enabled",
103 "issues_access_level",
104 # 'cve_id_request_enabled', # FIXME: GitLab
105 "repository_access_level",
106 "merge_requests_access_level",
107 "forking_access_level",
108 "lfs_enabled",
109 "builds_access_level",
110 "container_registry_access_level",
111 "analytics_access_level",
112 "requirements_access_level",
113 "security_and_compliance_access_level",
114 "wiki_access_level",
115 "snippets_access_level",
116 "packages_enabled",
117 "model_experiments_access_level",
118 "model_registry_access_level",
119 "pages_access_level",
120 "monitor_access_level",
121 "environments_access_level",
122 "feature_flags_access_level",
123 "infrastructure_access_level",
124 "releases_access_level",
125 # 'duo_features_enabled', # FIXME: Gitlab
126 "emails_enabled",
127 # 'show_diff_preview_in_email', # FIXME: Gitlab
128 # 'show_default_award_emojis', # FIXME: Gitlab
129 # 'ci_resources_enabled', # FIXME: Gitlab
130 # '### Badges',
131 "### Default description template for issues",
132 "issues_template",
133 "### Service Desk",
134 "service_desk_enabled",
135 # 'service_desk_...', # FIXME: Gitlab
136 "### Advanced",
137 "archived",
138 # '## Integrations', # FIXME
139 "## Webhooks",
140 "webhooks",
141 "unknown_webhooks",
142 "## Repository",
143 "### Branch defaults",
144 "default_branch",
145 "autoclose_referenced_issues",
146 "issue_branch_template",
147 # '### Branch Rules', # FIXME: ...
148 # '### Push Rules', # FIXME: ...
149 "### Mirroring repositories",
150 "mirror",
151 "import_url",
152 "mirror_user_id",
153 "mirror_overwrites_diverged_branches",
154 "mirror_trigger_builds",
155 "only_mirror_protected_branches",
156 "### Protected Branches",
157 "protected_branches",
158 "unknown_protected_branches",
159 "### Protected Tags",
160 "protected_tags",
161 "unknown_protected_tags",
162 # '### Deploy Keys', # FIXME: ...
163 # '### Deploy Tokens', # FIXME: ...
164 "## Merge requests",
165 "### Merge requests",
166 "merge_method",
167 "merge_pipelines_enabled",
168 "merge_trains_enabled",
169 # 'merge_trains_skip_train_allowed', # FIXME: GitLab
170 "resolve_outdated_diff_discussions",
171 "printing_merge_request_link_enabled",
172 "remove_source_branch_after_merge",
173 "squash_option",
174 "only_allow_merge_if_pipeline_succeeds",
175 "allow_merge_on_skipped_pipeline",
176 "only_allow_merge_if_all_discussions_are_resolved",
177 "only_allow_merge_if_all_status_checks_passed",
178 "suggestion_commit_message",
179 "merge_commit_template",
180 "squash_commit_template",
181 "merge_requests_template",
182 "### Merge request approvals", # FIXME: ...
183 "approvals_before_merge",
184 "## CI / CD Settings",
185 "### General pipelines",
186 "public_jobs",
187 "auto_cancel_pending_pipelines",
188 "ci_forward_deployment_enabled",
189 "ci_forward_deployment_rollback_allowed",
190 "ci_separated_caches",
191 "ci_restrict_pipeline_cancellation_role",
192 "ci_config_path",
193 "build_git_strategy",
194 "ci_default_git_depth",
195 "build_timeout",
196 "ci_allow_fork_pipelines_to_run_in_parent_project",
197 "### Auto DevOps",
198 "auto_devops_enabled",
199 "auto_devops_deploy_strategy",
200 "### Protected Environments",
201 # FIXME: ...
202 "allow_pipeline_trigger_approve_deployment",
203 "### Runners",
204 "shared_runners_enabled",
205 "group_runners_enabled",
206 "### Artifacts",
207 "keep_latest_artifact",
208 "### Variables",
209 "variables",
210 "unknown_variables",
211 # '### Pipeline trigger tokens', # FIXME
212 # '### Automatic deployment rollbacks', # FIXME
213 # 'auto_rollback_enabled', # FIXME: GitLab
214 # '### Deploy freezes', # FIXME
215 # '### Job token permissions', # FIXME
216 # '### Secure files', # FIXME
217 # '### Pipeline subscriptions', # FIXME
218 "## Packages and registries",
219 "### Cleanup policies",
220 "container_expiration_policy",
221 "# Mirroring repositories, packages and container images",
222 "mirrors",
223 "package_mirrors",
224 "image_mirrors",
225 "# Deprecated",
226 "build_coverage_regex",
227 "container_registry_enabled",
228 "emails_disabled",
229 "issues_enabled",
230 "jobs_enabled",
231 "merge_requests_enabled",
232 "public_builds",
233 "snippets_enabled",
234 "tag_list",
235 "wiki_enabled",
236 ]
237 SCHEMA: ClassVar[dict] = {
238 "$schema": "http://json-schema.org/draft-04/schema#",
239 "title": "Project",
240 "type": "object",
241 "properties": {
242 "gitlab_id": {
243 "type": "string",
244 "description": "GitLab id",
245 "_example": "gitlab",
246 "_doc_link": "action_file.md#gitlab_id",
247 },
248 "create_object": {
249 "type": "boolean",
250 "description": "Create object if it does not exists",
251 },
252 "delete_object": {
253 "type": "boolean",
254 "description": "Delete object if it exists",
255 },
256 # From https://docs.gitlab.com/ee/api/projects.html#create-project
257 # and https://docs.gitlab.com/ee/api/projects.html#edit-project
258 "initialize_with_readme": {
259 "type": "boolean",
260 "description": "false by default",
261 },
262 "repository_object_format": {
263 "type": "string",
264 "description": "Repository object format",
265 "enum": ["sha1", "sha256"],
266 },
267 # From https://docs.gitlab.com/ee/api/members.html#add-a-member-to-a-group-or-project
268 # FIXME: expires_at not supported
269 "members": {
270 "type": "object",
271 "description": "Members",
272 "additionalProperties": {
273 "type": "string",
274 "description": "The permissions level to grant the member.",
275 "enum": ["guest", "reporter", "developer", "maintainer", "owner"],
276 },
277 "_example": (
278 "\n"
279 " foo: developer\n"
280 " bar: maintainer # one of guest, reporter, developer, maintainer, owner\n"
281 ),
282 },
283 "unknown_members": { # GitLabracadabra
284 "type": "string",
285 "description": "What to do with unknown members (`warn` by default).",
286 "enum": ["warn", "delete", "remove", "ignore", "skip"],
287 },
288 # From https://docs.gitlab.com/ee/api/projects.html#share-project-with-group_access_level
289 # and https://docs.gitlab.com/ee/api/projects.html#delete-a-shared-project-link-within-a-group
290 # FIXME: expires_at not supported
291 "groups": {
292 "type": "object",
293 "description": "Groups",
294 "additionalProperties": {
295 "type": "string",
296 "description": "The permissions level to grant the group.",
297 "enum": ["guest", "reporter", "developer", "maintainer"],
298 },
299 "_example": (
300 "\n"
301 " group/foo: guest\n"
302 " group/bar: reporter # one of guest, reporter, developer, maintainer\n"
303 ),
304 },
305 "unknown_groups": { # GitLabracadabra
306 "type": "string",
307 "description": "What to do with unknown groups (`warn` by default).",
308 "enum": ["warn", "delete", "remove", "ignore", "skip"],
309 },
310 # From https://docs.gitlab.com/ee/api/labels.html#create-a-new-label
311 "labels": {
312 "type": "array",
313 "description": "The list of project's labels",
314 "items": {
315 "type": "object",
316 "properties": {
317 "name": {
318 "type": "string",
319 "description": "The name of the label.",
320 },
321 "color": {
322 "type": "string",
323 "description": "The color of the label.",
324 },
325 "description": {
326 "type": "string",
327 "description": "The description of the label.",
328 },
329 "priority": {
330 "type": "integer",
331 "description": "The priority of the label.",
332 },
333 },
334 "required": ["name"], # color not required to allow priority override
335 "additionalProperties": False,
336 },
337 "uniqueItems": True,
338 "_example": (
339 "\n"
340 " - name: critical\n"
341 " priority: 0\n"
342 " - name: bug\n"
343 " priority: 1\n"
344 " - name: confirmed\n"
345 " priority: 2\n"
346 ),
347 },
348 "unknown_labels": { # GitLabracadabra
349 "type": "string",
350 "description": "What to do with unknown labels (`warn` by default).",
351 "enum": ["warn", "delete", "remove", "ignore", "skip"],
352 },
353 # From https://docs.gitlab.com/ee/api/boards.html
354 "boards": {
355 "type": "array",
356 "description": "The list of project's boards",
357 "items": {
358 "type": "object",
359 "properties": {
360 "name": {
361 "type": "string",
362 "description": "The name of the board.",
363 },
364 "old_name": {
365 "type": "string",
366 "description": "The previous name of the board.",
367 },
368 "hide_backlog_list": {
369 "type": "boolean",
370 "description": "Hide the Open list",
371 },
372 "hide_closed_list": {
373 "type": "boolean",
374 "description": "Hide the Closed list",
375 },
376 "lists": {
377 "type": "array",
378 "description": "Ordered list of labels",
379 "items": {
380 "type": "object",
381 "properties": {
382 "label": {
383 "type": "string",
384 "description": "The name of a label",
385 },
386 },
387 },
388 },
389 "unknown_lists": { # GitLabracadabra
390 "type": "string",
391 "description": (
392 "What to do with unknown board lists " "(Value of `unknown_board_lists` by default)."
393 ),
394 "enum": ["warn", "delete", "remove", "ignore", "skip"],
395 },
396 },
397 "required": ["name"],
398 "additionalProperties": False,
399 },
400 "uniqueItems": True,
401 "_example": (
402 "\n"
403 " - name: My Board\n"
404 " # old_name: Development # Use this to rename a board\n"
405 " hide_backlog_list: false\n"
406 " hide_closed_list: false\n"
407 " lists:\n"
408 " - label: TODO\n"
409 " - label: WIP\n"
410 ),
411 },
412 "unknown_boards": { # GitLabracadabra
413 "type": "string",
414 "description": "What to do with unknown boards (`warn` by default).",
415 "enum": ["warn", "delete", "remove", "ignore", "skip"],
416 },
417 "unknown_board_lists": { # GitLabracadabra
418 "type": "string",
419 "description": "What to do with unknown board lists (`delete` by default).",
420 "enum": ["warn", "delete", "remove", "ignore", "skip"],
421 },
422 # From https://docs.gitlab.com/ee/api/milestones.html#edit-milestone
423 "milestones": {
424 "type": "array",
425 "description": "The list of project's milestones",
426 "items": {
427 "type": "object",
428 "properties": {
429 "title": {
430 "type": "string",
431 "description": "The title of a milestone",
432 },
433 "description": {
434 "type": "string",
435 "description": "The description of a milestone",
436 },
437 "due_date": {
438 "type": "string",
439 "description": "The due date of the milestone",
440 "pattern": "^(\\d{4}-\\d{2}-\\d{2})?$",
441 },
442 "start_date": {
443 "type": "string",
444 "description": "The start date of the milestone",
445 "pattern": "^(\\d{4}-\\d{2}-\\d{2})?$",
446 },
447 "state": {
448 "type": "string",
449 "description": "The state event of the milestone",
450 "enum": ["closed", "active"],
451 },
452 },
453 "required": ["title"],
454 "additionalProperties": False,
455 },
456 "uniqueItems": True,
457 "_example": (
458 "\n"
459 " - title: '1.0'\n"
460 " description: Version 1.0\n"
461 " due_date: '2021-01-23' # Quotes are mandatory\n"
462 " start_date: '2020-01-23' # Quotes are mandatory\n"
463 " state: active # or closed\n"
464 ),
465 },
466 "unknown_milestones": { # GitLabracadabra
467 "type": "string",
468 "description": "What to do with unknown milestones (`warn` by default).",
469 "enum": ["warn", "delete", "remove", "ignore", "skip"],
470 },
471 # From https://docs.gitlab.com/ee/api/branches.html#create-repository-branch
472 "branches": {
473 "type": "array",
474 "description": "The list of branches for a project. " "Branches are created in order",
475 "items": {
476 "type": "string",
477 },
478 "uniqueItems": True,
479 "_example": ("\n" " - main\n" " - develop"),
480 },
481 "rename_branches": {
482 "type": "array",
483 "description": "Rename branches of a project. "
484 "Rename pairs (old_name: new_name) are processed in order",
485 "items": {
486 "type": "object",
487 "additionalProperties": {
488 "type": "string",
489 "description": "The new branch name.",
490 },
491 "minProperties": 1,
492 "maxProperties": 1,
493 },
494 "uniqueItems": True,
495 "_example": (
496 "\n"
497 " - old_name: new_name\n"
498 " # To Rename consecutive branches:\n"
499 " - branch2: branch3\n"
500 " - branch1: branch2"
501 ),
502 },
503 # From https://docs.gitlab.com/ee/api/pipeline_schedules.html#create-a-new-pipeline-schedule
504 "pipeline_schedules": {
505 "type": "array",
506 "description": "The list of project's pipeline schedules",
507 "items": {
508 "type": "object",
509 "properties": {
510 "description": {
511 "type": "string",
512 "description": "The description of pipeline schedule",
513 "pattern": "[a-zA-Z0-9_]+",
514 },
515 "ref": {
516 "type": "string",
517 "description": "The branch/tag name will be triggered",
518 },
519 "cron": {
520 "type": "string",
521 "description": (
522 "The cron (e.g. `0 1 * * *`) " "([Cron syntax](https://en.wikipedia.org/wiki/Cron))"
523 ),
524 },
525 "cron_timezone": {
526 "type": "string",
527 "description": (
528 "The timezone supported by `ActiveSupport::TimeZone` "
529 "(e.g. `Pacific Time (US & Canada)`) (default: `'UTC'`)"
530 ),
531 },
532 "active": {
533 "type": "boolean",
534 "description": "The activation of pipeline schedule",
535 },
536 # From https://docs.gitlab.com/ee/api/pipeline_schedules.html
537 # #create-a-new-pipeline-schedule-variable
538 "variables": {
539 "type": "array",
540 "description": "The list of project's variables",
541 "items": {
542 "type": "object",
543 "properties": {
544 "key": {
545 "type": "string",
546 "description": "The key of a variable",
547 "pattern": "[a-zA-Z0-9_]+",
548 },
549 "value": {
550 "type": "string",
551 "description": "The value of a variable",
552 },
553 "variable_type": {
554 "type": "string",
555 "description": (
556 "The type of a variable. " "Available types are: env_var (default) and file"
557 ),
558 "enum": ["env_var", "file"],
559 },
560 },
561 "required": ["key", "value"],
562 "additionalProperties": False,
563 },
564 "uniqueItems": True,
565 },
566 "unknown_variables": { # GitLabracadabra
567 "type": "string",
568 "description": (
569 "What to do with unknown pipeline schedule variables "
570 "(Value of `unknown_pipeline_schedule_variables` by default)."
571 ),
572 "enum": ["warn", "delete", "remove", "ignore", "skip"],
573 },
574 },
575 "required": ["description", "ref", "cron"],
576 "additionalProperties": False,
577 },
578 "uniqueItems": True,
579 "_example": (
580 "\n"
581 " - description: Build packages\n"
582 " ref: main\n"
583 " cron: '0 1 * * 5'\n"
584 " # cron_timezone: UTC\n"
585 " # active: true\n"
586 " variables:\n"
587 " - key: MY_VAR\n"
588 " value: my value\n"
589 " # variable_type: env_var # or file\n"
590 " # unknown_variables: warn # one of warn, delete, remove, ignore, skip\n"
591 ),
592 },
593 "unknown_pipeline_schedules": { # GitLabracadabra
594 "type": "string",
595 "description": "What to do with unknown pipeline schedules (`warn` by default).",
596 "enum": ["warn", "delete", "remove", "ignore", "skip"],
597 },
598 "unknown_pipeline_schedule_variables": { # GitLabracadabra
599 "type": "string",
600 "description": "What to do with unknown pipeline schedule variables (`warn` by default).",
601 "enum": ["warn", "delete", "remove", "ignore", "skip"],
602 },
603 "name": {
604 "type": "string",
605 "description": "Project name",
606 },
607 # 'path': {
608 # 'type': 'string',
609 # 'description': 'Repository name for new project. '
610 # 'Generated based on name if not provided (generated lowercased with dashes).',
611 # },
612 "description": {
613 "type": "string",
614 "description": "Project description",
615 "_example": "|-\n 🧹 GitLabracadabra 🧙\n\n :alembic: Adds some magic to GitLab :crystal\\_ball:",
616 },
617 "topics": {
618 "type": "array",
619 "description": "Topics",
620 "items": {
621 "type": "string",
622 },
623 "uniqueItems": True,
624 "_example": "[GitLab, API, YAML]",
625 },
626 # 'avatar': {
627 # 'type': 'string',
628 # 'description': 'Project avatar',
629 # },
630 "visibility": {
631 "type": "string",
632 "description": "Project visibility",
633 "enum": ["private", "internal", "public"],
634 },
635 "request_access_enabled": {
636 "type": "boolean",
637 "description": "Allow users to request access",
638 },
639 "issues_access_level": {
640 "type": "string",
641 "description": "Set visibility of issues.",
642 "enum": ["disabled", "private", "enabled"],
643 },
644 "repository_access_level": {
645 "type": "string",
646 "description": "Set visibility of repository.",
647 "enum": ["disabled", "private", "enabled"],
648 },
649 "merge_requests_access_level": {
650 "type": "string",
651 "description": "Set visibility of merge requests.",
652 "enum": ["disabled", "private", "enabled"],
653 },
654 "forking_access_level": {
655 "type": "string",
656 "description": "Set visibility of forks.",
657 "enum": ["disabled", "private", "enabled"],
658 },
659 "lfs_enabled": {
660 "type": "boolean",
661 "description": "Enable LFS",
662 },
663 "builds_access_level": {
664 "type": "string",
665 "description": "Set visibility of pipelines.",
666 "enum": ["disabled", "private", "enabled"],
667 },
668 "container_registry_access_level": {
669 "type": "string",
670 "description": "Set visibility of container registry.",
671 "enum": ["disabled", "private", "enabled"],
672 },
673 "analytics_access_level": {
674 "type": "string",
675 "description": "Set visibility of analytics.",
676 "enum": ["disabled", "private", "enabled"],
677 },
678 "requirements_access_level": {
679 "type": "string",
680 "description": "Set visibility of requirements management.",
681 "enum": ["disabled", "private", "enabled"],
682 },
683 "security_and_compliance_access_level": {
684 "type": "string",
685 "description": "Set visibility of security and compliance.",
686 "enum": ["disabled", "private", "enabled"],
687 },
688 "wiki_access_level": {
689 "type": "string",
690 "description": "Set visibility of wiki.",
691 "enum": ["disabled", "private", "enabled"],
692 },
693 "snippets_access_level": {
694 "type": "string",
695 "description": "Set visibility of snippets.",
696 "enum": ["disabled", "private", "enabled"],
697 },
698 "packages_enabled": {
699 "type": "boolean",
700 "description": "Enable or disable packages repository feature",
701 },
702 "model_experiments_access_level": {
703 "type": "string",
704 "description": "Set visibility of machine learning model experiments.",
705 "enum": ["disabled", "private", "enabled"],
706 },
707 "model_registry_access_level": {
708 "type": "string",
709 "description": "Set visibility of machine learning model registry.",
710 "enum": ["disabled", "private", "enabled"],
711 },
712 "pages_access_level": {
713 "type": "string",
714 "description": "Set visibility of GitLab Pages.",
715 "enum": ["disabled", "private", "enabled", "public"],
716 },
717 "monitor_access_level": {
718 "type": "string",
719 "description": "Set visibility of application performance monitoring.",
720 "enum": ["disabled", "private", "enabled"],
721 },
722 "environments_access_level": {
723 "type": "string",
724 "description": "Set visibility of environments.",
725 "enum": ["disabled", "private", "enabled"],
726 },
727 "feature_flags_access_level": {
728 "type": "string",
729 "description": "Set visibility of feature flags.",
730 "enum": ["disabled", "private", "enabled"],
731 },
732 "infrastructure_access_level": {
733 "type": "string",
734 "description": "Set visibility of infrastructure management.",
735 "enum": ["disabled", "private", "enabled"],
736 },
737 "releases_access_level": {
738 "type": "string",
739 "description": "Set visibility of releases.",
740 "enum": ["disabled", "private", "enabled"],
741 },
742 "emails_enabled": {
743 "type": "boolean",
744 "description": "Enable email notifications.",
745 },
746 "issues_template": {
747 "type": "string",
748 "description": "Default description for Issues.",
749 },
750 "service_desk_enabled": {
751 "type": "boolean",
752 "description": "Enable or disable Service Desk feature.",
753 },
754 # https://docs.gitlab.com/ee/api/projects.html#archive-a-project
755 # https://docs.gitlab.com/ee/api/projects.html#unarchive-a-project
756 "archived": {
757 "type": "boolean",
758 "description": "Archive or unarchive project",
759 },
760 # From https://docs.gitlab.com/ee/api/projects.html#hooks
761 "webhooks": {
762 "type": "array",
763 "description": "The list of project's webhooks",
764 "items": {
765 "type": "object",
766 "properties": {
767 "url": {
768 "type": "string",
769 "description": "The hook URL",
770 },
771 "push_events": {
772 "type": "boolean",
773 "description": "Trigger hook on push events",
774 },
775 "push_events_branch_filter": {
776 "type": "string",
777 "description": "Trigger hook on push events for matching branches only",
778 },
779 "issues_events": {
780 "type": "boolean",
781 "description": "Trigger hook on issues events",
782 },
783 "confidential_issues_events": {
784 "type": "boolean",
785 "description": "Trigger hook on confidential issues events",
786 },
787 "merge_requests_events": {
788 "type": "boolean",
789 "description": "Trigger hook on merge requests events",
790 },
791 "tag_push_events": {
792 "type": "boolean",
793 "description": "Trigger hook on tag push events",
794 },
795 "note_events": {
796 "type": "boolean",
797 "description": "Trigger hook on note events",
798 },
799 "confidential_note_events": {
800 "type": "boolean",
801 "description": "Trigger hook on confidential note events",
802 },
803 "job_events": {
804 "type": "boolean",
805 "description": "Trigger hook on job events",
806 },
807 "pipeline_events": {
808 "type": "boolean",
809 "description": "Trigger hook on pipeline events",
810 },
811 "wiki_page_events": {
812 "type": "boolean",
813 "description": "Trigger hook on wiki events",
814 },
815 "enable_ssl_verification": {
816 "type": "boolean",
817 "description": "Do SSL verification when triggering the hook",
818 },
819 "token": {
820 "type": "string",
821 "description": (
822 "Secret token to validate received payloads; "
823 "this will not be returned in the response"
824 ),
825 },
826 },
827 "required": ["url"],
828 "additionalProperties": False,
829 },
830 "uniqueItems": True,
831 "_example": (
832 "\n"
833 " - url: http://example.com/api/trigger\n"
834 " push_events: true\n"
835 " push_events_branch_filter: ''\n"
836 " issues_events: true\n"
837 " confidential_issues_events: true\n"
838 " merge_requests_events: true\n"
839 " tag_push_events: true\n"
840 " note_events: true\n"
841 " confidential_note_events: true\n"
842 " job_events: true\n"
843 " pipeline_events: true\n"
844 " wiki_page_events: true\n"
845 " enable_ssl_verification: true\n"
846 " # token: T0k3N\n"
847 ),
848 },
849 "unknown_webhooks": { # GitLabracadabra
850 "type": "string",
851 "description": "What to do with unknown webhooks (`warn` by default).",
852 "enum": ["warn", "delete", "remove", "ignore", "skip"],
853 },
854 "default_branch": {
855 "type": "string",
856 "description": "The default branch name",
857 },
858 "autoclose_referenced_issues": {
859 "type": "boolean",
860 "description": "Set whether auto-closing referenced issues on default branch",
861 },
862 "issue_branch_template": {
863 "type": "string",
864 "description": "Template used to suggest names for branches created from issues.",
865 },
866 # From https://docs.gitlab.com/ee/api/projects.html#edit-project
867 "mirror": {
868 "type": "boolean",
869 "description": "Enables pull mirroring in a project",
870 },
871 "import_url": {
872 "type": "string",
873 "description": "URL to import repository from",
874 },
875 "mirror_user_id": {
876 "type": "integer",
877 "description": "User responsible for all the activity surrounding a pull mirror event",
878 },
879 "mirror_overwrites_diverged_branches": {
880 "type": "boolean",
881 "description": "Pull mirror overwrites diverged branches",
882 },
883 "mirror_trigger_builds": {
884 "type": "boolean",
885 "description": "Pull mirroring triggers builds",
886 },
887 "only_mirror_protected_branches": {
888 "type": "boolean",
889 "description": "Only mirror protected branches",
890 },
891 # From https://docs.gitlab.com/ee/api/protected_branches.html#protect-repository-branches
892 "protected_branches": {
893 "type": "object",
894 "description": "Protected branches",
895 "additionalProperties": {
896 "type": "object",
897 "properties": {
898 "merge_access_level": {
899 "type": "string",
900 "description": "Access levels allowed to merge.",
901 "enum": ["noone", "developer", "maintainer", "admin"],
902 },
903 "allowed_to_merge": {
904 "type": "array",
905 "description": "",
906 "items": {
907 "oneOf": [
908 {
909 "type": "object",
910 "description": "Role",
911 "properties": {
912 "role": {
913 "type": "string",
914 "description": "Role name",
915 "enum": ["noone", "developer", "maintainer", "admin"],
916 },
917 },
918 "additionalProperties": False,
919 },
920 {
921 "type": "object",
922 "description": "User",
923 "properties": {
924 "user": {
925 "type": "string",
926 "description": "User name",
927 },
928 },
929 "additionalProperties": False,
930 },
931 {
932 "type": "object",
933 "description": "Group",
934 "properties": {
935 "group": {
936 "type": "string",
937 "description": "Group full path",
938 },
939 },
940 "additionalProperties": False,
941 },
942 ],
943 },
944 },
945 "push_access_level": {
946 "type": "string",
947 "description": "Access levels allowed to push.",
948 "enum": ["noone", "developer", "maintainer", "admin"],
949 },
950 "allowed_to_push": {
951 "type": "array",
952 "description": "",
953 "items": {
954 "oneOf": [
955 {
956 "type": "object",
957 "description": "Role",
958 "properties": {
959 "role": {
960 "type": "string",
961 "description": "Role name",
962 "enum": ["noone", "developer", "maintainer", "admin"],
963 },
964 },
965 "additionalProperties": False,
966 },
967 {
968 "type": "object",
969 "description": "User",
970 "properties": {
971 "user": {
972 "type": "string",
973 "description": "User name",
974 },
975 },
976 "additionalProperties": False,
977 },
978 {
979 "type": "object",
980 "description": "Group",
981 "properties": {
982 "group": {
983 "type": "string",
984 "description": "Group full path",
985 },
986 },
987 "additionalProperties": False,
988 },
989 {
990 "type": "object",
991 "description": "Deploy Key",
992 "properties": {
993 "deploy_key": {
994 "type": "string",
995 "description": "Deploy key Title",
996 },
997 },
998 "additionalProperties": False,
999 },
1000 ],
1001 },
1002 },
1003 "allow_force_push": {
1004 "type": "boolean",
1005 "description": "When enabled, members who can push to this branch can also force push.",
1006 },
1007 "code_owner_approval_required": {
1008 "type": "boolean",
1009 "description": (
1010 "Prevent pushes to this branch " "if it matches an item in the CODEOWNERS file."
1011 ),
1012 },
1013 "allowed_to_unprotect": {
1014 "type": "array",
1015 "description": "",
1016 "items": {
1017 "oneOf": [
1018 {
1019 "type": "object",
1020 "description": "Role",
1021 "properties": {
1022 "role": {
1023 "type": "string",
1024 "description": "Role name",
1025 "enum": ["developer", "maintainer", "admin"],
1026 },
1027 },
1028 "additionalProperties": False,
1029 },
1030 {
1031 "type": "object",
1032 "description": "User",
1033 "properties": {
1034 "user": {
1035 "type": "string",
1036 "description": "User name",
1037 },
1038 },
1039 "additionalProperties": False,
1040 },
1041 {
1042 "type": "object",
1043 "description": "Group",
1044 "properties": {
1045 "group": {
1046 "type": "string",
1047 "description": "Group full path",
1048 },
1049 },
1050 "additionalProperties": False,
1051 },
1052 ],
1053 },
1054 },
1055 },
1056 },
1057 "_example": (
1058 "\n"
1059 " main:\n"
1060 " allowed_to_merge::\n"
1061 " - role: developer # one of noone, developer, maintainer, admin\n"
1062 " - user: my_username # EE only\n"
1063 " - group: my_group # EE only\n"
1064 " allowed_to_push::\n"
1065 " - role: noone # one of noone, developer, maintainer, admin\n"
1066 " - user: my_username # EE only\n"
1067 " - group: my_group # EE only\n"
1068 " - deploy_key: Deploy Key Title # EE only\n"
1069 " allow_force_push: false\n"
1070 " code_owner_approval_required: false # EE only\n"
1071 " allowed_to_unprotect: # EE only\n"
1072 " - role: maintainer # one of developer, maintainer, admin\n"
1073 " - user: my_username\n"
1074 " - group: my_group\n"
1075 " develop:\n"
1076 " merge_access_level: developer\n"
1077 " push_access_level: developer\n"
1078 ),
1079 },
1080 "unknown_protected_branches": { # GitLabracadabra
1081 "type": "string",
1082 "description": "What to do with unknown protected branches (`warn` by default).",
1083 "enum": ["warn", "delete", "remove", "ignore", "skip"],
1084 },
1085 # From https://docs.gitlab.com/ee/api/protected_tags.html#protect-repository-tags
1086 "protected_tags": {
1087 "type": "object",
1088 "description": "Protected tags",
1089 "additionalProperties": {
1090 "type": "string",
1091 "description": "Access levels allowed to create (defaults: maintainer access level)",
1092 "enum": ["noone", "developer", "maintainer"],
1093 },
1094 "_example": (
1095 "\n"
1096 " v*: maintainer # one of noone, developer, maintainer\n"
1097 " *: developer # one of noone, developer, maintainer\n"
1098 ),
1099 },
1100 "unknown_protected_tags": { # GitLabracadabra
1101 "type": "string",
1102 "description": "What to do with unknown protected tags (`warn` by default).",
1103 "enum": ["warn", "delete", "remove", "ignore", "skip"],
1104 },
1105 "merge_method": {
1106 "type": "string",
1107 "description": "Set the merge method used",
1108 "enum": ["merge", "rebase_merge", "ff"],
1109 },
1110 "merge_pipelines_enabled": {
1111 "type": "boolean",
1112 "description": "Enable or disable merged results pipelines.",
1113 },
1114 "merge_trains_enabled": {
1115 "type": "boolean",
1116 "description": "Enable or disable merge trains.",
1117 },
1118 "resolve_outdated_diff_discussions": {
1119 "type": "boolean",
1120 "description": "Automatically resolve merge request diffs discussions on lines changed with a push",
1121 },
1122 "printing_merge_request_link_enabled": {
1123 "type": "boolean",
1124 "description": "Show link to create/view merge request when pushing from the command line",
1125 },
1126 "remove_source_branch_after_merge": {
1127 "type": "boolean",
1128 "description": "Enable Delete source branch option by default for all new merge requests",
1129 },
1130 "squash_option": {
1131 "type": "string",
1132 "description": "Squash option.",
1133 "enum": ["never", "always", "default_on", "default_off"],
1134 },
1135 "only_allow_merge_if_pipeline_succeeds": {
1136 "type": "boolean",
1137 "description": "Set whether merge requests can only be merged with successful jobs",
1138 },
1139 "allow_merge_on_skipped_pipeline": {
1140 "type": "boolean",
1141 "description": "Set whether or not merge requests can be merged with skipped jobs",
1142 },
1143 "only_allow_merge_if_all_discussions_are_resolved": {
1144 "type": "boolean",
1145 "description": "Set whether merge requests can only be merged when all the discussions are resolved",
1146 },
1147 "only_allow_merge_if_all_status_checks_passed": {
1148 "type": "boolean",
1149 "description": (
1150 "Indicates that merges of merge requests" "should be blocked unless all status checks have passed."
1151 ),
1152 },
1153 "suggestion_commit_message": {
1154 "type": "string",
1155 "description": "The commit message used to apply merge request suggestions",
1156 },
1157 "merge_commit_template": {
1158 "type": "string",
1159 "description": "Template used to create merge commit message in merge requests.",
1160 },
1161 "squash_commit_template": {
1162 "type": "string",
1163 "description": "Template used to create squash commit message in merge requests.",
1164 },
1165 "merge_requests_template": {
1166 "type": "string",
1167 "description": "Default description for merge requests.",
1168 },
1169 "approvals_before_merge": {
1170 "type": "integer",
1171 "description": "How many approvers should approve merge request by default",
1172 "multipleOf": 1,
1173 "minimum": 0,
1174 },
1175 # From https://docs.gitlab.com/ee/api/projects.html#edit-project
1176 "public_jobs": {
1177 "type": "boolean",
1178 "description": "If true, jobs can be viewed by non-project members.",
1179 },
1180 "auto_cancel_pending_pipelines": {
1181 "type": "string",
1182 "description": "Auto-cancel pending pipelines",
1183 "enum": ["enabled", "disabled"],
1184 },
1185 "ci_forward_deployment_enabled": {
1186 "type": "boolean",
1187 "description": "Enable or disable prevent outdated deployment jobs.",
1188 },
1189 "ci_forward_deployment_rollback_allowed": {
1190 "type": "boolean",
1191 "description": "Enable or disable allow job retries for rollback deployments.",
1192 },
1193 "ci_separated_caches": {
1194 "type": "boolean",
1195 "description": "Set whether or not caches should be separated by branch protection status.",
1196 },
1197 "ci_restrict_pipeline_cancellation_role": {
1198 "type": "string",
1199 "description": "Set the role required to cancel a pipeline or job.",
1200 "enum": ["developer", "maintainer", "no_one"],
1201 },
1202 "ci_config_path": {
1203 "type": "string",
1204 "description": "The path to CI config file",
1205 "_example": "debian/salsa-ci.yml",
1206 },
1207 "build_git_strategy": {
1208 "type": "string",
1209 "description": "The Git strategy",
1210 "enum": ["fetch", "clone"],
1211 },
1212 "ci_default_git_depth": {
1213 "type": "integer",
1214 "description": "Default number of revisions for shallow cloning",
1215 },
1216 "build_timeout": {
1217 "type": "integer",
1218 "description": "The maximum amount of time in minutes that a job is able run (in seconds)",
1219 },
1220 "ci_allow_fork_pipelines_to_run_in_parent_project": {
1221 "type": "boolean",
1222 "description": (
1223 "Enable or disable running pipelines in the parent project " "for merge requests from forks."
1224 ),
1225 },
1226 "auto_devops_enabled": {
1227 "type": "boolean",
1228 "description": "Enable Auto DevOps for this project",
1229 },
1230 "auto_devops_deploy_strategy": {
1231 "type": "string",
1232 "description": "Auto Deploy strategy",
1233 "enum": ["continuous", "manual", "timed_incremental"],
1234 },
1235 "allow_pipeline_trigger_approve_deployment": {
1236 "type": "boolean",
1237 "description": "Set whether or not a pipeline triggerer is allowed to approve deployments.",
1238 },
1239 "shared_runners_enabled": {
1240 "type": "boolean",
1241 "description": "Enable shared runners for this project.",
1242 },
1243 "group_runners_enabled": {
1244 "type": "boolean",
1245 "description": "Enable group runners for this project.",
1246 },
1247 "keep_latest_artifact": {
1248 "type": "boolean",
1249 "description": "Disable or enable the ability to keep the latest artifact for this project.",
1250 },
1251 # From https://docs.gitlab.com/ee/api/project_level_variables.html#create-variable
1252 "variables": {
1253 "type": "array",
1254 "description": "The list of project's variables",
1255 "items": {
1256 "type": "object",
1257 "properties": {
1258 "key": {
1259 "type": "string",
1260 "description": "The key of a variable.",
1261 "pattern": "[a-zA-Z0-9_]+",
1262 "maxLength": 255,
1263 },
1264 "value": {
1265 "type": "string",
1266 "description": "The value of a variable.",
1267 },
1268 "description": {
1269 "type": "string",
1270 "description": "The description of the variable.",
1271 },
1272 "variable_type": {
1273 "type": "string",
1274 "description": "The type of a variable. Available types are: env_var (default) and file.",
1275 "enum": ["env_var", "file"],
1276 },
1277 "protected": {
1278 "type": "boolean",
1279 "description": "Whether the variable is protected.",
1280 },
1281 "masked": {
1282 "type": "boolean",
1283 "description": "Whether the variable is masked.",
1284 },
1285 "raw": {
1286 "type": "boolean",
1287 "description": "Whether the variable is treated as a raw string.",
1288 },
1289 "environment_scope": { # Premium+/Silver+
1290 "type": "string",
1291 "description": "The environment_scope of the variable.",
1292 },
1293 },
1294 "required": ["key"],
1295 "additionalProperties": False,
1296 },
1297 "uniqueItems": True,
1298 "_example": (
1299 "\n"
1300 " - key: DAST_DISABLED\n"
1301 " value: '1'\n"
1302 " description: Disabled SAST\n"
1303 " masked: false\n"
1304 " protected: false\n"
1305 " raw: false # Expand variables\n"
1306 " environment_scope: '*'\n"
1307 " variable_type: env_var\n"
1308 ),
1309 },
1310 "unknown_variables": { # GitLabracadabra
1311 "type": "string",
1312 "description": "What to do with unknown variables (`warn` by default).",
1313 "enum": ["warn", "delete", "remove", "ignore", "skip"],
1314 },
1315 # From https://docs.gitlab.com/ee/api/projects.html#edit-project
1316 # container_expiration_policy_attributes
1317 "container_expiration_policy": {
1318 "type": "object",
1319 "description": "Update the image cleanup policy for this project",
1320 "properties": {
1321 "enabled": {
1322 "type": "boolean",
1323 },
1324 "cadence": {
1325 "type": "string",
1326 },
1327 "keep_n": {
1328 "type": "integer",
1329 },
1330 "name_regex_keep": {
1331 "type": "string",
1332 },
1333 "older_than": {
1334 "type": "string",
1335 },
1336 "name_regex_delete": {
1337 "type": "string",
1338 },
1339 },
1340 "required": ["enabled"],
1341 "additionalProperties": False,
1342 "_example": (
1343 "\n"
1344 " enabled: true\n"
1345 " cadence: 7d # 1d, 7d, 14d, 1month, 3month\n"
1346 " keep_n: 10 # 1, 5, 10, 25, 50, 100\n"
1347 " name_regex_keep: '.*main|.*release|release-.*|main-.*'\n"
1348 " older_than: 90d # 7d, 14d, 30d, 90d\n"
1349 " name_regex_delete: '.*'\n"
1350 ),
1351 },
1352 # GitLabracadabra
1353 "mirrors": {
1354 "type": "array",
1355 "description": "The list of project's mirrors",
1356 "items": {
1357 "type": "object",
1358 "properties": {
1359 "url": {
1360 "type": "string",
1361 "description": "Repository URL",
1362 },
1363 "auth_id": {
1364 "type": "string",
1365 "description": "Section from .python-gitlab.cfg for authentication",
1366 },
1367 "direction": {
1368 "type": "string",
1369 "description": "Mirror direction",
1370 "enum": ["pull", "push"],
1371 },
1372 "skip_ci": {
1373 "type": "boolean",
1374 "description": "Skip CI during push",
1375 },
1376 "push_options": {
1377 "type": "array",
1378 "description": "Default push options",
1379 "items": {
1380 "type": "string",
1381 },
1382 },
1383 "branches": {
1384 "type": "array",
1385 "description": "The branches mapping",
1386 "items": {
1387 "type": "object",
1388 "properties": {
1389 "from": {
1390 "type": "string",
1391 "description": "Source name or regular expression",
1392 },
1393 "to": {
1394 "type": "string",
1395 "description": "Destination name or regular expression template",
1396 },
1397 "push_options": {
1398 "type": "array",
1399 "description": "Push options",
1400 "items": {
1401 "type": "string",
1402 },
1403 },
1404 },
1405 "required": ["from"],
1406 "additionalProperties": False,
1407 },
1408 "uniqueItems": True,
1409 },
1410 "tags": {
1411 "type": "array",
1412 "description": "The tags mapping",
1413 "items": {
1414 "type": "object",
1415 "properties": {
1416 "from": {
1417 "type": "string",
1418 "description": "Source name or regular expression",
1419 },
1420 "to": {
1421 "type": "string",
1422 "description": "Destination name or regular expression template",
1423 },
1424 "push_options": {
1425 "type": "array",
1426 "description": "Push options",
1427 "items": {
1428 "type": "string",
1429 },
1430 },
1431 },
1432 "required": ["from"],
1433 "additionalProperties": False,
1434 },
1435 "uniqueItems": True,
1436 },
1437 },
1438 "required": ["url"],
1439 "additionalProperties": False,
1440 },
1441 "uniqueItems": True,
1442 "_example": (
1443 "\n"
1444 " - url: https://gitlab.com/gitlabracadabra/gitlabracadabra.git\n"
1445 " # auth_id: gitlab # Section from .python-gitlab.cfg for authentication\n"
1446 " direction: pull # one of pull, push ; only first pull mirror is processed\n"
1447 " push_options: [ci.skip]\n"
1448 " branches: # if you omit this parameter, all branches are mirrored\n"
1449 " - from: '/wip-.*/'\n"
1450 " to: '' # This will skip those branches\n"
1451 " - from: main\n"
1452 " # to: main # implicitly equal to source branch\n"
1453 " # push_options: [] # inherited by default\n"
1454 " # Using regexps\n"
1455 " - from: '/(.*)/'\n"
1456 " to: 'upstream/\\1'\n"
1457 " tags: # if you omit this parameter, all tags are mirrored\n"
1458 " - from: '/v(.*)/i'\n"
1459 " to: 'upstream-\\1'\n"
1460 " # push_options: [] # inherited by default\n"
1461 " builds_access_level: disabled # If you want to prevent triggering pipelines on push"
1462 ),
1463 "_doc_link": "mirrors.md",
1464 "x-gitlabracadabra-order": 10,
1465 },
1466 "package_mirrors": {
1467 "type": "array",
1468 "description": "Package image mirrors",
1469 "items": {
1470 "oneOf": [
1471 {
1472 "type": "object",
1473 "description": "Raw source",
1474 "properties": {
1475 "enabled": {
1476 "type": "boolean",
1477 },
1478 "raw": {
1479 "type": "object",
1480 "properties": {
1481 "default_url": {
1482 "type": "string",
1483 },
1484 "default_package_name": {
1485 "type": "string",
1486 },
1487 "default_package_version": {
1488 "type": "string",
1489 },
1490 "package_files": {
1491 "type": "array",
1492 "items": {
1493 "type": "object",
1494 "properties": {
1495 "url": {
1496 "type": "string",
1497 },
1498 "package_name": {
1499 "type": "string",
1500 },
1501 "package_version": {
1502 "type": "string",
1503 },
1504 "file_name": {
1505 "type": "string",
1506 },
1507 },
1508 "additionalProperties": False,
1509 },
1510 },
1511 },
1512 "required": ["default_url"],
1513 "additionalProperties": False,
1514 },
1515 },
1516 "additionalProperties": False,
1517 },
1518 {
1519 "type": "object",
1520 "description": "Github source",
1521 "properties": {
1522 "enabled": {
1523 "type": "boolean",
1524 },
1525 "github": {
1526 "type": "object",
1527 "properties": {
1528 "full_name": {
1529 "type": "string",
1530 },
1531 "package_name": {
1532 "type": "string",
1533 },
1534 "tags": {
1535 "type": "array",
1536 "items": {
1537 "type": "string",
1538 },
1539 },
1540 "semver": {
1541 "type": "string",
1542 },
1543 "latest_release": {
1544 "type": "boolean",
1545 },
1546 "tarball": {
1547 "type": "boolean",
1548 },
1549 "zipball": {
1550 "type": "boolean",
1551 },
1552 "assets": {
1553 "type": "array",
1554 "items": {
1555 "type": "string",
1556 },
1557 },
1558 },
1559 "required": ["full_name"],
1560 "additionalProperties": False,
1561 },
1562 },
1563 "additionalProperties": False,
1564 },
1565 {
1566 "type": "object",
1567 "description": "Helm source",
1568 "properties": {
1569 "enabled": {
1570 "type": "boolean",
1571 },
1572 "helm": {
1573 "type": "object",
1574 "properties": {
1575 "repo_url": {
1576 "type": "string",
1577 },
1578 "package_name": {
1579 "type": "string",
1580 },
1581 "versions": {
1582 "type": "array",
1583 "items": {
1584 "type": "string",
1585 },
1586 },
1587 "semver": {
1588 "type": "string",
1589 },
1590 "limit": {
1591 "type": "integer",
1592 },
1593 "channel": {
1594 "type": "string",
1595 },
1596 },
1597 "required": ["repo_url", "package_name"],
1598 "additionalProperties": False,
1599 },
1600 },
1601 "additionalProperties": False,
1602 },
1603 {
1604 "type": "object",
1605 "description": "PyPI source",
1606 "properties": {
1607 "enabled": {
1608 "type": "boolean",
1609 },
1610 "pypi": {
1611 "type": "object",
1612 "properties": {
1613 "index_url": {
1614 "type": "string",
1615 },
1616 "requirements": {
1617 "oneOf": [
1618 {
1619 "type": "string",
1620 },
1621 {
1622 "type": "array",
1623 "items": {
1624 "type": "string",
1625 },
1626 },
1627 ],
1628 },
1629 },
1630 "required": ["requirements"],
1631 "additionalProperties": False,
1632 },
1633 },
1634 "additionalProperties": False,
1635 },
1636 ],
1637 },
1638 "_example": (
1639 "\n"
1640 " - raw:\n"
1641 " default_url: https://download.docker.com/linux/debian/gpg\n"
1642 " default_package_name: docker\n"
1643 " default_package_version: '0'\n"
1644 ),
1645 "_doc_link": "package_mirrors.md",
1646 "x-gitlabracadabra-order": 10,
1647 },
1648 "image_mirrors": {
1649 "type": "array",
1650 "description": "Container image mirrors",
1651 "items": {
1652 "type": "object",
1653 "properties": {
1654 "enabled": {
1655 "type": "boolean",
1656 },
1657 "from": {
1658 "oneOf": [
1659 {
1660 "type": "string",
1661 "description": "The source image",
1662 "pattern": ".+",
1663 },
1664 {
1665 "type": "object",
1666 "properties": {
1667 "base": {
1668 "type": "string",
1669 },
1670 "repositories": {
1671 "type": "array",
1672 "items": {
1673 "type": "string",
1674 },
1675 },
1676 "tags": {
1677 "type": "array",
1678 "items": {
1679 "type": "string",
1680 },
1681 },
1682 },
1683 "required": ["repositories"],
1684 "additionalProperties": False,
1685 },
1686 ],
1687 },
1688 "to": {
1689 "oneOf": [
1690 {
1691 "type": "string",
1692 "description": "The destination image",
1693 },
1694 {
1695 "type": "object",
1696 "properties": {
1697 "base": {
1698 "type": "string",
1699 },
1700 "repository": {
1701 "type": "string",
1702 },
1703 "tag": {
1704 "type": "string",
1705 },
1706 },
1707 "additionalProperties": False,
1708 },
1709 ],
1710 },
1711 "semver": {
1712 "type": "string",
1713 "description": "Version specification as an NPM range",
1714 },
1715 },
1716 "required": ["from"],
1717 "additionalProperties": False,
1718 },
1719 "_example": (
1720 "\n"
1721 " # Mirror debian:bookworm\n"
1722 " # ... to registry.example.org/mygroup/myproject/library/debian:bookworm:\n"
1723 " - from: 'debian:bookworm'\n"
1724 " # Overriding destination:\n"
1725 " - from: 'quay.org/coreos/etcd:v3.4.1'\n"
1726 " to: 'etcd:v3.4.1' # Default would be coreos/etcd:v3.4.1\n"
1727 ),
1728 "_doc_link": "image_mirrors.md",
1729 "x-gitlabracadabra-order": 10,
1730 },
1731 # From https://docs.gitlab.com/ee/api/projects.html#edit-project
1732 # Deprecated
1733 "build_coverage_regex": {
1734 "type": "string",
1735 "description": "(Removed) Test coverage parsing.",
1736 },
1737 "container_registry_enabled": {
1738 "type": "boolean",
1739 "description": (
1740 "(Deprecated) Enable container registry for this project. "
1741 "Use container_registry_access_level instead."
1742 ),
1743 },
1744 "emails_disabled": {
1745 "type": "boolean",
1746 "description": "(Deprecated) Disable email notifications. Use emails_enabled instead.",
1747 },
1748 "issues_enabled": {
1749 "type": "boolean",
1750 "description": "(Deprecated) Enable issues for this project. Use issues_access_level instead.",
1751 },
1752 "jobs_enabled": {
1753 "type": "boolean",
1754 "description": "(Deprecated) Enable jobs for this project. Use builds_access_level instead.",
1755 },
1756 "merge_requests_enabled": {
1757 "type": "boolean",
1758 "description": (
1759 "(Deprecated) Enable merge requests for this project. " "Use merge_requests_access_level instead."
1760 ),
1761 },
1762 "public_builds": {
1763 "type": "boolean",
1764 "description": (
1765 "(Deprecated) If true, jobs can be viewed by non-project members. " "Use public_jobs instead."
1766 ),
1767 },
1768 "snippets_enabled": {
1769 "type": "boolean",
1770 "description": "(Deprecated) Enable snippets for this project. Use snippets_access_level instead.",
1771 },
1772 "tag_list": {
1773 "type": "array",
1774 "description": (
1775 "(Deprecated in GitLab 14.0) The list of tags for a project; put array of tags, "
1776 "that should be finally assigned to a project. Use topics instead."
1777 ),
1778 "items": {
1779 "type": "string",
1780 },
1781 "uniqueItems": True,
1782 "_example": "[GitLab, API, YAML]",
1783 },
1784 "wiki_enabled": {
1785 "type": "boolean",
1786 "description": "(Deprecated) Enable wiki for this project. Use wiki_access_level instead.",
1787 },
1788 # Below are undocumented settings
1789 "repository_storage": {
1790 "type": "string",
1791 "description": "Which storage shard the repository is on. Available only to admins",
1792 },
1793 "external_authorization_classification_label": {
1794 "type": "string",
1795 "description": "The classification label for the project",
1796 },
1797 },
1798 "additionalProperties": False,
1799 }
1801 IGNORED_PARAMS: ClassVar[list[str]] = [
1802 "initialize_with_readme",
1803 "repository_object_format",
1804 "unknown_boards",
1805 "unknown_board_lists",
1806 "unknown_groups",
1807 "unknown_labels",
1808 "unknown_members",
1809 "unknown_milestones",
1810 "unknown_pipeline_schedules",
1811 "unknown_pipeline_schedule_variables",
1812 "unknown_protected_branches",
1813 "unknown_protected_tags",
1814 "unknown_variables",
1815 "unknown_webhooks",
1816 ]
1818 CREATE_KEY = "name"
1819 CREATE_PARAMS: ClassVar[list[str]] = ["initialize_with_readme", "repository_object_format"]
1821 def _get_current_branches(self):
1822 if not hasattr(self, "_current_branches"):
1823 try:
1824 self._current_branches = [branch.name for branch in self._obj.branches.list(all=True)]
1825 except GitlabListError as err:
1826 if err.response_code != HTTPStatus.FORBIDDEN: # repository_enabled=false?
1827 pass
1828 self._current_branches = None
1829 return self._current_branches
1831 """"_process_archived()
1833 Process the archived param.
1834 """
1836 def _process_archived(self, param_name, param_value, *, dry_run=False, skip_save=False):
1837 assert param_name == "archived" # noqa: S101
1838 assert not skip_save # noqa: S101
1840 current_value = getattr(self._obj, param_name)
1841 if current_value != param_value: 1841 ↛ exitline 1841 didn't return from function '_process_archived' because the condition on line 1841 was always true
1842 if dry_run: 1842 ↛ 1843line 1842 didn't jump to line 1843 because the condition on line 1842 was never true
1843 logger.info(
1844 "[%s] NOT Changing param %s: %s -> %s (dry-run)", self._name, param_name, current_value, param_value
1845 )
1846 setattr(self._obj, param_name, param_value)
1847 else:
1848 logger.info("[%s] Changing param %s: %s -> %s", self._name, param_name, current_value, param_value)
1849 if param_value:
1850 self._obj.archive()
1851 else:
1852 self._obj.unarchive()
1854 """"_process_branches()
1856 Process the branches param.
1857 """
1859 def _process_branches(self, param_name, param_value, *, dry_run=False, skip_save=False):
1860 assert param_name == "branches" # noqa: S101
1861 assert not skip_save # noqa: S101
1862 if "default_branch" in self._content and self._content["default_branch"] in self._get_current_branches(): 1862 ↛ 1864line 1862 didn't jump to line 1864 because the condition on line 1862 was never true
1863 # Create from target default branch if it exists
1864 ref = self._content["default_branch"]
1865 elif self._obj.default_branch in self._get_current_branches(): 1865 ↛ 1869line 1865 didn't jump to line 1869 because the condition on line 1865 was always true
1866 # Create from current default branch otherwise
1867 ref = self._obj.default_branch
1868 else:
1869 ref = None
1870 for branch_name in param_value:
1871 if branch_name not in self._get_current_branches(): 1871 ↛ 1886line 1871 didn't jump to line 1886 because the condition on line 1871 was always true
1872 if ref is None: 1872 ↛ 1873line 1872 didn't jump to line 1873 because the condition on line 1872 was never true
1873 logger.info("[%s] NOT Creating branch: %s (no reference)", self._name, branch_name)
1874 elif dry_run: 1874 ↛ 1875line 1874 didn't jump to line 1875 because the condition on line 1874 was never true
1875 logger.info("[%s] NOT Creating branch: %s (dry-run)", self._name, branch_name)
1876 self._current_branches.append(branch_name)
1877 else:
1878 logger.info("[%s] Creating branch: %s", self._name, branch_name)
1879 self._obj.branches.create(
1880 {
1881 "branch": branch_name,
1882 "ref": ref,
1883 }
1884 )
1885 self._current_branches.append(branch_name)
1886 if branch_name in self._get_current_branches(): 1886 ↛ 1870line 1886 didn't jump to line 1870 because the condition on line 1886 was always true
1887 # Next branch will be created from this ref
1888 ref = branch_name
1890 """"_process_protected_tags()
1892 Process the protected_tags param.
1893 """
1895 def _process_protected_tags(self, param_name, param_value, *, dry_run=False, skip_save=False):
1896 assert param_name == "protected_tags" # noqa: S101
1897 assert not skip_save # noqa: S101
1898 unknown_protected_tags = self._content.get("unknown_protected_tags", "warn")
1899 try:
1900 current_protected_tags = dict(
1901 [[protected_tag.name, protected_tag] for protected_tag in self._obj.protectedtags.list(all=True)]
1902 )
1903 except AttributeError:
1904 logger.error(
1905 "[%s] Unable to manage protected tags: %s", self._name, "protected tags requires python-gitlab >= 1.7.0"
1906 )
1907 return
1908 # We first check for already protected tags
1909 for protected_name, target_config in sorted(param_value.items()):
1910 target_config = {
1911 "name": protected_name,
1912 "create_access_level": access_level_value(target_config),
1913 }
1914 if protected_name in current_protected_tags:
1915 current_protected_tag = current_protected_tags[protected_name]
1916 current_config = {
1917 "name": protected_name,
1918 "create_access_level": current_protected_tag.create_access_levels[0]["access_level"],
1919 }
1920 else:
1921 current_config = {}
1922 if current_config != target_config: 1922 ↛ 1909line 1922 didn't jump to line 1909 because the condition on line 1922 was always true
1923 if dry_run: 1923 ↛ 1924line 1923 didn't jump to line 1924 because the condition on line 1923 was never true
1924 logger.info(
1925 "[%s] NOT Changing protected tag %s access level: %s -> %s (dry-run)",
1926 self._name,
1927 protected_name,
1928 current_config,
1929 target_config,
1930 )
1931 else:
1932 logger.info(
1933 "[%s] Changing protected tag %s access level: %s -> %s",
1934 self._name,
1935 protected_name,
1936 current_config,
1937 target_config,
1938 )
1939 if "name" in current_config:
1940 self._obj.protectedtags.delete(protected_name)
1941 self._obj.protectedtags.create(target_config)
1942 # Remaining protected tags
1943 if unknown_protected_tags not in ["ignore", "skip"]: 1943 ↛ exitline 1943 didn't return from function '_process_protected_tags' because the condition on line 1943 was always true
1944 current_protected_tags = sorted(
1945 protected_tag.name for protected_tag in self._obj.protectedtags.list(all=True)
1946 )
1947 for protected_name in current_protected_tags:
1948 if protected_name not in param_value:
1949 if unknown_protected_tags in ["delete", "remove"]: 1949 ↛ 1958line 1949 didn't jump to line 1958 because the condition on line 1949 was always true
1950 if dry_run: 1950 ↛ 1951line 1950 didn't jump to line 1951 because the condition on line 1950 was never true
1951 logger.info(
1952 "[%s] NOT Deleting unknown protected tag: %s (dry-run)", self._name, protected_name
1953 )
1954 else:
1955 logger.info("[%s] Deleting unknown protected tag: %s", self._name, protected_name)
1956 self._obj.protectedtags.delete(protected_name)
1957 else:
1958 logger.warning(
1959 "[%s] NOT Deleting unknown protected tag: %s (unknown_protected_tags=%s)",
1960 self._name,
1961 protected_name,
1962 unknown_protected_tags,
1963 )
1965 """"_process_container_expiration_policy()
1967 Process the container_expiration_policy param.
1968 """
1970 def _process_container_expiration_policy(self, param_name, param_value, *, dry_run=False, skip_save=False):
1971 assert param_name == "container_expiration_policy" # noqa: S101
1972 assert not skip_save # noqa: S101
1974 current_value = getattr(self._obj, param_name)
1976 for k, v in sorted(param_value.items()):
1977 current_v = current_value.get(k, None)
1978 if v != current_v:
1979 if dry_run: 1979 ↛ 1980line 1979 didn't jump to line 1980 because the condition on line 1979 was never true
1980 logger.info(
1981 "[%s] NOT Changing container expiration policy %s: %s -> %s (dry-run)",
1982 self._name,
1983 k,
1984 current_v,
1985 v,
1986 )
1987 else:
1988 logger.info("[%s] Changing container expiration policy %s: %s -> %s", self._name, k, current_v, v)
1989 self._obj.container_expiration_policy_attributes = {k: v}
1990 self._obj.save()