1// 2// Copyright 2021, Sander van Harmelen 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17package gitlab 18 19import ( 20 "fmt" 21 "io" 22 "net/http" 23 "time" 24 25 retryablehttp "github.com/hashicorp/go-retryablehttp" 26) 27 28// ProjectsService handles communication with the repositories related methods 29// of the GitLab API. 30// 31// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html 32type ProjectsService struct { 33 client *Client 34} 35 36// Project represents a GitLab project. 37// 38// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html 39type Project struct { 40 ID int `json:"id"` 41 Description string `json:"description"` 42 DefaultBranch string `json:"default_branch"` 43 Public bool `json:"public"` 44 Visibility VisibilityValue `json:"visibility"` 45 SSHURLToRepo string `json:"ssh_url_to_repo"` 46 HTTPURLToRepo string `json:"http_url_to_repo"` 47 WebURL string `json:"web_url"` 48 ReadmeURL string `json:"readme_url"` 49 TagList []string `json:"tag_list"` 50 Topics []string `json:"topics"` 51 Owner *User `json:"owner"` 52 Name string `json:"name"` 53 NameWithNamespace string `json:"name_with_namespace"` 54 Path string `json:"path"` 55 PathWithNamespace string `json:"path_with_namespace"` 56 IssuesEnabled bool `json:"issues_enabled"` 57 OpenIssuesCount int `json:"open_issues_count"` 58 MergeRequestsEnabled bool `json:"merge_requests_enabled"` 59 ApprovalsBeforeMerge int `json:"approvals_before_merge"` 60 JobsEnabled bool `json:"jobs_enabled"` 61 WikiEnabled bool `json:"wiki_enabled"` 62 SnippetsEnabled bool `json:"snippets_enabled"` 63 ResolveOutdatedDiffDiscussions bool `json:"resolve_outdated_diff_discussions"` 64 ContainerExpirationPolicy *ContainerExpirationPolicy `json:"container_expiration_policy,omitempty"` 65 ContainerRegistryEnabled bool `json:"container_registry_enabled"` 66 ContainerRegistryAccessLevel AccessControlValue `json:"container_registry_access_level"` 67 CreatedAt *time.Time `json:"created_at,omitempty"` 68 LastActivityAt *time.Time `json:"last_activity_at,omitempty"` 69 CreatorID int `json:"creator_id"` 70 Namespace *ProjectNamespace `json:"namespace"` 71 ImportStatus string `json:"import_status"` 72 ImportError string `json:"import_error"` 73 Permissions *Permissions `json:"permissions"` 74 MarkedForDeletionAt *ISOTime `json:"marked_for_deletion_at"` 75 EmptyRepo bool `json:"empty_repo"` 76 Archived bool `json:"archived"` 77 AvatarURL string `json:"avatar_url"` 78 LicenseURL string `json:"license_url"` 79 License *ProjectLicense `json:"license"` 80 SharedRunnersEnabled bool `json:"shared_runners_enabled"` 81 ForksCount int `json:"forks_count"` 82 StarCount int `json:"star_count"` 83 RunnersToken string `json:"runners_token"` 84 PublicBuilds bool `json:"public_builds"` 85 AllowMergeOnSkippedPipeline bool `json:"allow_merge_on_skipped_pipeline"` 86 OnlyAllowMergeIfPipelineSucceeds bool `json:"only_allow_merge_if_pipeline_succeeds"` 87 OnlyAllowMergeIfAllDiscussionsAreResolved bool `json:"only_allow_merge_if_all_discussions_are_resolved"` 88 RemoveSourceBranchAfterMerge bool `json:"remove_source_branch_after_merge"` 89 PrintingMergeRequestLinkEnabled bool `json:"printing_merge_request_link_enabled"` 90 LFSEnabled bool `json:"lfs_enabled"` 91 RepositoryStorage string `json:"repository_storage"` 92 RequestAccessEnabled bool `json:"request_access_enabled"` 93 MergeMethod MergeMethodValue `json:"merge_method"` 94 ForkedFromProject *ForkParent `json:"forked_from_project"` 95 Mirror bool `json:"mirror"` 96 MirrorUserID int `json:"mirror_user_id"` 97 MirrorTriggerBuilds bool `json:"mirror_trigger_builds"` 98 OnlyMirrorProtectedBranches bool `json:"only_mirror_protected_branches"` 99 MirrorOverwritesDivergedBranches bool `json:"mirror_overwrites_diverged_branches"` 100 PackagesEnabled bool `json:"packages_enabled"` 101 ServiceDeskEnabled bool `json:"service_desk_enabled"` 102 ServiceDeskAddress string `json:"service_desk_address"` 103 IssuesAccessLevel AccessControlValue `json:"issues_access_level"` 104 RepositoryAccessLevel AccessControlValue `json:"repository_access_level"` 105 MergeRequestsAccessLevel AccessControlValue `json:"merge_requests_access_level"` 106 ForkingAccessLevel AccessControlValue `json:"forking_access_level"` 107 WikiAccessLevel AccessControlValue `json:"wiki_access_level"` 108 BuildsAccessLevel AccessControlValue `json:"builds_access_level"` 109 SnippetsAccessLevel AccessControlValue `json:"snippets_access_level"` 110 PagesAccessLevel AccessControlValue `json:"pages_access_level"` 111 OperationsAccessLevel AccessControlValue `json:"operations_access_level"` 112 AnalyticsAccessLevel AccessControlValue `json:"analytics_access_level"` 113 AutocloseReferencedIssues bool `json:"autoclose_referenced_issues"` 114 SuggestionCommitMessage string `json:"suggestion_commit_message"` 115 AutoCancelPendingPipelines string `json:"auto_cancel_pending_pipelines"` 116 CIForwardDeploymentEnabled bool `json:"ci_forward_deployment_enabled"` 117 SquashOption SquashOptionValue `json:"squash_option"` 118 SharedWithGroups []struct { 119 GroupID int `json:"group_id"` 120 GroupName string `json:"group_name"` 121 GroupAccessLevel int `json:"group_access_level"` 122 } `json:"shared_with_groups"` 123 Statistics *ProjectStatistics `json:"statistics"` 124 Links *Links `json:"_links,omitempty"` 125 CIConfigPath string `json:"ci_config_path"` 126 CIDefaultGitDepth int `json:"ci_default_git_depth"` 127 CustomAttributes []*CustomAttribute `json:"custom_attributes"` 128 ComplianceFrameworks []string `json:"compliance_frameworks"` 129 BuildCoverageRegex string `json:"build_coverage_regex"` 130 BuildTimeout int `json:"build_timeout"` 131 IssuesTemplate string `json:"issues_template"` 132 MergeRequestsTemplate string `json:"merge_requests_template"` 133 KeepLatestArtifact bool `json:"keep_latest_artifact"` 134 MergePipelinesEnabled bool `json:"merge_pipelines_enabled"` 135 MergeTrainsEnabled bool `json:"merge_trains_enabled"` 136 RestrictUserDefinedVariables bool `json:"restrict_user_defined_variables"` 137 MergeCommitTemplate string `json:"merge_commit_template"` 138 SquashCommitTemplate string `json:"squash_commit_template"` 139 AutoDevopsDeployStrategy string `json:"auto_devops_deploy_strategy"` 140 AutoDevopsEnabled bool `json:"auto_devops_enabled"` 141 BuildGitStrategy string `json:"build_git_strategy"` 142 EmailsDisabled bool `json:"emails_disabled"` 143 ExternalAuthorizationClassificationLabel string `json:"external_authorization_classification_label"` 144 RequirementsAccessLevel AccessControlValue `json:"requirements_access_level"` 145 SecurityAndComplianceAccessLevel AccessControlValue `json:"security_and_compliance_access_level"` 146} 147 148// BasicProject included in other service responses (such as todos). 149type BasicProject struct { 150 ID int `json:"id"` 151 Description string `json:"description"` 152 Name string `json:"name"` 153 NameWithNamespace string `json:"name_with_namespace"` 154 Path string `json:"path"` 155 PathWithNamespace string `json:"path_with_namespace"` 156 CreatedAt *time.Time `json:"created_at"` 157} 158 159// ContainerExpirationPolicy represents the container expiration policy. 160type ContainerExpirationPolicy struct { 161 Cadence string `json:"cadence"` 162 KeepN int `json:"keep_n"` 163 OlderThan string `json:"older_than"` 164 NameRegexDelete string `json:"name_regex_delete"` 165 NameRegexKeep string `json:"name_regex_keep"` 166 Enabled bool `json:"enabled"` 167 NextRunAt *time.Time `json:"next_run_at"` 168} 169 170// ForkParent represents the parent project when this is a fork. 171type ForkParent struct { 172 HTTPURLToRepo string `json:"http_url_to_repo"` 173 ID int `json:"id"` 174 Name string `json:"name"` 175 NameWithNamespace string `json:"name_with_namespace"` 176 Path string `json:"path"` 177 PathWithNamespace string `json:"path_with_namespace"` 178 WebURL string `json:"web_url"` 179} 180 181// GroupAccess represents group access. 182type GroupAccess struct { 183 AccessLevel AccessLevelValue `json:"access_level"` 184 NotificationLevel NotificationLevelValue `json:"notification_level"` 185} 186 187// Links represents a project web links for self, issues, merge_requests, 188// repo_branches, labels, events, members. 189type Links struct { 190 Self string `json:"self"` 191 Issues string `json:"issues"` 192 MergeRequests string `json:"merge_requests"` 193 RepoBranches string `json:"repo_branches"` 194 Labels string `json:"labels"` 195 Events string `json:"events"` 196 Members string `json:"members"` 197} 198 199// Permissions represents permissions. 200type Permissions struct { 201 ProjectAccess *ProjectAccess `json:"project_access"` 202 GroupAccess *GroupAccess `json:"group_access"` 203} 204 205// ProjectAccess represents project access. 206type ProjectAccess struct { 207 AccessLevel AccessLevelValue `json:"access_level"` 208 NotificationLevel NotificationLevelValue `json:"notification_level"` 209} 210 211// ProjectLicense represent the license for a project. 212type ProjectLicense struct { 213 Key string `json:"key"` 214 Name string `json:"name"` 215 Nickname string `json:"nickname"` 216 HTMLURL string `json:"html_url"` 217 SourceURL string `json:"source_url"` 218} 219 220// ProjectNamespace represents a project namespace. 221type ProjectNamespace struct { 222 ID int `json:"id"` 223 Name string `json:"name"` 224 Path string `json:"path"` 225 Kind string `json:"kind"` 226 FullPath string `json:"full_path"` 227 AvatarURL string `json:"avatar_url"` 228 WebURL string `json:"web_url"` 229} 230 231// ProjectStatistics represents a statistics record for a project. 232type ProjectStatistics struct { 233 StorageStatistics 234 CommitCount int `json:"commit_count"` 235} 236 237// Repository represents a repository. 238type Repository struct { 239 Name string `json:"name"` 240 Description string `json:"description"` 241 WebURL string `json:"web_url"` 242 AvatarURL string `json:"avatar_url"` 243 GitSSHURL string `json:"git_ssh_url"` 244 GitHTTPURL string `json:"git_http_url"` 245 Namespace string `json:"namespace"` 246 Visibility VisibilityValue `json:"visibility"` 247 PathWithNamespace string `json:"path_with_namespace"` 248 DefaultBranch string `json:"default_branch"` 249 Homepage string `json:"homepage"` 250 URL string `json:"url"` 251 SSHURL string `json:"ssh_url"` 252 HTTPURL string `json:"http_url"` 253} 254 255// StorageStatistics represents a statistics record for a group or project. 256type StorageStatistics struct { 257 StorageSize int64 `json:"storage_size"` 258 RepositorySize int64 `json:"repository_size"` 259 LfsObjectsSize int64 `json:"lfs_objects_size"` 260 JobArtifactsSize int64 `json:"job_artifacts_size"` 261} 262 263func (s Project) String() string { 264 return Stringify(s) 265} 266 267// ProjectApprovalRule represents a GitLab project approval rule. 268// 269// GitLab API docs: 270// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules 271type ProjectApprovalRule struct { 272 ID int `json:"id"` 273 Name string `json:"name"` 274 RuleType string `json:"rule_type"` 275 EligibleApprovers []*BasicUser `json:"eligible_approvers"` 276 ApprovalsRequired int `json:"approvals_required"` 277 Users []*BasicUser `json:"users"` 278 Groups []*Group `json:"groups"` 279 ContainsHiddenGroups bool `json:"contains_hidden_groups"` 280 ProtectedBranches []*ProtectedBranch `json:"protected_branches"` 281} 282 283func (s ProjectApprovalRule) String() string { 284 return Stringify(s) 285} 286 287// ListProjectsOptions represents the available ListProjects() options. 288// 289// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-projects 290type ListProjectsOptions struct { 291 ListOptions 292 Archived *bool `url:"archived,omitempty" json:"archived,omitempty"` 293 IDAfter *int `url:"id_after,omitempty" json:"id_after,omitempty"` 294 IDBefore *int `url:"id_before,omitempty" json:"id_before,omitempty"` 295 LastActivityAfter *time.Time `url:"last_activity_after,omitempty" json:"last_activity_after,omitempty"` 296 LastActivityBefore *time.Time `url:"last_activity_before,omitempty" json:"last_activity_before,omitempty"` 297 Membership *bool `url:"membership,omitempty" json:"membership,omitempty"` 298 MinAccessLevel *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"` 299 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` 300 Owned *bool `url:"owned,omitempty" json:"owned,omitempty"` 301 RepositoryChecksumFailed *bool `url:"repository_checksum_failed,omitempty" json:"repository_checksum_failed,omitempty"` 302 RepositoryStorage *string `url:"repository_storage,omitempty" json:"repository_storage,omitempty"` 303 Search *string `url:"search,omitempty" json:"search,omitempty"` 304 SearchNamespaces *bool `url:"search_namespaces,omitempty" json:"search_namespaces,omitempty"` 305 Simple *bool `url:"simple,omitempty" json:"simple,omitempty"` 306 Sort *string `url:"sort,omitempty" json:"sort,omitempty"` 307 Starred *bool `url:"starred,omitempty" json:"starred,omitempty"` 308 Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"` 309 Topic *string `url:"topic,omitempty" json:"topic,omitempty"` 310 Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` 311 WikiChecksumFailed *bool `url:"wiki_checksum_failed,omitempty" json:"wiki_checksum_failed,omitempty"` 312 WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"` 313 WithIssuesEnabled *bool `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"` 314 WithMergeRequestsEnabled *bool `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"` 315 WithProgrammingLanguage *string `url:"with_programming_language,omitempty" json:"with_programming_language,omitempty"` 316} 317 318// ListProjects gets a list of projects accessible by the authenticated user. 319// 320// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-projects 321func (s *ProjectsService) ListProjects(opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { 322 req, err := s.client.NewRequest(http.MethodGet, "projects", opt, options) 323 if err != nil { 324 return nil, nil, err 325 } 326 327 var p []*Project 328 resp, err := s.client.Do(req, &p) 329 if err != nil { 330 return nil, resp, err 331 } 332 333 return p, resp, err 334} 335 336// ListUserProjects gets a list of projects for the given user. 337// 338// GitLab API docs: 339// https://docs.gitlab.com/ce/api/projects.html#list-user-projects 340func (s *ProjectsService) ListUserProjects(uid interface{}, opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { 341 user, err := parseID(uid) 342 if err != nil { 343 return nil, nil, err 344 } 345 u := fmt.Sprintf("users/%s/projects", user) 346 347 req, err := s.client.NewRequest(http.MethodGet, u, opt, options) 348 if err != nil { 349 return nil, nil, err 350 } 351 352 var p []*Project 353 resp, err := s.client.Do(req, &p) 354 if err != nil { 355 return nil, resp, err 356 } 357 358 return p, resp, err 359} 360 361// ListUserStarredProjects gets a list of projects starred by the given user. 362// 363// GitLab API docs: 364// https://docs.gitlab.com/ee/api/projects.html#list-projects-starred-by-a-user 365func (s *ProjectsService) ListUserStarredProjects(uid interface{}, opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { 366 user, err := parseID(uid) 367 if err != nil { 368 return nil, nil, err 369 } 370 u := fmt.Sprintf("users/%s/starred_projects", user) 371 372 req, err := s.client.NewRequest(http.MethodGet, u, opt, options) 373 if err != nil { 374 return nil, nil, err 375 } 376 377 var p []*Project 378 resp, err := s.client.Do(req, &p) 379 if err != nil { 380 return nil, resp, err 381 } 382 383 return p, resp, err 384} 385 386// ProjectUser represents a GitLab project user. 387type ProjectUser struct { 388 ID int `json:"id"` 389 Name string `json:"name"` 390 Username string `json:"username"` 391 State string `json:"state"` 392 AvatarURL string `json:"avatar_url"` 393 WebURL string `json:"web_url"` 394} 395 396// ListProjectUserOptions represents the available ListProjectsUsers() options. 397// 398// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#get-project-users 399type ListProjectUserOptions struct { 400 ListOptions 401 Search *string `url:"search,omitempty" json:"search,omitempty"` 402} 403 404// ListProjectsUsers gets a list of users for the given project. 405// 406// GitLab API docs: 407// https://docs.gitlab.com/ce/api/projects.html#get-project-users 408func (s *ProjectsService) ListProjectsUsers(pid interface{}, opt *ListProjectUserOptions, options ...RequestOptionFunc) ([]*ProjectUser, *Response, error) { 409 project, err := parseID(pid) 410 if err != nil { 411 return nil, nil, err 412 } 413 u := fmt.Sprintf("projects/%s/users", PathEscape(project)) 414 415 req, err := s.client.NewRequest(http.MethodGet, u, opt, options) 416 if err != nil { 417 return nil, nil, err 418 } 419 420 var p []*ProjectUser 421 resp, err := s.client.Do(req, &p) 422 if err != nil { 423 return nil, resp, err 424 } 425 426 return p, resp, err 427} 428 429// ProjectGroup represents a GitLab project group. 430type ProjectGroup struct { 431 ID int `json:"id"` 432 Name string `json:"name"` 433 AvatarURL string `json:"avatar_url"` 434 WebURL string `json:"web_url"` 435 FullName string `json:"full_name"` 436 FullPath string `json:"full_path"` 437} 438 439// ListProjectGroupOptions represents the available ListProjectsGroups() options. 440// 441// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-a-projects-groups 442type ListProjectGroupOptions struct { 443 ListOptions 444 Search *string `url:"search,omitempty" json:"search,omitempty"` 445 SharedMinAccessLevel *AccessLevelValue `url:"shared_min_access_level,omitempty" json:"shared_min_access_level,omitempty"` 446 SharedVisiableOnly *bool `url:"shared_visible_only,omitempty" json:"shared_visible_only,omitempty"` 447 SkipGroups *[]int `url:"skip_groups,omitempty" json:"skip_groups,omitempty"` 448 WithShared *bool `url:"with_shared,omitempty" json:"with_shared,omitempty"` 449} 450 451// ListProjectsGroups gets a list of groups for the given project. 452// 453// GitLab API docs: 454// https://docs.gitlab.com/ce/api/projects.html#list-a-projects-groups 455func (s *ProjectsService) ListProjectsGroups(pid interface{}, opt *ListProjectGroupOptions, options ...RequestOptionFunc) ([]*ProjectGroup, *Response, error) { 456 project, err := parseID(pid) 457 if err != nil { 458 return nil, nil, err 459 } 460 u := fmt.Sprintf("projects/%s/groups", PathEscape(project)) 461 462 req, err := s.client.NewRequest(http.MethodGet, u, opt, options) 463 if err != nil { 464 return nil, nil, err 465 } 466 467 var p []*ProjectGroup 468 resp, err := s.client.Do(req, &p) 469 if err != nil { 470 return nil, resp, err 471 } 472 473 return p, resp, err 474} 475 476// ProjectLanguages is a map of strings because the response is arbitrary 477// 478// Gitlab API docs: https://docs.gitlab.com/ce/api/projects.html#languages 479type ProjectLanguages map[string]float32 480 481// GetProjectLanguages gets a list of languages used by the project 482// 483// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#languages 484func (s *ProjectsService) GetProjectLanguages(pid interface{}, options ...RequestOptionFunc) (*ProjectLanguages, *Response, error) { 485 project, err := parseID(pid) 486 if err != nil { 487 return nil, nil, err 488 } 489 u := fmt.Sprintf("projects/%s/languages", PathEscape(project)) 490 491 req, err := s.client.NewRequest(http.MethodGet, u, nil, options) 492 if err != nil { 493 return nil, nil, err 494 } 495 496 p := new(ProjectLanguages) 497 resp, err := s.client.Do(req, p) 498 if err != nil { 499 return nil, resp, err 500 } 501 502 return p, resp, err 503} 504 505// GetProjectOptions represents the available GetProject() options. 506// 507// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#get-single-project 508type GetProjectOptions struct { 509 License *bool `url:"license,omitempty" json:"license,omitempty"` 510 Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"` 511 WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"` 512} 513 514// GetProject gets a specific project, identified by project ID or 515// NAMESPACE/PROJECT_NAME, which is owned by the authenticated user. 516// 517// GitLab API docs: 518// https://docs.gitlab.com/ce/api/projects.html#get-single-project 519func (s *ProjectsService) GetProject(pid interface{}, opt *GetProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { 520 project, err := parseID(pid) 521 if err != nil { 522 return nil, nil, err 523 } 524 u := fmt.Sprintf("projects/%s", PathEscape(project)) 525 526 req, err := s.client.NewRequest(http.MethodGet, u, opt, options) 527 if err != nil { 528 return nil, nil, err 529 } 530 531 p := new(Project) 532 resp, err := s.client.Do(req, p) 533 if err != nil { 534 return nil, resp, err 535 } 536 537 return p, resp, err 538} 539 540// ProjectEvent represents a GitLab project event. 541// 542// GitLab API docs: 543// https://docs.gitlab.com/ce/api/projects.html#get-project-events 544type ProjectEvent struct { 545 Title interface{} `json:"title"` 546 ProjectID int `json:"project_id"` 547 ActionName string `json:"action_name"` 548 TargetID interface{} `json:"target_id"` 549 TargetType interface{} `json:"target_type"` 550 AuthorID int `json:"author_id"` 551 AuthorUsername string `json:"author_username"` 552 Data struct { 553 Before string `json:"before"` 554 After string `json:"after"` 555 Ref string `json:"ref"` 556 UserID int `json:"user_id"` 557 UserName string `json:"user_name"` 558 Repository *Repository `json:"repository"` 559 Commits []*Commit `json:"commits"` 560 TotalCommitsCount int `json:"total_commits_count"` 561 } `json:"data"` 562 TargetTitle interface{} `json:"target_title"` 563} 564 565func (s ProjectEvent) String() string { 566 return Stringify(s) 567} 568 569// GetProjectEventsOptions represents the available GetProjectEvents() options. 570// 571// GitLab API docs: 572// https://docs.gitlab.com/ce/api/projects.html#get-project-events 573type GetProjectEventsOptions ListOptions 574 575// GetProjectEvents gets the events for the specified project. Sorted from 576// newest to latest. 577// 578// GitLab API docs: 579// https://docs.gitlab.com/ce/api/projects.html#get-project-events 580func (s *ProjectsService) GetProjectEvents(pid interface{}, opt *GetProjectEventsOptions, options ...RequestOptionFunc) ([]*ProjectEvent, *Response, error) { 581 project, err := parseID(pid) 582 if err != nil { 583 return nil, nil, err 584 } 585 u := fmt.Sprintf("projects/%s/events", PathEscape(project)) 586 587 req, err := s.client.NewRequest(http.MethodGet, u, opt, options) 588 if err != nil { 589 return nil, nil, err 590 } 591 592 var p []*ProjectEvent 593 resp, err := s.client.Do(req, &p) 594 if err != nil { 595 return nil, resp, err 596 } 597 598 return p, resp, err 599} 600 601// CreateProjectOptions represents the available CreateProject() options. 602// 603// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project 604type CreateProjectOptions struct { 605 AllowMergeOnSkippedPipeline *bool `url:"allow_merge_on_skipped_pipeline,omitempty" json:"allow_merge_on_skipped_pipeline,omitempty"` 606 AnalyticsAccessLevel *AccessControlValue `url:"analytics_access_level,omitempty" json:"analytics_access_level,omitempty"` 607 ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"` 608 AutoCancelPendingPipelines *string `url:"auto_cancel_pending_pipelines,omitempty" json:"auto_cancel_pending_pipelines,omitempty"` 609 AutoDevopsDeployStrategy *string `url:"auto_devops_deploy_strategy,omitempty" json:"auto_devops_deploy_strategy,omitempty"` 610 AutoDevopsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"` 611 AutocloseReferencedIssues *bool `url:"autoclose_referenced_issues,omitempty" json:"autoclose_referenced_issues,omitempty"` 612 Avatar *ProjectAvatar `url:"-" json:"-"` 613 BuildCoverageRegex *string `url:"build_coverage_regex,omitempty" json:"build_coverage_regex,omitempty"` 614 BuildGitStrategy *string `url:"build_git_strategy,omitempty" json:"build_git_strategy,omitempty"` 615 BuildTimeout *int `url:"build_timeout,omitempty" json:"build_timeout,omitempty"` 616 BuildsAccessLevel *AccessControlValue `url:"builds_access_level,omitempty" json:"builds_access_level,omitempty"` 617 CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"` 618 ContainerExpirationPolicyAttributes *ContainerExpirationPolicyAttributes `url:"container_expiration_policy_attributes,omitempty" json:"container_expiration_policy_attributes,omitempty"` 619 ContainerRegistryAccessLevel *AccessControlValue `url:"container_registry_access_level,omitempty" json:"container_registry_access_level,omitempty"` 620 DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"` 621 Description *string `url:"description,omitempty" json:"description,omitempty"` 622 EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"` 623 ExternalAuthorizationClassificationLabel *string `url:"external_authorization_classification_label,omitempty" json:"external_authorization_classification_label,omitempty"` 624 ForkingAccessLevel *AccessControlValue `url:"forking_access_level,omitempty" json:"forking_access_level,omitempty"` 625 GroupWithProjectTemplatesID *int `url:"group_with_project_templates_id,omitempty" json:"group_with_project_templates_id,omitempty"` 626 ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"` 627 InitializeWithReadme *bool `url:"initialize_with_readme,omitempty" json:"initialize_with_readme,omitempty"` 628 IssuesAccessLevel *AccessControlValue `url:"issues_access_level,omitempty" json:"issues_access_level,omitempty"` 629 LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"` 630 MergeCommitTemplate *string `url:"merge_commit_template,omitempty" json:"merge_commit_template,omitempty"` 631 MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"` 632 MergePipelinesEnabled *bool `url:"merge_pipelines_enabled,omitempty" json:"merge_pipelines_enabled,omitempty"` 633 MergeRequestsAccessLevel *AccessControlValue `url:"merge_requests_access_level,omitempty" json:"merge_requests_access_level,omitempty"` 634 MergeTrainsEnabled *bool `url:"merge_trains_enabled,omitempty" json:"merge_trains_enabled,omitempty"` 635 Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"` 636 MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"` 637 Name *string `url:"name,omitempty" json:"name,omitempty"` 638 NamespaceID *int `url:"namespace_id,omitempty" json:"namespace_id,omitempty"` 639 OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"` 640 OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"` 641 OperationsAccessLevel *AccessControlValue `url:"operations_access_level,omitempty" json:"operations_access_level,omitempty"` 642 PackagesEnabled *bool `url:"packages_enabled,omitempty" json:"packages_enabled,omitempty"` 643 PagesAccessLevel *AccessControlValue `url:"pages_access_level,omitempty" json:"pages_access_level,omitempty"` 644 Path *string `url:"path,omitempty" json:"path,omitempty"` 645 PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"` 646 RemoveSourceBranchAfterMerge *bool `url:"remove_source_branch_after_merge,omitempty" json:"remove_source_branch_after_merge,omitempty"` 647 PrintingMergeRequestLinkEnabled *bool `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"` 648 RepositoryAccessLevel *AccessControlValue `url:"repository_access_level,omitempty" json:"repository_access_level,omitempty"` 649 RepositoryStorage *string `url:"repository_storage,omitempty" json:"repository_storage,omitempty"` 650 RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"` 651 RequirementsAccessLevel *AccessControlValue `url:"requirements_access_level,omitempty" json:"requirements_access_level,omitempty"` 652 ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"` 653 SecurityAndComplianceAccessLevel *AccessControlValue `url:"security_and_compliance_access_level,omitempty" json:"security_and_compliance_access_level,omitempty"` 654 SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"` 655 ShowDefaultAwardEmojis *bool `url:"show_default_award_emojis,omitempty" json:"show_default_award_emojis,omitempty"` 656 SnippetsAccessLevel *AccessControlValue `url:"snippets_access_level,omitempty" json:"snippets_access_level,omitempty"` 657 SquashCommitTemplate *string `url:"squash_commit_template,omitempty" json:"squash_commit_template,omitempty"` 658 SquashOption *SquashOptionValue `url:"squash_option,omitempty" json:"squash_option,omitempty"` 659 SuggestionCommitMessage *string `url:"suggestion_commit_message,omitempty" json:"suggestion_commit_message,omitempty"` 660 TemplateName *string `url:"template_name,omitempty" json:"template_name,omitempty"` 661 TemplateProjectID *int `url:"template_project_id,omitempty" json:"template_project_id,omitempty"` 662 Topics *[]string `url:"topics,omitempty" json:"topics,omitempty"` 663 UseCustomTemplate *bool `url:"use_custom_template,omitempty" json:"use_custom_template,omitempty"` 664 Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` 665 WikiAccessLevel *AccessControlValue `url:"wiki_access_level,omitempty" json:"wiki_access_level,omitempty"` 666 667 // Deprecated members 668 CIForwardDeploymentEnabled *bool `url:"ci_forward_deployment_enabled,omitempty" json:"ci_forward_deployment_enabled,omitempty"` 669 ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"` 670 IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"` 671 IssuesTemplate *string `url:"issues_template,omitempty" json:"issues_template,omitempty"` 672 JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"` 673 MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"` 674 MergeRequestsTemplate *string `url:"merge_requests_template,omitempty" json:"merge_requests_template,omitempty"` 675 ServiceDeskEnabled *bool `url:"service_desk_enabled,omitempty" json:"service_desk_enabled,omitempty"` 676 SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"` 677 TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"` 678 WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"` 679} 680 681// ContainerExpirationPolicyAttributes represents the available container 682// expiration policy attributes. 683// 684// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project 685type ContainerExpirationPolicyAttributes struct { 686 Cadence *string `url:"cadence,omitempty" json:"cadence,omitempty"` 687 KeepN *int `url:"keep_n,omitempty" json:"keep_n,omitempty"` 688 OlderThan *string `url:"older_than,omitempty" json:"older_than,omitempty"` 689 NameRegexDelete *string `url:"name_regex_delete,omitempty" json:"name_regex_delete,omitempty"` 690 NameRegexKeep *string `url:"name_regex_keep,omitempty" json:"name_regex_keep,omitempty"` 691 Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` 692 693 // Deprecated members 694 NameRegex *string `url:"name_regex,omitempty" json:"name_regex,omitempty"` 695} 696 697type ProjectAvatar struct { 698 Filename string 699 Image io.Reader 700} 701 702// CreateProject creates a new project owned by the authenticated user. 703// 704// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#create-project 705func (s *ProjectsService) CreateProject(opt *CreateProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { 706 if opt.ContainerExpirationPolicyAttributes != nil { 707 // This is needed to satisfy the API. Should be deleted 708 // when NameRegex is removed (it's now deprecated). 709 opt.ContainerExpirationPolicyAttributes.NameRegex = 710 opt.ContainerExpirationPolicyAttributes.NameRegexDelete 711 } 712 713 var err error 714 var req *retryablehttp.Request 715 716 if opt.Avatar == nil { 717 req, err = s.client.NewRequest(http.MethodPost, "projects", opt, options) 718 } else { 719 req, err = s.client.UploadRequest( 720 http.MethodPost, 721 "projects", 722 opt.Avatar.Image, 723 opt.Avatar.Filename, 724 UploadAvatar, 725 opt, 726 options, 727 ) 728 } 729 if err != nil { 730 return nil, nil, err 731 } 732 733 p := new(Project) 734 resp, err := s.client.Do(req, p) 735 if err != nil { 736 return nil, resp, err 737 } 738 739 return p, resp, err 740} 741 742// CreateProjectForUserOptions represents the available CreateProjectForUser() 743// options. 744// 745// GitLab API docs: 746// https://docs.gitlab.com/ce/api/projects.html#create-project-for-user 747type CreateProjectForUserOptions CreateProjectOptions 748 749// CreateProjectForUser creates a new project owned by the specified user. 750// Available only for admins. 751// 752// GitLab API docs: 753// https://docs.gitlab.com/ce/api/projects.html#create-project-for-user 754func (s *ProjectsService) CreateProjectForUser(user int, opt *CreateProjectForUserOptions, options ...RequestOptionFunc) (*Project, *Response, error) { 755 if opt.ContainerExpirationPolicyAttributes != nil { 756 // This is needed to satisfy the API. Should be deleted 757 // when NameRegex is removed (it's now deprecated). 758 opt.ContainerExpirationPolicyAttributes.NameRegex = 759 opt.ContainerExpirationPolicyAttributes.NameRegexDelete 760 } 761 762 var err error 763 var req *retryablehttp.Request 764 u := fmt.Sprintf("projects/user/%d", user) 765 766 if opt.Avatar == nil { 767 req, err = s.client.NewRequest(http.MethodPost, u, opt, options) 768 } else { 769 req, err = s.client.UploadRequest( 770 http.MethodPost, 771 u, 772 opt.Avatar.Image, 773 opt.Avatar.Filename, 774 UploadAvatar, 775 opt, 776 options, 777 ) 778 } 779 if err != nil { 780 return nil, nil, err 781 } 782 783 p := new(Project) 784 resp, err := s.client.Do(req, p) 785 if err != nil { 786 return nil, resp, err 787 } 788 789 return p, resp, err 790} 791 792// EditProjectOptions represents the available EditProject() options. 793// 794// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project 795type EditProjectOptions struct { 796 AllowMergeOnSkippedPipeline *bool `url:"allow_merge_on_skipped_pipeline,omitempty" json:"allow_merge_on_skipped_pipeline,omitempty"` 797 AnalyticsAccessLevel *AccessControlValue `url:"analytics_access_level,omitempty" json:"analytics_access_level,omitempty"` 798 ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"` 799 AutoCancelPendingPipelines *string `url:"auto_cancel_pending_pipelines,omitempty" json:"auto_cancel_pending_pipelines,omitempty"` 800 AutoDevopsDeployStrategy *string `url:"auto_devops_deploy_strategy,omitempty" json:"auto_devops_deploy_strategy,omitempty"` 801 AutoDevopsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"` 802 AutocloseReferencedIssues *bool `url:"autoclose_referenced_issues,omitempty" json:"autoclose_referenced_issues,omitempty"` 803 Avatar *ProjectAvatar `url:"-" json:"-"` 804 BuildCoverageRegex *string `url:"build_coverage_regex,omitempty" json:"build_coverage_regex,omitempty"` 805 BuildGitStrategy *string `url:"build_git_strategy,omitempty" json:"build_git_strategy,omitempty"` 806 BuildTimeout *int `url:"build_timeout,omitempty" json:"build_timeout,omitempty"` 807 BuildsAccessLevel *AccessControlValue `url:"builds_access_level,omitempty" json:"builds_access_level,omitempty"` 808 CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"` 809 CIDefaultGitDepth *int `url:"ci_default_git_depth,omitempty" json:"ci_default_git_depth,omitempty"` 810 ContainerExpirationPolicyAttributes *ContainerExpirationPolicyAttributes `url:"container_expiration_policy_attributes,omitempty" json:"container_expiration_policy_attributes,omitempty"` 811 ContainerRegistryAccessLevel *AccessControlValue `url:"container_registry_access_level,omitempty" json:"container_registry_access_level,omitempty"` 812 DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"` 813 Description *string `url:"description,omitempty" json:"description,omitempty"` 814 EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"` 815 ExternalAuthorizationClassificationLabel *string `url:"external_authorization_classification_label,omitempty" json:"external_authorization_classification_label,omitempty"` 816 ForkingAccessLevel *AccessControlValue `url:"forking_access_level,omitempty" json:"forking_access_level,omitempty"` 817 ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"` 818 IssuesAccessLevel *AccessControlValue `url:"issues_access_level,omitempty" json:"issues_access_level,omitempty"` 819 KeepLatestArtifact *bool `url:"keep_latest_artifact,omitempty" json:"keep_latest_artifact,omitempty"` 820 LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"` 821 MergeCommitTemplate *string `url:"merge_commit_template,omitempty" json:"merge_commit_template,omitempty"` 822 MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"` 823 MergePipelinesEnabled *bool `url:"merge_pipelines_enabled,omitempty" json:"merge_pipelines_enabled,omitempty"` 824 MergeRequestsAccessLevel *AccessControlValue `url:"merge_requests_access_level,omitempty" json:"merge_requests_access_level,omitempty"` 825 MergeTrainsEnabled *bool `url:"merge_trains_enabled,omitempty" json:"merge_trains_enabled,omitempty"` 826 Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"` 827 MirrorOverwritesDivergedBranches *bool `url:"mirror_overwrites_diverged_branches,omitempty" json:"mirror_overwrites_diverged_branches,omitempty"` 828 MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"` 829 MirrorUserID *int `url:"mirror_user_id,omitempty" json:"mirror_user_id,omitempty"` 830 Name *string `url:"name,omitempty" json:"name,omitempty"` 831 OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"` 832 OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"` 833 OnlyMirrorProtectedBranches *bool `url:"only_mirror_protected_branches,omitempty" json:"only_mirror_protected_branches,omitempty"` 834 OperationsAccessLevel *AccessControlValue `url:"operations_access_level,omitempty" json:"operations_access_level,omitempty"` 835 PackagesEnabled *bool `url:"packages_enabled,omitempty" json:"packages_enabled,omitempty"` 836 PagesAccessLevel *AccessControlValue `url:"pages_access_level,omitempty" json:"pages_access_level,omitempty"` 837 Path *string `url:"path,omitempty" json:"path,omitempty"` 838 PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"` 839 RemoveSourceBranchAfterMerge *bool `url:"remove_source_branch_after_merge,omitempty" json:"remove_source_branch_after_merge,omitempty"` 840 PrintingMergeRequestLinkEnabled *bool `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"` 841 RepositoryAccessLevel *AccessControlValue `url:"repository_access_level,omitempty" json:"repository_access_level,omitempty"` 842 RepositoryStorage *string `url:"repository_storage,omitempty" json:"repository_storage,omitempty"` 843 RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"` 844 RequirementsAccessLevel *AccessControlValue `url:"requirements_access_level,omitempty" json:"requirements_access_level,omitempty"` 845 ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"` 846 RestrictUserDefinedVariables *bool `url:"restrict_user_defined_variables,omitempty" json:"restrict_user_defined_variables,omitempty"` 847 SecurityAndComplianceAccessLevel *AccessControlValue `url:"security_and_compliance_access_level,omitempty" json:"security_and_compliance_access_level,omitempty"` 848 SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"` 849 ShowDefaultAwardEmojis *bool `url:"show_default_award_emojis,omitempty" json:"show_default_award_emojis,omitempty"` 850 SnippetsAccessLevel *AccessControlValue `url:"snippets_access_level,omitempty" json:"snippets_access_level,omitempty"` 851 SquashCommitTemplate *string `url:"squash_commit_template,omitempty" json:"squash_commit_template,omitempty"` 852 SquashOption *SquashOptionValue `url:"squash_option,omitempty" json:"squash_option,omitempty"` 853 SuggestionCommitMessage *string `url:"suggestion_commit_message,omitempty" json:"suggestion_commit_message,omitempty"` 854 Topics *[]string `url:"topics,omitempty" json:"topics,omitempty"` 855 Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` 856 WikiAccessLevel *AccessControlValue `url:"wiki_access_level,omitempty" json:"wiki_access_level,omitempty"` 857 858 // Deprecated members 859 CIForwardDeploymentEnabled *bool `url:"ci_forward_deployment_enabled,omitempty" json:"ci_forward_deployment_enabled,omitempty"` 860 ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"` 861 IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"` 862 IssuesTemplate *string `url:"issues_template,omitempty" json:"issues_template,omitempty"` 863 JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"` 864 MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"` 865 MergeRequestsTemplate *string `url:"merge_requests_template,omitempty" json:"merge_requests_template,omitempty"` 866 ServiceDeskEnabled *bool `url:"service_desk_enabled,omitempty" json:"service_desk_enabled,omitempty"` 867 SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"` 868 TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"` 869 WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"` 870} 871 872// EditProject updates an existing project. 873// 874// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project 875func (s *ProjectsService) EditProject(pid interface{}, opt *EditProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { 876 if opt.ContainerExpirationPolicyAttributes != nil { 877 // This is needed to satisfy the API. Should be deleted 878 // when NameRegex is removed (it's now deprecated). 879 opt.ContainerExpirationPolicyAttributes.NameRegex = 880 opt.ContainerExpirationPolicyAttributes.NameRegexDelete 881 } 882 883 project, err := parseID(pid) 884 if err != nil { 885 return nil, nil, err 886 } 887 u := fmt.Sprintf("projects/%s", PathEscape(project)) 888 889 var req *retryablehttp.Request 890 891 if opt.Avatar == nil { 892 req, err = s.client.NewRequest(http.MethodPut, u, opt, options) 893 } else { 894 req, err = s.client.UploadRequest( 895 http.MethodPost, 896 u, 897 opt.Avatar.Image, 898 opt.Avatar.Filename, 899 UploadAvatar, 900 opt, 901 options, 902 ) 903 } 904 if err != nil { 905 return nil, nil, err 906 } 907 908 p := new(Project) 909 resp, err := s.client.Do(req, p) 910 if err != nil { 911 return nil, resp, err 912 } 913 914 return p, resp, err 915} 916 917// ForkProjectOptions represents the available ForkProject() options. 918// 919// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#fork-project 920type ForkProjectOptions struct { 921 Description *string `url:"description,omitempty" json:"description,omitempty"` 922 MergeRequestDefaultTargetSelf *bool `url:"mr_default_target_self,omitempty" json:"mr_default_target_self,omitempty"` 923 Name *string `url:"name,omitempty" json:"name,omitempty"` 924 NamespaceID *int `url:"namespace_id,omitempty" json:"namespace_id,omitempty"` 925 NamespacePath *string `url:"namespace_path,omitempty" json:"namespace_path,omitempty"` 926 Path *string `url:"path,omitempty" json:"path,omitempty"` 927 Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` 928 929 // Deprecated members 930 Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"` 931} 932 933// ForkProject forks a project into the user namespace of the authenticated 934// user. 935// 936// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#fork-project 937func (s *ProjectsService) ForkProject(pid interface{}, opt *ForkProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { 938 project, err := parseID(pid) 939 if err != nil { 940 return nil, nil, err 941 } 942 u := fmt.Sprintf("projects/%s/fork", PathEscape(project)) 943 944 req, err := s.client.NewRequest(http.MethodPost, u, opt, options) 945 if err != nil { 946 return nil, nil, err 947 } 948 949 p := new(Project) 950 resp, err := s.client.Do(req, p) 951 if err != nil { 952 return nil, resp, err 953 } 954 955 return p, resp, err 956} 957 958// StarProject stars a given the project. 959// 960// GitLab API docs: 961// https://docs.gitlab.com/ce/api/projects.html#star-a-project 962func (s *ProjectsService) StarProject(pid interface{}, options ...RequestOptionFunc) (*Project, *Response, error) { 963 project, err := parseID(pid) 964 if err != nil { 965 return nil, nil, err 966 } 967 u := fmt.Sprintf("projects/%s/star", PathEscape(project)) 968 969 req, err := s.client.NewRequest(http.MethodPost, u, nil, options) 970 if err != nil { 971 return nil, nil, err 972 } 973 974 p := new(Project) 975 resp, err := s.client.Do(req, p) 976 if err != nil { 977 return nil, resp, err 978 } 979 980 return p, resp, err 981} 982 983// UnstarProject unstars a given project. 984// 985// GitLab API docs: 986// https://docs.gitlab.com/ce/api/projects.html#unstar-a-project 987func (s *ProjectsService) UnstarProject(pid interface{}, options ...RequestOptionFunc) (*Project, *Response, error) { 988 project, err := parseID(pid) 989 if err != nil { 990 return nil, nil, err 991 } 992 u := fmt.Sprintf("projects/%s/unstar", PathEscape(project)) 993 994 req, err := s.client.NewRequest(http.MethodPost, u, nil, options) 995 if err != nil { 996 return nil, nil, err 997 } 998 999 p := new(Project) 1000 resp, err := s.client.Do(req, p) 1001 if err != nil { 1002 return nil, resp, err 1003 } 1004 1005 return p, resp, err 1006} 1007 1008// ArchiveProject archives the project if the user is either admin or the 1009// project owner of this project. 1010// 1011// GitLab API docs: 1012// https://docs.gitlab.com/ce/api/projects.html#archive-a-project 1013func (s *ProjectsService) ArchiveProject(pid interface{}, options ...RequestOptionFunc) (*Project, *Response, error) { 1014 project, err := parseID(pid) 1015 if err != nil { 1016 return nil, nil, err 1017 } 1018 u := fmt.Sprintf("projects/%s/archive", PathEscape(project)) 1019 1020 req, err := s.client.NewRequest(http.MethodPost, u, nil, options) 1021 if err != nil { 1022 return nil, nil, err 1023 } 1024 1025 p := new(Project) 1026 resp, err := s.client.Do(req, p) 1027 if err != nil { 1028 return nil, resp, err 1029 } 1030 1031 return p, resp, err 1032} 1033 1034// UnarchiveProject unarchives the project if the user is either admin or 1035// the project owner of this project. 1036// 1037// GitLab API docs: 1038// https://docs.gitlab.com/ce/api/projects.html#unarchive-a-project 1039func (s *ProjectsService) UnarchiveProject(pid interface{}, options ...RequestOptionFunc) (*Project, *Response, error) { 1040 project, err := parseID(pid) 1041 if err != nil { 1042 return nil, nil, err 1043 } 1044 u := fmt.Sprintf("projects/%s/unarchive", PathEscape(project)) 1045 1046 req, err := s.client.NewRequest(http.MethodPost, u, nil, options) 1047 if err != nil { 1048 return nil, nil, err 1049 } 1050 1051 p := new(Project) 1052 resp, err := s.client.Do(req, p) 1053 if err != nil { 1054 return nil, resp, err 1055 } 1056 1057 return p, resp, err 1058} 1059 1060// DeleteProject removes a project including all associated resources 1061// (issues, merge requests etc.) 1062// 1063// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#remove-project 1064func (s *ProjectsService) DeleteProject(pid interface{}, options ...RequestOptionFunc) (*Response, error) { 1065 project, err := parseID(pid) 1066 if err != nil { 1067 return nil, err 1068 } 1069 u := fmt.Sprintf("projects/%s", PathEscape(project)) 1070 1071 req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) 1072 if err != nil { 1073 return nil, err 1074 } 1075 1076 return s.client.Do(req, nil) 1077} 1078 1079// ShareWithGroupOptions represents options to share project with groups 1080// 1081// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#share-project-with-group 1082type ShareWithGroupOptions struct { 1083 ExpiresAt *string `url:"expires_at" json:"expires_at"` 1084 GroupAccess *AccessLevelValue `url:"group_access" json:"group_access"` 1085 GroupID *int `url:"group_id" json:"group_id"` 1086} 1087 1088// ShareProjectWithGroup allows to share a project with a group. 1089// 1090// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#share-project-with-group 1091func (s *ProjectsService) ShareProjectWithGroup(pid interface{}, opt *ShareWithGroupOptions, options ...RequestOptionFunc) (*Response, error) { 1092 project, err := parseID(pid) 1093 if err != nil { 1094 return nil, err 1095 } 1096 u := fmt.Sprintf("projects/%s/share", PathEscape(project)) 1097 1098 req, err := s.client.NewRequest(http.MethodPost, u, opt, options) 1099 if err != nil { 1100 return nil, err 1101 } 1102 1103 return s.client.Do(req, nil) 1104} 1105 1106// DeleteSharedProjectFromGroup allows to unshare a project from a group. 1107// 1108// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#delete-a-shared-project-link-within-a-group 1109func (s *ProjectsService) DeleteSharedProjectFromGroup(pid interface{}, groupID int, options ...RequestOptionFunc) (*Response, error) { 1110 project, err := parseID(pid) 1111 if err != nil { 1112 return nil, err 1113 } 1114 u := fmt.Sprintf("projects/%s/share/%d", PathEscape(project), groupID) 1115 1116 req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) 1117 if err != nil { 1118 return nil, err 1119 } 1120 1121 return s.client.Do(req, nil) 1122} 1123 1124// ProjectMember represents a project member. 1125// 1126// GitLab API docs: 1127// https://docs.gitlab.com/ce/api/projects.html#list-project-team-members 1128type ProjectMember struct { 1129 ID int `json:"id"` 1130 Username string `json:"username"` 1131 Email string `json:"email"` 1132 Name string `json:"name"` 1133 State string `json:"state"` 1134 CreatedAt *time.Time `json:"created_at"` 1135 ExpiresAt *ISOTime `json:"expires_at"` 1136 AccessLevel AccessLevelValue `json:"access_level"` 1137 WebURL string `json:"web_url"` 1138 AvatarURL string `json:"avatar_url"` 1139} 1140 1141// ProjectHook represents a project hook. 1142// 1143// GitLab API docs: 1144// https://docs.gitlab.com/ce/api/projects.html#list-project-hooks 1145type ProjectHook struct { 1146 ID int `json:"id"` 1147 URL string `json:"url"` 1148 ConfidentialNoteEvents bool `json:"confidential_note_events"` 1149 ProjectID int `json:"project_id"` 1150 PushEvents bool `json:"push_events"` 1151 PushEventsBranchFilter string `json:"push_events_branch_filter"` 1152 IssuesEvents bool `json:"issues_events"` 1153 ConfidentialIssuesEvents bool `json:"confidential_issues_events"` 1154 MergeRequestsEvents bool `json:"merge_requests_events"` 1155 TagPushEvents bool `json:"tag_push_events"` 1156 NoteEvents bool `json:"note_events"` 1157 JobEvents bool `json:"job_events"` 1158 PipelineEvents bool `json:"pipeline_events"` 1159 WikiPageEvents bool `json:"wiki_page_events"` 1160 DeploymentEvents bool `json:"deployment_events"` 1161 ReleasesEvents bool `json:"releases_events"` 1162 EnableSSLVerification bool `json:"enable_ssl_verification"` 1163 CreatedAt *time.Time `json:"created_at"` 1164} 1165 1166// ListProjectHooksOptions represents the available ListProjectHooks() options. 1167// 1168// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-project-hooks 1169type ListProjectHooksOptions ListOptions 1170 1171// ListProjectHooks gets a list of project hooks. 1172// 1173// GitLab API docs: 1174// https://docs.gitlab.com/ce/api/projects.html#list-project-hooks 1175func (s *ProjectsService) ListProjectHooks(pid interface{}, opt *ListProjectHooksOptions, options ...RequestOptionFunc) ([]*ProjectHook, *Response, error) { 1176 project, err := parseID(pid) 1177 if err != nil { 1178 return nil, nil, err 1179 } 1180 u := fmt.Sprintf("projects/%s/hooks", PathEscape(project)) 1181 1182 req, err := s.client.NewRequest(http.MethodGet, u, opt, options) 1183 if err != nil { 1184 return nil, nil, err 1185 } 1186 1187 var ph []*ProjectHook 1188 resp, err := s.client.Do(req, &ph) 1189 if err != nil { 1190 return nil, resp, err 1191 } 1192 1193 return ph, resp, err 1194} 1195 1196// GetProjectHook gets a specific hook for a project. 1197// 1198// GitLab API docs: 1199// https://docs.gitlab.com/ce/api/projects.html#get-project-hook 1200func (s *ProjectsService) GetProjectHook(pid interface{}, hook int, options ...RequestOptionFunc) (*ProjectHook, *Response, error) { 1201 project, err := parseID(pid) 1202 if err != nil { 1203 return nil, nil, err 1204 } 1205 u := fmt.Sprintf("projects/%s/hooks/%d", PathEscape(project), hook) 1206 1207 req, err := s.client.NewRequest(http.MethodGet, u, nil, options) 1208 if err != nil { 1209 return nil, nil, err 1210 } 1211 1212 ph := new(ProjectHook) 1213 resp, err := s.client.Do(req, ph) 1214 if err != nil { 1215 return nil, resp, err 1216 } 1217 1218 return ph, resp, err 1219} 1220 1221// AddProjectHookOptions represents the available AddProjectHook() options. 1222// 1223// GitLab API docs: 1224// https://docs.gitlab.com/ce/api/projects.html#add-project-hook 1225type AddProjectHookOptions struct { 1226 ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` 1227 ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` 1228 DeploymentEvents *bool `url:"deployment_events,omitempty" json:"deployment_events,omitempty"` 1229 EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` 1230 IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` 1231 JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"` 1232 MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` 1233 NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` 1234 PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` 1235 PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` 1236 PushEventsBranchFilter *string `url:"push_events_branch_filter,omitempty" json:"push_events_branch_filter,omitempty"` 1237 ReleasesEvents *bool `url:"releases_events,omitempty" json:"releases_events,omitempty"` 1238 TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` 1239 Token *string `url:"token,omitempty" json:"token,omitempty"` 1240 URL *string `url:"url,omitempty" json:"url,omitempty"` 1241 WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` 1242} 1243 1244// AddProjectHook adds a hook to a specified project. 1245// 1246// GitLab API docs: 1247// https://docs.gitlab.com/ce/api/projects.html#add-project-hook 1248func (s *ProjectsService) AddProjectHook(pid interface{}, opt *AddProjectHookOptions, options ...RequestOptionFunc) (*ProjectHook, *Response, error) { 1249 project, err := parseID(pid) 1250 if err != nil { 1251 return nil, nil, err 1252 } 1253 u := fmt.Sprintf("projects/%s/hooks", PathEscape(project)) 1254 1255 req, err := s.client.NewRequest(http.MethodPost, u, opt, options) 1256 if err != nil { 1257 return nil, nil, err 1258 } 1259 1260 ph := new(ProjectHook) 1261 resp, err := s.client.Do(req, ph) 1262 if err != nil { 1263 return nil, resp, err 1264 } 1265 1266 return ph, resp, err 1267} 1268 1269// EditProjectHookOptions represents the available EditProjectHook() options. 1270// 1271// GitLab API docs: 1272// https://docs.gitlab.com/ce/api/projects.html#edit-project-hook 1273type EditProjectHookOptions struct { 1274 ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` 1275 ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` 1276 DeploymentEvents *bool `url:"deployment_events,omitempty" json:"deployment_events,omitempty"` 1277 EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` 1278 IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` 1279 JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"` 1280 MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` 1281 NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` 1282 PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` 1283 PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` 1284 PushEventsBranchFilter *string `url:"push_events_branch_filter,omitempty" json:"push_events_branch_filter,omitempty"` 1285 ReleasesEvents *bool `url:"releases_events,omitempty" json:"releases_events,omitempty"` 1286 TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` 1287 Token *string `url:"token,omitempty" json:"token,omitempty"` 1288 URL *string `url:"url,omitempty" json:"url,omitempty"` 1289 WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` 1290} 1291 1292// EditProjectHook edits a hook for a specified project. 1293// 1294// GitLab API docs: 1295// https://docs.gitlab.com/ce/api/projects.html#edit-project-hook 1296func (s *ProjectsService) EditProjectHook(pid interface{}, hook int, opt *EditProjectHookOptions, options ...RequestOptionFunc) (*ProjectHook, *Response, error) { 1297 project, err := parseID(pid) 1298 if err != nil { 1299 return nil, nil, err 1300 } 1301 u := fmt.Sprintf("projects/%s/hooks/%d", PathEscape(project), hook) 1302 1303 req, err := s.client.NewRequest(http.MethodPut, u, opt, options) 1304 if err != nil { 1305 return nil, nil, err 1306 } 1307 1308 ph := new(ProjectHook) 1309 resp, err := s.client.Do(req, ph) 1310 if err != nil { 1311 return nil, resp, err 1312 } 1313 1314 return ph, resp, err 1315} 1316 1317// DeleteProjectHook removes a hook from a project. This is an idempotent 1318// method and can be called multiple times. Either the hook is available or not. 1319// 1320// GitLab API docs: 1321// https://docs.gitlab.com/ce/api/projects.html#delete-project-hook 1322func (s *ProjectsService) DeleteProjectHook(pid interface{}, hook int, options ...RequestOptionFunc) (*Response, error) { 1323 project, err := parseID(pid) 1324 if err != nil { 1325 return nil, err 1326 } 1327 u := fmt.Sprintf("projects/%s/hooks/%d", PathEscape(project), hook) 1328 1329 req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) 1330 if err != nil { 1331 return nil, err 1332 } 1333 1334 return s.client.Do(req, nil) 1335} 1336 1337// ProjectForkRelation represents a project fork relationship. 1338// 1339// GitLab API docs: 1340// https://docs.gitlab.com/ce/api/projects.html#admin-fork-relation 1341type ProjectForkRelation struct { 1342 ID int `json:"id"` 1343 ForkedToProjectID int `json:"forked_to_project_id"` 1344 ForkedFromProjectID int `json:"forked_from_project_id"` 1345 CreatedAt *time.Time `json:"created_at"` 1346 UpdatedAt *time.Time `json:"updated_at"` 1347} 1348 1349// CreateProjectForkRelation creates a forked from/to relation between 1350// existing projects. 1351// 1352// GitLab API docs: 1353// https://docs.gitlab.com/ce/api/projects.html#create-a-forked-fromto-relation-between-existing-projects. 1354func (s *ProjectsService) CreateProjectForkRelation(pid interface{}, fork int, options ...RequestOptionFunc) (*ProjectForkRelation, *Response, error) { 1355 project, err := parseID(pid) 1356 if err != nil { 1357 return nil, nil, err 1358 } 1359 u := fmt.Sprintf("projects/%s/fork/%d", PathEscape(project), fork) 1360 1361 req, err := s.client.NewRequest(http.MethodPost, u, nil, options) 1362 if err != nil { 1363 return nil, nil, err 1364 } 1365 1366 pfr := new(ProjectForkRelation) 1367 resp, err := s.client.Do(req, pfr) 1368 if err != nil { 1369 return nil, resp, err 1370 } 1371 1372 return pfr, resp, err 1373} 1374 1375// DeleteProjectForkRelation deletes an existing forked from relationship. 1376// 1377// GitLab API docs: 1378// https://docs.gitlab.com/ce/api/projects.html#delete-an-existing-forked-from-relationship 1379func (s *ProjectsService) DeleteProjectForkRelation(pid interface{}, options ...RequestOptionFunc) (*Response, error) { 1380 project, err := parseID(pid) 1381 if err != nil { 1382 return nil, err 1383 } 1384 u := fmt.Sprintf("projects/%s/fork", PathEscape(project)) 1385 1386 req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) 1387 if err != nil { 1388 return nil, err 1389 } 1390 1391 return s.client.Do(req, nil) 1392} 1393 1394// ProjectFile represents an uploaded project file. 1395// 1396// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#upload-a-file 1397type ProjectFile struct { 1398 Alt string `json:"alt"` 1399 URL string `json:"url"` 1400 Markdown string `json:"markdown"` 1401} 1402 1403// UploadFile uploads a file. 1404// 1405// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#upload-a-file 1406func (s *ProjectsService) UploadFile(pid interface{}, content io.Reader, filename string, options ...RequestOptionFunc) (*ProjectFile, *Response, error) { 1407 project, err := parseID(pid) 1408 if err != nil { 1409 return nil, nil, err 1410 } 1411 u := fmt.Sprintf("projects/%s/uploads", PathEscape(project)) 1412 1413 req, err := s.client.UploadRequest( 1414 http.MethodPost, 1415 u, 1416 content, 1417 filename, 1418 UploadFile, 1419 nil, 1420 options, 1421 ) 1422 if err != nil { 1423 return nil, nil, err 1424 } 1425 1426 pf := new(ProjectFile) 1427 resp, err := s.client.Do(req, pf) 1428 if err != nil { 1429 return nil, resp, err 1430 } 1431 1432 return pf, resp, nil 1433} 1434 1435// UploadAvatar uploads an avatar. 1436// 1437// GitLab API docs: 1438// https://docs.gitlab.com/ee/api/projects.html#upload-a-project-avatar 1439func (s *ProjectsService) UploadAvatar(pid interface{}, avatar io.Reader, filename string, options ...RequestOptionFunc) (*Project, *Response, error) { 1440 project, err := parseID(pid) 1441 if err != nil { 1442 return nil, nil, err 1443 } 1444 u := fmt.Sprintf("projects/%s", PathEscape(project)) 1445 1446 req, err := s.client.UploadRequest( 1447 http.MethodPut, 1448 u, 1449 avatar, 1450 filename, 1451 UploadAvatar, 1452 nil, 1453 options, 1454 ) 1455 if err != nil { 1456 return nil, nil, err 1457 } 1458 1459 p := new(Project) 1460 resp, err := s.client.Do(req, p) 1461 if err != nil { 1462 return nil, resp, err 1463 } 1464 1465 return p, resp, err 1466} 1467 1468// ListProjectForks gets a list of project forks. 1469// 1470// GitLab API docs: 1471// https://docs.gitlab.com/ce/api/projects.html#list-forks-of-a-project 1472func (s *ProjectsService) ListProjectForks(pid interface{}, opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { 1473 project, err := parseID(pid) 1474 if err != nil { 1475 return nil, nil, err 1476 } 1477 u := fmt.Sprintf("projects/%s/forks", PathEscape(project)) 1478 1479 req, err := s.client.NewRequest(http.MethodGet, u, opt, options) 1480 if err != nil { 1481 return nil, nil, err 1482 } 1483 1484 var forks []*Project 1485 resp, err := s.client.Do(req, &forks) 1486 if err != nil { 1487 return nil, resp, err 1488 } 1489 1490 return forks, resp, err 1491} 1492 1493// ProjectPushRules represents a project push rule. 1494// 1495// GitLab API docs: 1496// https://docs.gitlab.com/ee/api/projects.html#push-rules 1497type ProjectPushRules struct { 1498 ID int `json:"id"` 1499 ProjectID int `json:"project_id"` 1500 CommitMessageRegex string `json:"commit_message_regex"` 1501 CommitMessageNegativeRegex string `json:"commit_message_negative_regex"` 1502 BranchNameRegex string `json:"branch_name_regex"` 1503 DenyDeleteTag bool `json:"deny_delete_tag"` 1504 CreatedAt *time.Time `json:"created_at"` 1505 MemberCheck bool `json:"member_check"` 1506 PreventSecrets bool `json:"prevent_secrets"` 1507 AuthorEmailRegex string `json:"author_email_regex"` 1508 FileNameRegex string `json:"file_name_regex"` 1509 MaxFileSize int `json:"max_file_size"` 1510 CommitCommitterCheck bool `json:"commit_committer_check"` 1511 RejectUnsignedCommits bool `json:"reject_unsigned_commits"` 1512} 1513 1514// GetProjectPushRules gets the push rules of a project. 1515// 1516// GitLab API docs: 1517// https://docs.gitlab.com/ee/api/projects.html#get-project-push-rules 1518func (s *ProjectsService) GetProjectPushRules(pid interface{}, options ...RequestOptionFunc) (*ProjectPushRules, *Response, error) { 1519 project, err := parseID(pid) 1520 if err != nil { 1521 return nil, nil, err 1522 } 1523 u := fmt.Sprintf("projects/%s/push_rule", PathEscape(project)) 1524 1525 req, err := s.client.NewRequest(http.MethodGet, u, nil, options) 1526 if err != nil { 1527 return nil, nil, err 1528 } 1529 1530 ppr := new(ProjectPushRules) 1531 resp, err := s.client.Do(req, ppr) 1532 if err != nil { 1533 return nil, resp, err 1534 } 1535 1536 return ppr, resp, err 1537} 1538 1539// AddProjectPushRuleOptions represents the available AddProjectPushRule() 1540// options. 1541// 1542// GitLab API docs: 1543// https://docs.gitlab.com/ee/api/projects.html#add-project-push-rule 1544type AddProjectPushRuleOptions struct { 1545 AuthorEmailRegex *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"` 1546 BranchNameRegex *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"` 1547 CommitCommitterCheck *bool `url:"commit_committer_check,omitempty" json:"commit_committer_check,omitempty"` 1548 CommitMessageNegativeRegex *string `url:"commit_message_negative_regex,omitempty" json:"commit_message_negative_regex,omitempty"` 1549 CommitMessageRegex *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"` 1550 DenyDeleteTag *bool `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"` 1551 FileNameRegex *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"` 1552 MaxFileSize *int `url:"max_file_size,omitempty" json:"max_file_size,omitempty"` 1553 MemberCheck *bool `url:"member_check,omitempty" json:"member_check,omitempty"` 1554 PreventSecrets *bool `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"` 1555 RejectUnsignedCommits *bool `url:"reject_unsigned_commits,omitempty" json:"reject_unsigned_commits,omitempty"` 1556} 1557 1558// AddProjectPushRule adds a push rule to a specified project. 1559// 1560// GitLab API docs: 1561// https://docs.gitlab.com/ee/api/projects.html#add-project-push-rule 1562func (s *ProjectsService) AddProjectPushRule(pid interface{}, opt *AddProjectPushRuleOptions, options ...RequestOptionFunc) (*ProjectPushRules, *Response, error) { 1563 project, err := parseID(pid) 1564 if err != nil { 1565 return nil, nil, err 1566 } 1567 u := fmt.Sprintf("projects/%s/push_rule", PathEscape(project)) 1568 1569 req, err := s.client.NewRequest(http.MethodPost, u, opt, options) 1570 if err != nil { 1571 return nil, nil, err 1572 } 1573 1574 ppr := new(ProjectPushRules) 1575 resp, err := s.client.Do(req, ppr) 1576 if err != nil { 1577 return nil, resp, err 1578 } 1579 1580 return ppr, resp, err 1581} 1582 1583// EditProjectPushRuleOptions represents the available EditProjectPushRule() 1584// options. 1585// 1586// GitLab API docs: 1587// https://docs.gitlab.com/ee/api/projects.html#edit-project-push-rule 1588type EditProjectPushRuleOptions struct { 1589 AuthorEmailRegex *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"` 1590 BranchNameRegex *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"` 1591 CommitCommitterCheck *bool `url:"commit_committer_check,omitempty" json:"commit_committer_check,omitempty"` 1592 CommitMessageNegativeRegex *string `url:"commit_message_negative_regex,omitempty" json:"commit_message_negative_regex,omitempty"` 1593 CommitMessageRegex *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"` 1594 DenyDeleteTag *bool `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"` 1595 FileNameRegex *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"` 1596 MaxFileSize *int `url:"max_file_size,omitempty" json:"max_file_size,omitempty"` 1597 MemberCheck *bool `url:"member_check,omitempty" json:"member_check,omitempty"` 1598 PreventSecrets *bool `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"` 1599 RejectUnsignedCommits *bool `url:"reject_unsigned_commits,omitempty" json:"reject_unsigned_commits,omitempty"` 1600} 1601 1602// EditProjectPushRule edits a push rule for a specified project. 1603// 1604// GitLab API docs: 1605// https://docs.gitlab.com/ee/api/projects.html#edit-project-push-rule 1606func (s *ProjectsService) EditProjectPushRule(pid interface{}, opt *EditProjectPushRuleOptions, options ...RequestOptionFunc) (*ProjectPushRules, *Response, error) { 1607 project, err := parseID(pid) 1608 if err != nil { 1609 return nil, nil, err 1610 } 1611 u := fmt.Sprintf("projects/%s/push_rule", PathEscape(project)) 1612 1613 req, err := s.client.NewRequest(http.MethodPut, u, opt, options) 1614 if err != nil { 1615 return nil, nil, err 1616 } 1617 1618 ppr := new(ProjectPushRules) 1619 resp, err := s.client.Do(req, ppr) 1620 if err != nil { 1621 return nil, resp, err 1622 } 1623 1624 return ppr, resp, err 1625} 1626 1627// DeleteProjectPushRule removes a push rule from a project. This is an 1628// idempotent method and can be called multiple times. Either the push rule is 1629// available or not. 1630// 1631// GitLab API docs: 1632// https://docs.gitlab.com/ee/api/projects.html#delete-project-push-rule 1633func (s *ProjectsService) DeleteProjectPushRule(pid interface{}, options ...RequestOptionFunc) (*Response, error) { 1634 project, err := parseID(pid) 1635 if err != nil { 1636 return nil, err 1637 } 1638 u := fmt.Sprintf("projects/%s/push_rule", PathEscape(project)) 1639 1640 req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) 1641 if err != nil { 1642 return nil, err 1643 } 1644 1645 return s.client.Do(req, nil) 1646} 1647 1648// ProjectApprovals represents GitLab project level merge request approvals. 1649// 1650// GitLab API docs: 1651// https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals 1652type ProjectApprovals struct { 1653 Approvers []*MergeRequestApproverUser `json:"approvers"` 1654 ApproverGroups []*MergeRequestApproverGroup `json:"approver_groups"` 1655 ApprovalsBeforeMerge int `json:"approvals_before_merge"` 1656 ResetApprovalsOnPush bool `json:"reset_approvals_on_push"` 1657 DisableOverridingApproversPerMergeRequest bool `json:"disable_overriding_approvers_per_merge_request"` 1658 MergeRequestsAuthorApproval bool `json:"merge_requests_author_approval"` 1659 MergeRequestsDisableCommittersApproval bool `json:"merge_requests_disable_committers_approval"` 1660 RequirePasswordToApprove bool `json:"require_password_to_approve"` 1661} 1662 1663// GetApprovalConfiguration get the approval configuration for a project. 1664// 1665// GitLab API docs: 1666// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-configuration 1667func (s *ProjectsService) GetApprovalConfiguration(pid interface{}, options ...RequestOptionFunc) (*ProjectApprovals, *Response, error) { 1668 project, err := parseID(pid) 1669 if err != nil { 1670 return nil, nil, err 1671 } 1672 u := fmt.Sprintf("projects/%s/approvals", PathEscape(project)) 1673 1674 req, err := s.client.NewRequest(http.MethodGet, u, nil, options) 1675 if err != nil { 1676 return nil, nil, err 1677 } 1678 1679 pa := new(ProjectApprovals) 1680 resp, err := s.client.Do(req, pa) 1681 if err != nil { 1682 return nil, resp, err 1683 } 1684 1685 return pa, resp, err 1686} 1687 1688// ChangeApprovalConfigurationOptions represents the available 1689// ApprovalConfiguration() options. 1690// 1691// GitLab API docs: 1692// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-configuration 1693type ChangeApprovalConfigurationOptions struct { 1694 ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"` 1695 DisableOverridingApproversPerMergeRequest *bool `url:"disable_overriding_approvers_per_merge_request,omitempty" json:"disable_overriding_approvers_per_merge_request,omitempty"` 1696 MergeRequestsAuthorApproval *bool `url:"merge_requests_author_approval,omitempty" json:"merge_requests_author_approval,omitempty"` 1697 MergeRequestsDisableCommittersApproval *bool `url:"merge_requests_disable_committers_approval,omitempty" json:"merge_requests_disable_committers_approval,omitempty"` 1698 RequirePasswordToApprove *bool `url:"require_password_to_approve,omitempty" json:"require_password_to_approve,omitempty"` 1699 ResetApprovalsOnPush *bool `url:"reset_approvals_on_push,omitempty" json:"reset_approvals_on_push,omitempty"` 1700} 1701 1702// ChangeApprovalConfiguration updates the approval configuration for a project. 1703// 1704// GitLab API docs: 1705// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-configuration 1706func (s *ProjectsService) ChangeApprovalConfiguration(pid interface{}, opt *ChangeApprovalConfigurationOptions, options ...RequestOptionFunc) (*ProjectApprovals, *Response, error) { 1707 project, err := parseID(pid) 1708 if err != nil { 1709 return nil, nil, err 1710 } 1711 u := fmt.Sprintf("projects/%s/approvals", PathEscape(project)) 1712 1713 req, err := s.client.NewRequest(http.MethodPost, u, opt, options) 1714 if err != nil { 1715 return nil, nil, err 1716 } 1717 1718 pa := new(ProjectApprovals) 1719 resp, err := s.client.Do(req, pa) 1720 if err != nil { 1721 return nil, resp, err 1722 } 1723 1724 return pa, resp, err 1725} 1726 1727// GetProjectApprovalRules looks up the list of project level approvers. 1728// 1729// GitLab API docs: 1730// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules 1731func (s *ProjectsService) GetProjectApprovalRules(pid interface{}, options ...RequestOptionFunc) ([]*ProjectApprovalRule, *Response, error) { 1732 project, err := parseID(pid) 1733 if err != nil { 1734 return nil, nil, err 1735 } 1736 u := fmt.Sprintf("projects/%s/approval_rules", PathEscape(project)) 1737 1738 req, err := s.client.NewRequest(http.MethodGet, u, nil, options) 1739 if err != nil { 1740 return nil, nil, err 1741 } 1742 1743 var par []*ProjectApprovalRule 1744 resp, err := s.client.Do(req, &par) 1745 if err != nil { 1746 return nil, resp, err 1747 } 1748 1749 return par, resp, err 1750} 1751 1752// CreateProjectLevelRuleOptions represents the available CreateProjectApprovalRule() 1753// options. 1754// 1755// GitLab API docs: 1756// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-project-level-rules 1757type CreateProjectLevelRuleOptions struct { 1758 Name *string `url:"name,omitempty" json:"name,omitempty"` 1759 ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` 1760 RuleType *string `url:"rule_type,omitempty" json:"rule_type,omitempty"` 1761 UserIDs *[]int `url:"user_ids,omitempty" json:"user_ids,omitempty"` 1762 GroupIDs *[]int `url:"group_ids,omitempty" json:"group_ids,omitempty"` 1763 ProtectedBranchIDs *[]int `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"` 1764} 1765 1766// CreateProjectApprovalRule creates a new project-level approval rule. 1767// 1768// GitLab API docs: 1769// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-project-level-rules 1770func (s *ProjectsService) CreateProjectApprovalRule(pid interface{}, opt *CreateProjectLevelRuleOptions, options ...RequestOptionFunc) (*ProjectApprovalRule, *Response, error) { 1771 project, err := parseID(pid) 1772 if err != nil { 1773 return nil, nil, err 1774 } 1775 u := fmt.Sprintf("projects/%s/approval_rules", PathEscape(project)) 1776 1777 req, err := s.client.NewRequest(http.MethodPost, u, opt, options) 1778 if err != nil { 1779 return nil, nil, err 1780 } 1781 1782 par := new(ProjectApprovalRule) 1783 resp, err := s.client.Do(req, &par) 1784 if err != nil { 1785 return nil, resp, err 1786 } 1787 1788 return par, resp, err 1789} 1790 1791// UpdateProjectLevelRuleOptions represents the available UpdateProjectApprovalRule() 1792// options. 1793// 1794// GitLab API docs: 1795// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-project-level-rules 1796type UpdateProjectLevelRuleOptions struct { 1797 Name *string `url:"name,omitempty" json:"name,omitempty"` 1798 ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` 1799 UserIDs *[]int `url:"user_ids,omitempty" json:"user_ids,omitempty"` 1800 GroupIDs *[]int `url:"group_ids,omitempty" json:"group_ids,omitempty"` 1801 ProtectedBranchIDs *[]int `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"` 1802} 1803 1804// UpdateProjectApprovalRule updates an existing approval rule with new options. 1805// 1806// GitLab API docs: 1807// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-project-level-rules 1808func (s *ProjectsService) UpdateProjectApprovalRule(pid interface{}, approvalRule int, opt *UpdateProjectLevelRuleOptions, options ...RequestOptionFunc) (*ProjectApprovalRule, *Response, error) { 1809 project, err := parseID(pid) 1810 if err != nil { 1811 return nil, nil, err 1812 } 1813 u := fmt.Sprintf("projects/%s/approval_rules/%d", PathEscape(project), approvalRule) 1814 1815 req, err := s.client.NewRequest(http.MethodPut, u, opt, options) 1816 if err != nil { 1817 return nil, nil, err 1818 } 1819 1820 par := new(ProjectApprovalRule) 1821 resp, err := s.client.Do(req, &par) 1822 if err != nil { 1823 return nil, resp, err 1824 } 1825 1826 return par, resp, err 1827} 1828 1829// DeleteProjectApprovalRule deletes a project-level approval rule. 1830// 1831// GitLab API docs: 1832// https://docs.gitlab.com/ee/api/merge_request_approvals.html#delete-project-level-rules 1833func (s *ProjectsService) DeleteProjectApprovalRule(pid interface{}, approvalRule int, options ...RequestOptionFunc) (*Response, error) { 1834 project, err := parseID(pid) 1835 if err != nil { 1836 return nil, err 1837 } 1838 u := fmt.Sprintf("projects/%s/approval_rules/%d", PathEscape(project), approvalRule) 1839 1840 req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) 1841 if err != nil { 1842 return nil, err 1843 } 1844 1845 return s.client.Do(req, nil) 1846} 1847 1848// ChangeAllowedApproversOptions represents the available ChangeAllowedApprovers() 1849// options. 1850// 1851// GitLab API docs: 1852// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers 1853type ChangeAllowedApproversOptions struct { 1854 ApproverGroupIDs *[]int `url:"approver_group_ids,omitempty" json:"approver_group_ids,omitempty"` 1855 ApproverIDs *[]int `url:"approver_ids,omitempty" json:"approver_ids,omitempty"` 1856} 1857 1858// ChangeAllowedApprovers updates the list of approvers and approver groups. 1859// 1860// GitLab API docs: 1861// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers 1862func (s *ProjectsService) ChangeAllowedApprovers(pid interface{}, opt *ChangeAllowedApproversOptions, options ...RequestOptionFunc) (*ProjectApprovals, *Response, error) { 1863 project, err := parseID(pid) 1864 if err != nil { 1865 return nil, nil, err 1866 } 1867 u := fmt.Sprintf("projects/%s/approvers", PathEscape(project)) 1868 1869 req, err := s.client.NewRequest(http.MethodPut, u, opt, options) 1870 if err != nil { 1871 return nil, nil, err 1872 } 1873 1874 pa := new(ProjectApprovals) 1875 resp, err := s.client.Do(req, pa) 1876 if err != nil { 1877 return nil, resp, err 1878 } 1879 1880 return pa, resp, err 1881} 1882 1883// StartMirroringProject start the pull mirroring process for a project. 1884// 1885// GitLab API docs: 1886// https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project-starter 1887func (s *ProjectsService) StartMirroringProject(pid interface{}, options ...RequestOptionFunc) (*Response, error) { 1888 project, err := parseID(pid) 1889 if err != nil { 1890 return nil, err 1891 } 1892 u := fmt.Sprintf("projects/%s/mirror/pull", PathEscape(project)) 1893 1894 req, err := s.client.NewRequest(http.MethodPost, u, nil, options) 1895 if err != nil { 1896 return nil, err 1897 } 1898 1899 resp, err := s.client.Do(req, nil) 1900 if err != nil { 1901 return resp, err 1902 } 1903 1904 return resp, err 1905} 1906 1907// TransferProjectOptions represents the available TransferProject() options. 1908// 1909// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#transfer-a-project-to-a-new-namespace 1910type TransferProjectOptions struct { 1911 Namespace interface{} `url:"namespace,omitempty" json:"namespace,omitempty"` 1912} 1913 1914// TransferProject transfer a project into the specified namespace 1915// 1916// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#transfer-a-project-to-a-new-namespace 1917func (s *ProjectsService) TransferProject(pid interface{}, opt *TransferProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { 1918 project, err := parseID(pid) 1919 if err != nil { 1920 return nil, nil, err 1921 } 1922 u := fmt.Sprintf("projects/%s/transfer", PathEscape(project)) 1923 1924 req, err := s.client.NewRequest(http.MethodPut, u, opt, options) 1925 if err != nil { 1926 return nil, nil, err 1927 } 1928 1929 p := new(Project) 1930 resp, err := s.client.Do(req, p) 1931 if err != nil { 1932 return nil, resp, err 1933 } 1934 1935 return p, resp, err 1936} 1937