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