1//
2// Copyright 2017, 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 "io/ioutil"
24 "mime/multipart"
25 "os"
26 "time"
27)
28
29// ProjectsService handles communication with the repositories related methods
30// of the GitLab API.
31//
32// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
33type ProjectsService struct {
34 client *Client
35}
36
37// Project represents a GitLab project.
38//
39// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
40type Project struct {
41 ID int `json:"id"`
42 Description string `json:"description"`
43 DefaultBranch string `json:"default_branch"`
44 Public bool `json:"public"`
45 Visibility VisibilityValue `json:"visibility"`
46 SSHURLToRepo string `json:"ssh_url_to_repo"`
47 HTTPURLToRepo string `json:"http_url_to_repo"`
48 WebURL string `json:"web_url"`
49 ReadmeURL string `json:"readme_url"`
50 TagList []string `json:"tag_list"`
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 ContainerRegistryEnabled bool `json:"container_registry_enabled"`
65 CreatedAt *time.Time `json:"created_at,omitempty"`
66 LastActivityAt *time.Time `json:"last_activity_at,omitempty"`
67 CreatorID int `json:"creator_id"`
68 Namespace *ProjectNamespace `json:"namespace"`
69 ImportStatus string `json:"import_status"`
70 ImportError string `json:"import_error"`
71 Permissions *Permissions `json:"permissions"`
72 Archived bool `json:"archived"`
73 AvatarURL string `json:"avatar_url"`
74 SharedRunnersEnabled bool `json:"shared_runners_enabled"`
75 ForksCount int `json:"forks_count"`
76 StarCount int `json:"star_count"`
77 RunnersToken string `json:"runners_token"`
78 PublicBuilds bool `json:"public_builds"`
79 OnlyAllowMergeIfPipelineSucceeds bool `json:"only_allow_merge_if_pipeline_succeeds"`
80 OnlyAllowMergeIfAllDiscussionsAreResolved bool `json:"only_allow_merge_if_all_discussions_are_resolved"`
81 LFSEnabled bool `json:"lfs_enabled"`
82 RequestAccessEnabled bool `json:"request_access_enabled"`
83 MergeMethod MergeMethodValue `json:"merge_method"`
84 ForkedFromProject *ForkParent `json:"forked_from_project"`
85 Mirror bool `json:"mirror"`
86 MirrorUserID int `json:"mirror_user_id"`
87 MirrorTriggerBuilds bool `json:"mirror_trigger_builds"`
88 OnlyMirrorProtectedBranches bool `json:"only_mirror_protected_branches"`
89 MirrorOverwritesDivergedBranches bool `json:"mirror_overwrites_diverged_branches"`
90 SharedWithGroups []struct {
91 GroupID int `json:"group_id"`
92 GroupName string `json:"group_name"`
93 GroupAccessLevel int `json:"group_access_level"`
94 } `json:"shared_with_groups"`
95 Statistics *ProjectStatistics `json:"statistics"`
96 Links *Links `json:"_links,omitempty"`
97 CIConfigPath *string `json:"ci_config_path"`
98 CustomAttributes []*CustomAttribute `json:"custom_attributes"`
99}
100
101// Repository represents a repository.
102type Repository struct {
103 Name string `json:"name"`
104 Description string `json:"description"`
105 WebURL string `json:"web_url"`
106 AvatarURL string `json:"avatar_url"`
107 GitSSHURL string `json:"git_ssh_url"`
108 GitHTTPURL string `json:"git_http_url"`
109 Namespace string `json:"namespace"`
110 Visibility VisibilityValue `json:"visibility"`
111 PathWithNamespace string `json:"path_with_namespace"`
112 DefaultBranch string `json:"default_branch"`
113 Homepage string `json:"homepage"`
114 URL string `json:"url"`
115 SSHURL string `json:"ssh_url"`
116 HTTPURL string `json:"http_url"`
117}
118
119// ProjectNamespace represents a project namespace.
120type ProjectNamespace struct {
121 ID int `json:"id"`
122 Name string `json:"name"`
123 Path string `json:"path"`
124 Kind string `json:"kind"`
125 FullPath string `json:"full_path"`
126}
127
128// StorageStatistics represents a statistics record for a group or project.
129type StorageStatistics struct {
130 StorageSize int64 `json:"storage_size"`
131 RepositorySize int64 `json:"repository_size"`
132 LfsObjectsSize int64 `json:"lfs_objects_size"`
133 JobArtifactsSize int64 `json:"job_artifacts_size"`
134}
135
136// ProjectStatistics represents a statistics record for a project.
137type ProjectStatistics struct {
138 StorageStatistics
139 CommitCount int `json:"commit_count"`
140}
141
142// Permissions represents permissions.
143type Permissions struct {
144 ProjectAccess *ProjectAccess `json:"project_access"`
145 GroupAccess *GroupAccess `json:"group_access"`
146}
147
148// ProjectAccess represents project access.
149type ProjectAccess struct {
150 AccessLevel AccessLevelValue `json:"access_level"`
151 NotificationLevel NotificationLevelValue `json:"notification_level"`
152}
153
154// GroupAccess represents group access.
155type GroupAccess struct {
156 AccessLevel AccessLevelValue `json:"access_level"`
157 NotificationLevel NotificationLevelValue `json:"notification_level"`
158}
159
160// ForkParent represents the parent project when this is a fork.
161type ForkParent struct {
162 HTTPURLToRepo string `json:"http_url_to_repo"`
163 ID int `json:"id"`
164 Name string `json:"name"`
165 NameWithNamespace string `json:"name_with_namespace"`
166 Path string `json:"path"`
167 PathWithNamespace string `json:"path_with_namespace"`
168 WebURL string `json:"web_url"`
169}
170
171// Links represents a project web links for self, issues, merge_requests,
172// repo_branches, labels, events, members.
173type Links struct {
174 Self string `json:"self"`
175 Issues string `json:"issues"`
176 MergeRequests string `json:"merge_requests"`
177 RepoBranches string `json:"repo_branches"`
178 Labels string `json:"labels"`
179 Events string `json:"events"`
180 Members string `json:"members"`
181}
182
183func (s Project) String() string {
184 return Stringify(s)
185}
186
187// ProjectApprovalRule represents a GitLab project approval rule.
188//
189// GitLab API docs:
190// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules
191type ProjectApprovalRule struct {
192 ID int `json:"id"`
193 Name string `json:"name"`
194 RuleType string `json:"rule_type"`
195 EligibleApprovers []*BasicUser `json:"eligible_approvers"`
196 ApprovalsRequired int `json:"approvals_required"`
197 Users []*BasicUser `json:"users"`
198 Groups []*Group `json:"groups"`
199 ContainsHiddenGroups bool `json:"contains_hidden_groups"`
200}
201
202func (s ProjectApprovalRule) String() string {
203 return Stringify(s)
204}
205
206// ListProjectsOptions represents the available ListProjects() options.
207//
208// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-projects
209type ListProjectsOptions struct {
210 ListOptions
211 Archived *bool `url:"archived,omitempty" json:"archived,omitempty"`
212 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
213 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
214 Search *string `url:"search,omitempty" json:"search,omitempty"`
215 Simple *bool `url:"simple,omitempty" json:"simple,omitempty"`
216 Owned *bool `url:"owned,omitempty" json:"owned,omitempty"`
217 Membership *bool `url:"membership,omitempty" json:"membership,omitempty"`
218 Starred *bool `url:"starred,omitempty" json:"starred,omitempty"`
219 Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"`
220 Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
221 WithIssuesEnabled *bool `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"`
222 WithMergeRequestsEnabled *bool `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"`
223 MinAccessLevel *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"`
224 WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
225 WithProgrammingLanguage *string `url:"with_programming_language,omitempty" json:"with_programming_language,omitempty"`
226}
227
228// ListProjects gets a list of projects accessible by the authenticated user.
229//
230// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-projects
231func (s *ProjectsService) ListProjects(opt *ListProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
232 req, err := s.client.NewRequest("GET", "projects", opt, options)
233 if err != nil {
234 return nil, nil, err
235 }
236
237 var p []*Project
238 resp, err := s.client.Do(req, &p)
239 if err != nil {
240 return nil, resp, err
241 }
242
243 return p, resp, err
244}
245
246// ListUserProjects gets a list of projects for the given user.
247//
248// GitLab API docs:
249// https://docs.gitlab.com/ce/api/projects.html#list-user-projects
250func (s *ProjectsService) ListUserProjects(uid interface{}, opt *ListProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
251 user, err := parseID(uid)
252 if err != nil {
253 return nil, nil, err
254 }
255 u := fmt.Sprintf("users/%s/projects", user)
256
257 req, err := s.client.NewRequest("GET", u, opt, options)
258 if err != nil {
259 return nil, nil, err
260 }
261
262 var p []*Project
263 resp, err := s.client.Do(req, &p)
264 if err != nil {
265 return nil, resp, err
266 }
267
268 return p, resp, err
269}
270
271// ProjectUser represents a GitLab project user.
272type ProjectUser struct {
273 ID int `json:"id"`
274 Name string `json:"name"`
275 Username string `json:"username"`
276 State string `json:"state"`
277 AvatarURL string `json:"avatar_url"`
278 WebURL string `json:"web_url"`
279}
280
281// ListProjectUserOptions represents the available ListProjectsUsers() options.
282//
283// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#get-project-users
284type ListProjectUserOptions struct {
285 ListOptions
286 Search *string `url:"search,omitempty" json:"search,omitempty"`
287}
288
289// ListProjectsUsers gets a list of users for the given project.
290//
291// GitLab API docs:
292// https://docs.gitlab.com/ce/api/projects.html#get-project-users
293func (s *ProjectsService) ListProjectsUsers(pid interface{}, opt *ListProjectUserOptions, options ...OptionFunc) ([]*ProjectUser, *Response, error) {
294 project, err := parseID(pid)
295 if err != nil {
296 return nil, nil, err
297 }
298 u := fmt.Sprintf("projects/%s/users", pathEscape(project))
299
300 req, err := s.client.NewRequest("GET", u, opt, options)
301 if err != nil {
302 return nil, nil, err
303 }
304
305 var p []*ProjectUser
306 resp, err := s.client.Do(req, &p)
307 if err != nil {
308 return nil, resp, err
309 }
310
311 return p, resp, err
312}
313
314// ProjectLanguages is a map of strings because the response is arbitrary
315//
316// Gitlab API docs: https://docs.gitlab.com/ce/api/projects.html#languages
317type ProjectLanguages map[string]float32
318
319// GetProjectLanguages gets a list of languages used by the project
320//
321// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#languages
322func (s *ProjectsService) GetProjectLanguages(pid interface{}, options ...OptionFunc) (*ProjectLanguages, *Response, error) {
323 project, err := parseID(pid)
324 if err != nil {
325 return nil, nil, err
326 }
327 u := fmt.Sprintf("projects/%s/languages", pathEscape(project))
328
329 req, err := s.client.NewRequest("GET", u, nil, options)
330 if err != nil {
331 return nil, nil, err
332 }
333
334 p := new(ProjectLanguages)
335 resp, err := s.client.Do(req, p)
336 if err != nil {
337 return nil, resp, err
338 }
339
340 return p, resp, err
341}
342
343// GetProjectOptions represents the available GetProject() options.
344//
345// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#get-single-project
346type GetProjectOptions struct {
347 Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"`
348 License *bool `url:"license,omitempty" json:"license,omitempty"`
349 WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
350}
351
352// GetProject gets a specific project, identified by project ID or
353// NAMESPACE/PROJECT_NAME, which is owned by the authenticated user.
354//
355// GitLab API docs:
356// https://docs.gitlab.com/ce/api/projects.html#get-single-project
357func (s *ProjectsService) GetProject(pid interface{}, opt *GetProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
358 project, err := parseID(pid)
359 if err != nil {
360 return nil, nil, err
361 }
362 u := fmt.Sprintf("projects/%s", pathEscape(project))
363
364 req, err := s.client.NewRequest("GET", u, opt, options)
365 if err != nil {
366 return nil, nil, err
367 }
368
369 p := new(Project)
370 resp, err := s.client.Do(req, p)
371 if err != nil {
372 return nil, resp, err
373 }
374
375 return p, resp, err
376}
377
378// ProjectEvent represents a GitLab project event.
379//
380// GitLab API docs:
381// https://docs.gitlab.com/ce/api/projects.html#get-project-events
382type ProjectEvent struct {
383 Title interface{} `json:"title"`
384 ProjectID int `json:"project_id"`
385 ActionName string `json:"action_name"`
386 TargetID interface{} `json:"target_id"`
387 TargetType interface{} `json:"target_type"`
388 AuthorID int `json:"author_id"`
389 AuthorUsername string `json:"author_username"`
390 Data struct {
391 Before string `json:"before"`
392 After string `json:"after"`
393 Ref string `json:"ref"`
394 UserID int `json:"user_id"`
395 UserName string `json:"user_name"`
396 Repository *Repository `json:"repository"`
397 Commits []*Commit `json:"commits"`
398 TotalCommitsCount int `json:"total_commits_count"`
399 } `json:"data"`
400 TargetTitle interface{} `json:"target_title"`
401}
402
403func (s ProjectEvent) String() string {
404 return Stringify(s)
405}
406
407// GetProjectEventsOptions represents the available GetProjectEvents() options.
408//
409// GitLab API docs:
410// https://docs.gitlab.com/ce/api/projects.html#get-project-events
411type GetProjectEventsOptions ListOptions
412
413// GetProjectEvents gets the events for the specified project. Sorted from
414// newest to latest.
415//
416// GitLab API docs:
417// https://docs.gitlab.com/ce/api/projects.html#get-project-events
418func (s *ProjectsService) GetProjectEvents(pid interface{}, opt *GetProjectEventsOptions, options ...OptionFunc) ([]*ProjectEvent, *Response, error) {
419 project, err := parseID(pid)
420 if err != nil {
421 return nil, nil, err
422 }
423 u := fmt.Sprintf("projects/%s/events", pathEscape(project))
424
425 req, err := s.client.NewRequest("GET", u, opt, options)
426 if err != nil {
427 return nil, nil, err
428 }
429
430 var p []*ProjectEvent
431 resp, err := s.client.Do(req, &p)
432 if err != nil {
433 return nil, resp, err
434 }
435
436 return p, resp, err
437}
438
439// CreateProjectOptions represents the available CreateProject() options.
440//
441// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project
442type CreateProjectOptions struct {
443 Name *string `url:"name,omitempty" json:"name,omitempty"`
444 Path *string `url:"path,omitempty" json:"path,omitempty"`
445 DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"`
446 NamespaceID *int `url:"namespace_id,omitempty" json:"namespace_id,omitempty"`
447 Description *string `url:"description,omitempty" json:"description,omitempty"`
448 IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"`
449 MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"`
450 JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"`
451 WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"`
452 SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"`
453 ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"`
454 ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"`
455 SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
456 Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
457 ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"`
458 PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"`
459 OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"`
460 OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"`
461 MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"`
462 LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
463 RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
464 TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"`
465 PrintingMergeRequestLinkEnabled *bool `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"`
466 CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
467 ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
468 Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"`
469 MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"`
470 InitializeWithReadme *bool `url:"initialize_with_readme,omitempty" json:"initialize_with_readme,omitempty"`
471 TemplateName *string `url:"template_name,omitempty" json:"template_name,omitempty"`
472 UseCustomTemplate *bool `url:"use_custom_template,omitempty" json:"use_custom_template,omitempty"`
473 GroupWithProjectTemplatesID *int `url:"group_with_project_templates_id,omitempty" json:"group_with_project_templates_id,omitempty"`
474}
475
476// CreateProject creates a new project owned by the authenticated user.
477//
478// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#create-project
479func (s *ProjectsService) CreateProject(opt *CreateProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
480 req, err := s.client.NewRequest("POST", "projects", opt, options)
481 if err != nil {
482 return nil, nil, err
483 }
484
485 p := new(Project)
486 resp, err := s.client.Do(req, p)
487 if err != nil {
488 return nil, resp, err
489 }
490
491 return p, resp, err
492}
493
494// CreateProjectForUserOptions represents the available CreateProjectForUser()
495// options.
496//
497// GitLab API docs:
498// https://docs.gitlab.com/ce/api/projects.html#create-project-for-user
499type CreateProjectForUserOptions CreateProjectOptions
500
501// CreateProjectForUser creates a new project owned by the specified user.
502// Available only for admins.
503//
504// GitLab API docs:
505// https://docs.gitlab.com/ce/api/projects.html#create-project-for-user
506func (s *ProjectsService) CreateProjectForUser(user int, opt *CreateProjectForUserOptions, options ...OptionFunc) (*Project, *Response, error) {
507 u := fmt.Sprintf("projects/user/%d", user)
508
509 req, err := s.client.NewRequest("POST", u, opt, options)
510 if err != nil {
511 return nil, nil, err
512 }
513
514 p := new(Project)
515 resp, err := s.client.Do(req, p)
516 if err != nil {
517 return nil, resp, err
518 }
519
520 return p, resp, err
521}
522
523// EditProjectOptions represents the available EditProject() options.
524//
525// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project
526type EditProjectOptions struct {
527 Name *string `url:"name,omitempty" json:"name,omitempty"`
528 Path *string `url:"path,omitempty" json:"path,omitempty"`
529 DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"`
530 Description *string `url:"description,omitempty" json:"description,omitempty"`
531 IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"`
532 MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"`
533 JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"`
534 WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"`
535 SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"`
536 ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"`
537 ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"`
538 SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
539 Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
540 ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"`
541 PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"`
542 OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"`
543 OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"`
544 MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"`
545 LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
546 RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
547 TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"`
548 CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
549 ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
550 ExternalAuthorizationClassificationLabel *string `url:"external_authorization_classification_label,omitempty" json:"external_authorization_classification_label,omitempty"`
551 Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"`
552 MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"`
553 MirrorUserID *int `url:"mirror_user_id,omitempty" json:"mirror_user_id,omitempty"`
554 OnlyMirrorProtectedBranches *bool `url:"only_mirror_protected_branches,omitempty" json:"only_mirror_protected_branches,omitempty"`
555 MirrorOverwritesDivergedBranches *bool `url:"mirror_overwrites_diverged_branches,omitempty" json:"mirror_overwrites_diverged_branches,omitempty"`
556 PackagesEnabled *bool `url:"packages_enabled,omitempty" json:"packages_enabled,omitempty"`
557}
558
559// EditProject updates an existing project.
560//
561// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project
562func (s *ProjectsService) EditProject(pid interface{}, opt *EditProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
563 project, err := parseID(pid)
564 if err != nil {
565 return nil, nil, err
566 }
567 u := fmt.Sprintf("projects/%s", pathEscape(project))
568
569 req, err := s.client.NewRequest("PUT", u, opt, options)
570 if err != nil {
571 return nil, nil, err
572 }
573
574 p := new(Project)
575 resp, err := s.client.Do(req, p)
576 if err != nil {
577 return nil, resp, err
578 }
579
580 return p, resp, err
581}
582
583// ForkProjectOptions represents the available ForkProject() options.
584//
585// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#fork-project
586type ForkProjectOptions struct {
587 Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"`
588 Name *string `url:"name,omitempty" json:"name,omitempty" `
589 Path *string `url:"path,omitempty" json:"path,omitempty"`
590}
591
592// ForkProject forks a project into the user namespace of the authenticated
593// user.
594//
595// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#fork-project
596func (s *ProjectsService) ForkProject(pid interface{}, opt *ForkProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
597 project, err := parseID(pid)
598 if err != nil {
599 return nil, nil, err
600 }
601 u := fmt.Sprintf("projects/%s/fork", pathEscape(project))
602
603 req, err := s.client.NewRequest("POST", u, opt, options)
604 if err != nil {
605 return nil, nil, err
606 }
607
608 p := new(Project)
609 resp, err := s.client.Do(req, p)
610 if err != nil {
611 return nil, resp, err
612 }
613
614 return p, resp, err
615}
616
617// StarProject stars a given the project.
618//
619// GitLab API docs:
620// https://docs.gitlab.com/ce/api/projects.html#star-a-project
621func (s *ProjectsService) StarProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
622 project, err := parseID(pid)
623 if err != nil {
624 return nil, nil, err
625 }
626 u := fmt.Sprintf("projects/%s/star", pathEscape(project))
627
628 req, err := s.client.NewRequest("POST", u, nil, options)
629 if err != nil {
630 return nil, nil, err
631 }
632
633 p := new(Project)
634 resp, err := s.client.Do(req, p)
635 if err != nil {
636 return nil, resp, err
637 }
638
639 return p, resp, err
640}
641
642// UnstarProject unstars a given project.
643//
644// GitLab API docs:
645// https://docs.gitlab.com/ce/api/projects.html#unstar-a-project
646func (s *ProjectsService) UnstarProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
647 project, err := parseID(pid)
648 if err != nil {
649 return nil, nil, err
650 }
651 u := fmt.Sprintf("projects/%s/unstar", pathEscape(project))
652
653 req, err := s.client.NewRequest("POST", u, nil, options)
654 if err != nil {
655 return nil, nil, err
656 }
657
658 p := new(Project)
659 resp, err := s.client.Do(req, p)
660 if err != nil {
661 return nil, resp, err
662 }
663
664 return p, resp, err
665}
666
667// ArchiveProject archives the project if the user is either admin or the
668// project owner of this project.
669//
670// GitLab API docs:
671// https://docs.gitlab.com/ce/api/projects.html#archive-a-project
672func (s *ProjectsService) ArchiveProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
673 project, err := parseID(pid)
674 if err != nil {
675 return nil, nil, err
676 }
677 u := fmt.Sprintf("projects/%s/archive", pathEscape(project))
678
679 req, err := s.client.NewRequest("POST", u, nil, options)
680 if err != nil {
681 return nil, nil, err
682 }
683
684 p := new(Project)
685 resp, err := s.client.Do(req, p)
686 if err != nil {
687 return nil, resp, err
688 }
689
690 return p, resp, err
691}
692
693// UnarchiveProject unarchives the project if the user is either admin or
694// the project owner of this project.
695//
696// GitLab API docs:
697// https://docs.gitlab.com/ce/api/projects.html#unarchive-a-project
698func (s *ProjectsService) UnarchiveProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
699 project, err := parseID(pid)
700 if err != nil {
701 return nil, nil, err
702 }
703 u := fmt.Sprintf("projects/%s/unarchive", pathEscape(project))
704
705 req, err := s.client.NewRequest("POST", u, nil, options)
706 if err != nil {
707 return nil, nil, err
708 }
709
710 p := new(Project)
711 resp, err := s.client.Do(req, p)
712 if err != nil {
713 return nil, resp, err
714 }
715
716 return p, resp, err
717}
718
719// DeleteProject removes a project including all associated resources
720// (issues, merge requests etc.)
721//
722// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#remove-project
723func (s *ProjectsService) DeleteProject(pid interface{}, options ...OptionFunc) (*Response, error) {
724 project, err := parseID(pid)
725 if err != nil {
726 return nil, err
727 }
728 u := fmt.Sprintf("projects/%s", pathEscape(project))
729
730 req, err := s.client.NewRequest("DELETE", u, nil, options)
731 if err != nil {
732 return nil, err
733 }
734
735 return s.client.Do(req, nil)
736}
737
738// ShareWithGroupOptions represents options to share project with groups
739//
740// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#share-project-with-group
741type ShareWithGroupOptions struct {
742 GroupID *int `url:"group_id" json:"group_id"`
743 GroupAccess *AccessLevelValue `url:"group_access" json:"group_access"`
744 ExpiresAt *string `url:"expires_at" json:"expires_at"`
745}
746
747// ShareProjectWithGroup allows to share a project with a group.
748//
749// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#share-project-with-group
750func (s *ProjectsService) ShareProjectWithGroup(pid interface{}, opt *ShareWithGroupOptions, options ...OptionFunc) (*Response, error) {
751 project, err := parseID(pid)
752 if err != nil {
753 return nil, err
754 }
755 u := fmt.Sprintf("projects/%s/share", pathEscape(project))
756
757 req, err := s.client.NewRequest("POST", u, opt, options)
758 if err != nil {
759 return nil, err
760 }
761
762 return s.client.Do(req, nil)
763}
764
765// DeleteSharedProjectFromGroup allows to unshare a project from a group.
766//
767// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#delete-a-shared-project-link-within-a-group
768func (s *ProjectsService) DeleteSharedProjectFromGroup(pid interface{}, groupID int, options ...OptionFunc) (*Response, error) {
769 project, err := parseID(pid)
770 if err != nil {
771 return nil, err
772 }
773 u := fmt.Sprintf("projects/%s/share/%d", pathEscape(project), groupID)
774
775 req, err := s.client.NewRequest("DELETE", u, nil, options)
776 if err != nil {
777 return nil, err
778 }
779
780 return s.client.Do(req, nil)
781}
782
783// ProjectMember represents a project member.
784//
785// GitLab API docs:
786// https://docs.gitlab.com/ce/api/projects.html#list-project-team-members
787type ProjectMember struct {
788 ID int `json:"id"`
789 Username string `json:"username"`
790 Email string `json:"email"`
791 Name string `json:"name"`
792 State string `json:"state"`
793 CreatedAt *time.Time `json:"created_at"`
794 ExpiresAt *ISOTime `json:"expires_at"`
795 AccessLevel AccessLevelValue `json:"access_level"`
796}
797
798// ProjectHook represents a project hook.
799//
800// GitLab API docs:
801// https://docs.gitlab.com/ce/api/projects.html#list-project-hooks
802type ProjectHook struct {
803 ID int `json:"id"`
804 URL string `json:"url"`
805 ProjectID int `json:"project_id"`
806 PushEvents bool `json:"push_events"`
807 IssuesEvents bool `json:"issues_events"`
808 ConfidentialIssuesEvents bool `json:"confidential_issues_events"`
809 MergeRequestsEvents bool `json:"merge_requests_events"`
810 TagPushEvents bool `json:"tag_push_events"`
811 NoteEvents bool `json:"note_events"`
812 JobEvents bool `json:"job_events"`
813 PipelineEvents bool `json:"pipeline_events"`
814 WikiPageEvents bool `json:"wiki_page_events"`
815 EnableSSLVerification bool `json:"enable_ssl_verification"`
816 CreatedAt *time.Time `json:"created_at"`
817}
818
819// ListProjectHooksOptions represents the available ListProjectHooks() options.
820//
821// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-project-hooks
822type ListProjectHooksOptions ListOptions
823
824// ListProjectHooks gets a list of project hooks.
825//
826// GitLab API docs:
827// https://docs.gitlab.com/ce/api/projects.html#list-project-hooks
828func (s *ProjectsService) ListProjectHooks(pid interface{}, opt *ListProjectHooksOptions, options ...OptionFunc) ([]*ProjectHook, *Response, error) {
829 project, err := parseID(pid)
830 if err != nil {
831 return nil, nil, err
832 }
833 u := fmt.Sprintf("projects/%s/hooks", pathEscape(project))
834
835 req, err := s.client.NewRequest("GET", u, opt, options)
836 if err != nil {
837 return nil, nil, err
838 }
839
840 var ph []*ProjectHook
841 resp, err := s.client.Do(req, &ph)
842 if err != nil {
843 return nil, resp, err
844 }
845
846 return ph, resp, err
847}
848
849// GetProjectHook gets a specific hook for a project.
850//
851// GitLab API docs:
852// https://docs.gitlab.com/ce/api/projects.html#get-project-hook
853func (s *ProjectsService) GetProjectHook(pid interface{}, hook int, options ...OptionFunc) (*ProjectHook, *Response, error) {
854 project, err := parseID(pid)
855 if err != nil {
856 return nil, nil, err
857 }
858 u := fmt.Sprintf("projects/%s/hooks/%d", pathEscape(project), hook)
859
860 req, err := s.client.NewRequest("GET", u, nil, options)
861 if err != nil {
862 return nil, nil, err
863 }
864
865 ph := new(ProjectHook)
866 resp, err := s.client.Do(req, ph)
867 if err != nil {
868 return nil, resp, err
869 }
870
871 return ph, resp, err
872}
873
874// AddProjectHookOptions represents the available AddProjectHook() options.
875//
876// GitLab API docs:
877// https://docs.gitlab.com/ce/api/projects.html#add-project-hook
878type AddProjectHookOptions struct {
879 URL *string `url:"url,omitempty" json:"url,omitempty"`
880 PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
881 IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
882 ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
883 MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
884 TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
885 NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"`
886 JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"`
887 PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
888 WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
889 EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
890 Token *string `url:"token,omitempty" json:"token,omitempty"`
891}
892
893// AddProjectHook adds a hook to a specified project.
894//
895// GitLab API docs:
896// https://docs.gitlab.com/ce/api/projects.html#add-project-hook
897func (s *ProjectsService) AddProjectHook(pid interface{}, opt *AddProjectHookOptions, options ...OptionFunc) (*ProjectHook, *Response, error) {
898 project, err := parseID(pid)
899 if err != nil {
900 return nil, nil, err
901 }
902 u := fmt.Sprintf("projects/%s/hooks", pathEscape(project))
903
904 req, err := s.client.NewRequest("POST", u, opt, options)
905 if err != nil {
906 return nil, nil, err
907 }
908
909 ph := new(ProjectHook)
910 resp, err := s.client.Do(req, ph)
911 if err != nil {
912 return nil, resp, err
913 }
914
915 return ph, resp, err
916}
917
918// EditProjectHookOptions represents the available EditProjectHook() options.
919//
920// GitLab API docs:
921// https://docs.gitlab.com/ce/api/projects.html#edit-project-hook
922type EditProjectHookOptions struct {
923 URL *string `url:"url,omitempty" json:"url,omitempty"`
924 PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
925 IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
926 ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
927 MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
928 TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
929 NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"`
930 JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"`
931 PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
932 WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
933 EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
934 Token *string `url:"token,omitempty" json:"token,omitempty"`
935}
936
937// EditProjectHook edits a hook for a specified project.
938//
939// GitLab API docs:
940// https://docs.gitlab.com/ce/api/projects.html#edit-project-hook
941func (s *ProjectsService) EditProjectHook(pid interface{}, hook int, opt *EditProjectHookOptions, options ...OptionFunc) (*ProjectHook, *Response, error) {
942 project, err := parseID(pid)
943 if err != nil {
944 return nil, nil, err
945 }
946 u := fmt.Sprintf("projects/%s/hooks/%d", pathEscape(project), hook)
947
948 req, err := s.client.NewRequest("PUT", u, opt, options)
949 if err != nil {
950 return nil, nil, err
951 }
952
953 ph := new(ProjectHook)
954 resp, err := s.client.Do(req, ph)
955 if err != nil {
956 return nil, resp, err
957 }
958
959 return ph, resp, err
960}
961
962// DeleteProjectHook removes a hook from a project. This is an idempotent
963// method and can be called multiple times. Either the hook is available or not.
964//
965// GitLab API docs:
966// https://docs.gitlab.com/ce/api/projects.html#delete-project-hook
967func (s *ProjectsService) DeleteProjectHook(pid interface{}, hook int, options ...OptionFunc) (*Response, error) {
968 project, err := parseID(pid)
969 if err != nil {
970 return nil, err
971 }
972 u := fmt.Sprintf("projects/%s/hooks/%d", pathEscape(project), hook)
973
974 req, err := s.client.NewRequest("DELETE", u, nil, options)
975 if err != nil {
976 return nil, err
977 }
978
979 return s.client.Do(req, nil)
980}
981
982// ProjectForkRelation represents a project fork relationship.
983//
984// GitLab API docs:
985// https://docs.gitlab.com/ce/api/projects.html#admin-fork-relation
986type ProjectForkRelation struct {
987 ID int `json:"id"`
988 ForkedToProjectID int `json:"forked_to_project_id"`
989 ForkedFromProjectID int `json:"forked_from_project_id"`
990 CreatedAt *time.Time `json:"created_at"`
991 UpdatedAt *time.Time `json:"updated_at"`
992}
993
994// CreateProjectForkRelation creates a forked from/to relation between
995// existing projects.
996//
997// GitLab API docs:
998// https://docs.gitlab.com/ce/api/projects.html#create-a-forked-fromto-relation-between-existing-projects.
999func (s *ProjectsService) CreateProjectForkRelation(pid int, fork int, options ...OptionFunc) (*ProjectForkRelation, *Response, error) {
1000 u := fmt.Sprintf("projects/%d/fork/%d", pid, fork)
1001
1002 req, err := s.client.NewRequest("POST", u, nil, options)
1003 if err != nil {
1004 return nil, nil, err
1005 }
1006
1007 pfr := new(ProjectForkRelation)
1008 resp, err := s.client.Do(req, pfr)
1009 if err != nil {
1010 return nil, resp, err
1011 }
1012
1013 return pfr, resp, err
1014}
1015
1016// DeleteProjectForkRelation deletes an existing forked from relationship.
1017//
1018// GitLab API docs:
1019// https://docs.gitlab.com/ce/api/projects.html#delete-an-existing-forked-from-relationship
1020func (s *ProjectsService) DeleteProjectForkRelation(pid int, options ...OptionFunc) (*Response, error) {
1021 u := fmt.Sprintf("projects/%d/fork", pid)
1022
1023 req, err := s.client.NewRequest("DELETE", u, nil, options)
1024 if err != nil {
1025 return nil, err
1026 }
1027
1028 return s.client.Do(req, nil)
1029}
1030
1031// ProjectFile represents an uploaded project file
1032//
1033// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#upload-a-file
1034type ProjectFile struct {
1035 Alt string `json:"alt"`
1036 URL string `json:"url"`
1037 Markdown string `json:"markdown"`
1038}
1039
1040// UploadFile upload a file from disk
1041//
1042// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#upload-a-file
1043func (s *ProjectsService) UploadFile(pid interface{}, file string, options ...OptionFunc) (*ProjectFile, *Response, error) {
1044 project, err := parseID(pid)
1045 if err != nil {
1046 return nil, nil, err
1047 }
1048 u := fmt.Sprintf("projects/%s/uploads", pathEscape(project))
1049
1050 f, err := os.Open(file)
1051 if err != nil {
1052 return nil, nil, err
1053 }
1054 defer f.Close()
1055
1056 b := &bytes.Buffer{}
1057 w := multipart.NewWriter(b)
1058
1059 fw, err := w.CreateFormFile("file", file)
1060 if err != nil {
1061 return nil, nil, err
1062 }
1063
1064 _, err = io.Copy(fw, f)
1065 if err != nil {
1066 return nil, nil, err
1067 }
1068 w.Close()
1069
1070 req, err := s.client.NewRequest("", u, nil, options)
1071 if err != nil {
1072 return nil, nil, err
1073 }
1074
1075 req.Body = ioutil.NopCloser(b)
1076 req.ContentLength = int64(b.Len())
1077 req.Header.Set("Content-Type", w.FormDataContentType())
1078 req.Method = "POST"
1079
1080 uf := &ProjectFile{}
1081 resp, err := s.client.Do(req, uf)
1082 if err != nil {
1083 return nil, resp, err
1084 }
1085
1086 return uf, resp, nil
1087}
1088
1089// ListProjectForks gets a list of project forks.
1090//
1091// GitLab API docs:
1092// https://docs.gitlab.com/ce/api/projects.html#list-forks-of-a-project
1093func (s *ProjectsService) ListProjectForks(pid interface{}, opt *ListProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
1094 project, err := parseID(pid)
1095 if err != nil {
1096 return nil, nil, err
1097 }
1098 u := fmt.Sprintf("projects/%s/forks", pathEscape(project))
1099
1100 req, err := s.client.NewRequest("GET", u, opt, options)
1101 if err != nil {
1102 return nil, nil, err
1103 }
1104
1105 var forks []*Project
1106 resp, err := s.client.Do(req, &forks)
1107 if err != nil {
1108 return nil, resp, err
1109 }
1110
1111 return forks, resp, err
1112}
1113
1114// ProjectPushRules represents a project push rule.
1115//
1116// GitLab API docs:
1117// https://docs.gitlab.com/ee/api/projects.html#push-rules
1118type ProjectPushRules struct {
1119 ID int `json:"id"`
1120 ProjectID int `json:"project_id"`
1121 CommitMessageRegex string `json:"commit_message_regex"`
1122 BranchNameRegex string `json:"branch_name_regex"`
1123 DenyDeleteTag bool `json:"deny_delete_tag"`
1124 CreatedAt *time.Time `json:"created_at"`
1125 MemberCheck bool `json:"member_check"`
1126 PreventSecrets bool `json:"prevent_secrets"`
1127 AuthorEmailRegex string `json:"author_email_regex"`
1128 FileNameRegex string `json:"file_name_regex"`
1129 MaxFileSize int `json:"max_file_size"`
1130}
1131
1132// GetProjectPushRules gets the push rules of a project.
1133//
1134// GitLab API docs:
1135// https://docs.gitlab.com/ee/api/projects.html#get-project-push-rules
1136func (s *ProjectsService) GetProjectPushRules(pid interface{}, options ...OptionFunc) (*ProjectPushRules, *Response, error) {
1137 project, err := parseID(pid)
1138 if err != nil {
1139 return nil, nil, err
1140 }
1141 u := fmt.Sprintf("projects/%s/push_rule", pathEscape(project))
1142
1143 req, err := s.client.NewRequest("GET", u, nil, options)
1144 if err != nil {
1145 return nil, nil, err
1146 }
1147
1148 ppr := new(ProjectPushRules)
1149 resp, err := s.client.Do(req, ppr)
1150 if err != nil {
1151 return nil, resp, err
1152 }
1153
1154 return ppr, resp, err
1155}
1156
1157// AddProjectPushRuleOptions represents the available AddProjectPushRule()
1158// options.
1159//
1160// GitLab API docs:
1161// https://docs.gitlab.com/ee/api/projects.html#add-project-push-rule
1162type AddProjectPushRuleOptions struct {
1163 DenyDeleteTag *bool `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"`
1164 MemberCheck *bool `url:"member_check,omitempty" json:"member_check,omitempty"`
1165 PreventSecrets *bool `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"`
1166 CommitMessageRegex *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"`
1167 BranchNameRegex *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"`
1168 AuthorEmailRegex *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"`
1169 FileNameRegex *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"`
1170 MaxFileSize *int `url:"max_file_size,omitempty" json:"max_file_size,omitempty"`
1171}
1172
1173// AddProjectPushRule adds a push rule to a specified project.
1174//
1175// GitLab API docs:
1176// https://docs.gitlab.com/ee/api/projects.html#add-project-push-rule
1177func (s *ProjectsService) AddProjectPushRule(pid interface{}, opt *AddProjectPushRuleOptions, options ...OptionFunc) (*ProjectPushRules, *Response, error) {
1178 project, err := parseID(pid)
1179 if err != nil {
1180 return nil, nil, err
1181 }
1182 u := fmt.Sprintf("projects/%s/push_rule", pathEscape(project))
1183
1184 req, err := s.client.NewRequest("POST", u, opt, options)
1185 if err != nil {
1186 return nil, nil, err
1187 }
1188
1189 ppr := new(ProjectPushRules)
1190 resp, err := s.client.Do(req, ppr)
1191 if err != nil {
1192 return nil, resp, err
1193 }
1194
1195 return ppr, resp, err
1196}
1197
1198// EditProjectPushRuleOptions represents the available EditProjectPushRule()
1199// options.
1200//
1201// GitLab API docs:
1202// https://docs.gitlab.com/ee/api/projects.html#edit-project-push-rule
1203type EditProjectPushRuleOptions struct {
1204 AuthorEmailRegex *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"`
1205 BranchNameRegex *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"`
1206 CommitMessageRegex *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"`
1207 FileNameRegex *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"`
1208 DenyDeleteTag *bool `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"`
1209 MemberCheck *bool `url:"member_check,omitempty" json:"member_check,omitempty"`
1210 PreventSecrets *bool `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"`
1211 MaxFileSize *int `url:"max_file_size,omitempty" json:"max_file_size,omitempty"`
1212}
1213
1214// EditProjectPushRule edits a push rule for a specified project.
1215//
1216// GitLab API docs:
1217// https://docs.gitlab.com/ee/api/projects.html#edit-project-push-rule
1218func (s *ProjectsService) EditProjectPushRule(pid interface{}, opt *EditProjectPushRuleOptions, options ...OptionFunc) (*ProjectPushRules, *Response, error) {
1219 project, err := parseID(pid)
1220 if err != nil {
1221 return nil, nil, err
1222 }
1223 u := fmt.Sprintf("projects/%s/push_rule", pathEscape(project))
1224
1225 req, err := s.client.NewRequest("PUT", u, opt, options)
1226 if err != nil {
1227 return nil, nil, err
1228 }
1229
1230 ppr := new(ProjectPushRules)
1231 resp, err := s.client.Do(req, ppr)
1232 if err != nil {
1233 return nil, resp, err
1234 }
1235
1236 return ppr, resp, err
1237}
1238
1239// DeleteProjectPushRule removes a push rule from a project. This is an
1240// idempotent method and can be called multiple times. Either the push rule is
1241// available or not.
1242//
1243// GitLab API docs:
1244// https://docs.gitlab.com/ee/api/projects.html#delete-project-push-rule
1245func (s *ProjectsService) DeleteProjectPushRule(pid interface{}, options ...OptionFunc) (*Response, error) {
1246 project, err := parseID(pid)
1247 if err != nil {
1248 return nil, err
1249 }
1250 u := fmt.Sprintf("projects/%s/push_rule", pathEscape(project))
1251
1252 req, err := s.client.NewRequest("DELETE", u, nil, options)
1253 if err != nil {
1254 return nil, err
1255 }
1256
1257 return s.client.Do(req, nil)
1258}
1259
1260// ProjectApprovals represents GitLab project level merge request approvals.
1261//
1262// GitLab API docs:
1263// https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals
1264type ProjectApprovals struct {
1265 Approvers []*MergeRequestApproverUser `json:"approvers"`
1266 ApproverGroups []*MergeRequestApproverGroup `json:"approver_groups"`
1267 ApprovalsBeforeMerge int `json:"approvals_before_merge"`
1268 ResetApprovalsOnPush bool `json:"reset_approvals_on_push"`
1269 DisableOverridingApproversPerMergeRequest bool `json:"disable_overriding_approvers_per_merge_request"`
1270 MergeRequestsAuthorApproval bool `json:"merge_requests_author_approval"`
1271 MergeRequestsDisableCommittersApproval bool `json:"merge_requests_disable_committers_approval"`
1272}
1273
1274// GetApprovalConfiguration get the approval configuration for a project.
1275//
1276// GitLab API docs:
1277// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-configuration
1278func (s *ProjectsService) GetApprovalConfiguration(pid interface{}, options ...OptionFunc) (*ProjectApprovals, *Response, error) {
1279 project, err := parseID(pid)
1280 if err != nil {
1281 return nil, nil, err
1282 }
1283 u := fmt.Sprintf("projects/%s/approvals", pathEscape(project))
1284
1285 req, err := s.client.NewRequest("GET", u, nil, options)
1286 if err != nil {
1287 return nil, nil, err
1288 }
1289
1290 pa := new(ProjectApprovals)
1291 resp, err := s.client.Do(req, pa)
1292 if err != nil {
1293 return nil, resp, err
1294 }
1295
1296 return pa, resp, err
1297}
1298
1299// ChangeApprovalConfigurationOptions represents the available
1300// ApprovalConfiguration() options.
1301//
1302// GitLab API docs:
1303// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-configuration
1304type ChangeApprovalConfigurationOptions struct {
1305 ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
1306 ResetApprovalsOnPush *bool `url:"reset_approvals_on_push,omitempty" json:"reset_approvals_on_push,omitempty"`
1307 DisableOverridingApproversPerMergeRequest *bool `url:"disable_overriding_approvers_per_merge_request,omitempty" json:"disable_overriding_approvers_per_merge_request,omitempty"`
1308 MergeRequestsAuthorApproval *bool `url:"merge_requests_author_approval,omitempty" json:"merge_requests_author_approval,omitempty"`
1309 MergeRequestsDisableCommittersApproval *bool `url:"merge_requests_disable_committers_approval,omitempty" json:"merge_requests_disable_committers_approval,omitempty"`
1310}
1311
1312// ChangeApprovalConfiguration updates the approval configuration for a project.
1313//
1314// GitLab API docs:
1315// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-configuration
1316func (s *ProjectsService) ChangeApprovalConfiguration(pid interface{}, opt *ChangeApprovalConfigurationOptions, options ...OptionFunc) (*ProjectApprovals, *Response, error) {
1317 project, err := parseID(pid)
1318 if err != nil {
1319 return nil, nil, err
1320 }
1321 u := fmt.Sprintf("projects/%s/approvals", pathEscape(project))
1322
1323 req, err := s.client.NewRequest("POST", u, opt, options)
1324 if err != nil {
1325 return nil, nil, err
1326 }
1327
1328 pa := new(ProjectApprovals)
1329 resp, err := s.client.Do(req, pa)
1330 if err != nil {
1331 return nil, resp, err
1332 }
1333
1334 return pa, resp, err
1335}
1336
1337// GetProjectApprovalRules looks up the list of project level approvers.
1338//
1339// GitLab API docs:
1340// https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules
1341func (s *ProjectsService) GetProjectApprovalRules(pid interface{}, options ...OptionFunc) ([]*ProjectApprovalRule, *Response, error) {
1342 project, err := parseID(pid)
1343 if err != nil {
1344 return nil, nil, err
1345 }
1346 u := fmt.Sprintf("projects/%s/approval_rules", pathEscape(project))
1347
1348 req, err := s.client.NewRequest("GET", u, nil, options)
1349 if err != nil {
1350 return nil, nil, err
1351 }
1352
1353 var par []*ProjectApprovalRule
1354 resp, err := s.client.Do(req, &par)
1355 if err != nil {
1356 return nil, resp, err
1357 }
1358
1359 return par, resp, err
1360}
1361
1362// CreateProjectLevelRuleOptions represents the available CreateProjectApprovalRule()
1363// options.
1364//
1365// GitLab API docs:
1366// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-project-level-rules
1367type CreateProjectLevelRuleOptions struct {
1368 Name *string `url:"name,omitempty" json:"name,omitempty"`
1369 ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
1370 UserIDs []int `url:"user_ids,omitempty" json:"user_ids,omitempty"`
1371 GroupIDs []int `url:"group_ids,omitempty" json:"group_ids,omitempty"`
1372}
1373
1374// CreateProjectApprovalRule creates a new project-level approval rule.
1375//
1376// GitLab API docs:
1377// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-project-level-rules
1378func (s *ProjectsService) CreateProjectApprovalRule(pid interface{}, opt *CreateProjectLevelRuleOptions, options ...OptionFunc) (*ProjectApprovalRule, *Response, error) {
1379 project, err := parseID(pid)
1380 if err != nil {
1381 return nil, nil, err
1382 }
1383 u := fmt.Sprintf("projects/%s/approval_rules", pathEscape(project))
1384
1385 req, err := s.client.NewRequest("POST", u, opt, options)
1386 if err != nil {
1387 return nil, nil, err
1388 }
1389
1390 par := new(ProjectApprovalRule)
1391 resp, err := s.client.Do(req, &par)
1392 if err != nil {
1393 return nil, resp, err
1394 }
1395
1396 return par, resp, err
1397}
1398
1399// UpdateProjectLevelRuleOptions represents the available UpdateProjectApprovalRule()
1400// options.
1401//
1402// GitLab API docs:
1403// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-project-level-rules
1404type UpdateProjectLevelRuleOptions struct {
1405 Name *string `url:"name,omitempty" json:"name,omitempty"`
1406 ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
1407 UserIDs []int `url:"user_ids,omitempty" json:"user_ids,omitempty"`
1408 GroupIDs []int `url:"group_ids,omitempty" json:"group_ids,omitempty"`
1409}
1410
1411// UpdateProjectApprovalRule updates an existing approval rule with new options.
1412//
1413// GitLab API docs:
1414// https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-project-level-rules
1415func (s *ProjectsService) UpdateProjectApprovalRule(pid interface{}, approvalRule int, opt *UpdateProjectLevelRuleOptions, options ...OptionFunc) (*ProjectApprovalRule, *Response, error) {
1416 project, err := parseID(pid)
1417 if err != nil {
1418 return nil, nil, err
1419 }
1420 u := fmt.Sprintf("projects/%s/approval_rules/%d", pathEscape(project), approvalRule)
1421
1422 req, err := s.client.NewRequest("PUT", u, opt, options)
1423 if err != nil {
1424 return nil, nil, err
1425 }
1426
1427 par := new(ProjectApprovalRule)
1428 resp, err := s.client.Do(req, &par)
1429 if err != nil {
1430 return nil, resp, err
1431 }
1432
1433 return par, resp, err
1434}
1435
1436// DeleteProjectApprovalRule deletes a project-level approval rule.
1437//
1438// GitLab API docs:
1439// https://docs.gitlab.com/ee/api/merge_request_approvals.html#delete-project-level-rules
1440func (s *ProjectsService) DeleteProjectApprovalRule(pid interface{}, approvalRule int, options ...OptionFunc) (*Response, error) {
1441 project, err := parseID(pid)
1442 if err != nil {
1443 return nil, err
1444 }
1445 u := fmt.Sprintf("projects/%s/approval_rules/%d", pathEscape(project), approvalRule)
1446
1447 req, err := s.client.NewRequest("DELETE", u, nil, options)
1448 if err != nil {
1449 return nil, err
1450 }
1451
1452 return s.client.Do(req, nil)
1453}
1454
1455// ChangeAllowedApproversOptions represents the available ChangeAllowedApprovers()
1456// options.
1457//
1458// GitLab API docs:
1459// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers
1460type ChangeAllowedApproversOptions struct {
1461 ApproverIDs []int `url:"approver_ids,omitempty" json:"approver_ids,omitempty"`
1462 ApproverGroupIDs []int `url:"approver_group_ids,omitempty" json:"approver_group_ids,omitempty"`
1463}
1464
1465// ChangeAllowedApprovers updates the list of approvers and approver groups.
1466//
1467// GitLab API docs:
1468// https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers
1469func (s *ProjectsService) ChangeAllowedApprovers(pid interface{}, opt *ChangeAllowedApproversOptions, options ...OptionFunc) (*ProjectApprovals, *Response, error) {
1470 project, err := parseID(pid)
1471 if err != nil {
1472 return nil, nil, err
1473 }
1474 u := fmt.Sprintf("projects/%s/approvers", pathEscape(project))
1475
1476 req, err := s.client.NewRequest("PUT", u, opt, options)
1477 if err != nil {
1478 return nil, nil, err
1479 }
1480
1481 pa := new(ProjectApprovals)
1482 resp, err := s.client.Do(req, pa)
1483 if err != nil {
1484 return nil, resp, err
1485 }
1486
1487 return pa, resp, err
1488}
1489
1490// StartMirroringProject start the pull mirroring process for a project.
1491//
1492// GitLab API docs:
1493// https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project-starter
1494func (s *ProjectsService) StartMirroringProject(pid interface{}, options ...OptionFunc) (*Response, error) {
1495 project, err := parseID(pid)
1496 if err != nil {
1497 return nil, err
1498 }
1499 u := fmt.Sprintf("projects/%s/mirror/pull", pathEscape(project))
1500
1501 req, err := s.client.NewRequest("POST", u, nil, options)
1502 if err != nil {
1503 return nil, err
1504 }
1505
1506 resp, err := s.client.Do(req, nil)
1507 if err != nil {
1508 return resp, err
1509 }
1510
1511 return resp, err
1512}