1// Copyright 2013 The go-github AUTHORS. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6package github
7
8import (
9	"context"
10	"fmt"
11	"strings"
12)
13
14// RepositoriesService handles communication with the repository related
15// methods of the GitHub API.
16//
17// GitHub API docs: https://developer.github.com/v3/repos/
18type RepositoriesService service
19
20// Repository represents a GitHub repository.
21type Repository struct {
22	ID               *int64           `json:"id,omitempty"`
23	NodeID           *string          `json:"node_id,omitempty"`
24	Owner            *User            `json:"owner,omitempty"`
25	Name             *string          `json:"name,omitempty"`
26	FullName         *string          `json:"full_name,omitempty"`
27	Description      *string          `json:"description,omitempty"`
28	Homepage         *string          `json:"homepage,omitempty"`
29	CodeOfConduct    *CodeOfConduct   `json:"code_of_conduct,omitempty"`
30	DefaultBranch    *string          `json:"default_branch,omitempty"`
31	MasterBranch     *string          `json:"master_branch,omitempty"`
32	CreatedAt        *Timestamp       `json:"created_at,omitempty"`
33	PushedAt         *Timestamp       `json:"pushed_at,omitempty"`
34	UpdatedAt        *Timestamp       `json:"updated_at,omitempty"`
35	HTMLURL          *string          `json:"html_url,omitempty"`
36	CloneURL         *string          `json:"clone_url,omitempty"`
37	GitURL           *string          `json:"git_url,omitempty"`
38	MirrorURL        *string          `json:"mirror_url,omitempty"`
39	SSHURL           *string          `json:"ssh_url,omitempty"`
40	SVNURL           *string          `json:"svn_url,omitempty"`
41	Language         *string          `json:"language,omitempty"`
42	Fork             *bool            `json:"fork,omitempty"`
43	ForksCount       *int             `json:"forks_count,omitempty"`
44	NetworkCount     *int             `json:"network_count,omitempty"`
45	OpenIssuesCount  *int             `json:"open_issues_count,omitempty"`
46	StargazersCount  *int             `json:"stargazers_count,omitempty"`
47	SubscribersCount *int             `json:"subscribers_count,omitempty"`
48	WatchersCount    *int             `json:"watchers_count,omitempty"`
49	Size             *int             `json:"size,omitempty"`
50	AutoInit         *bool            `json:"auto_init,omitempty"`
51	Parent           *Repository      `json:"parent,omitempty"`
52	Source           *Repository      `json:"source,omitempty"`
53	Organization     *Organization    `json:"organization,omitempty"`
54	Permissions      *map[string]bool `json:"permissions,omitempty"`
55	AllowRebaseMerge *bool            `json:"allow_rebase_merge,omitempty"`
56	AllowSquashMerge *bool            `json:"allow_squash_merge,omitempty"`
57	AllowMergeCommit *bool            `json:"allow_merge_commit,omitempty"`
58	Topics           []string         `json:"topics,omitempty"`
59
60	// Only provided when using RepositoriesService.Get while in preview
61	License *License `json:"license,omitempty"`
62
63	// Additional mutable fields when creating and editing a repository
64	Private           *bool   `json:"private,omitempty"`
65	HasIssues         *bool   `json:"has_issues,omitempty"`
66	HasWiki           *bool   `json:"has_wiki,omitempty"`
67	HasPages          *bool   `json:"has_pages,omitempty"`
68	HasProjects       *bool   `json:"has_projects,omitempty"`
69	HasDownloads      *bool   `json:"has_downloads,omitempty"`
70	LicenseTemplate   *string `json:"license_template,omitempty"`
71	GitignoreTemplate *string `json:"gitignore_template,omitempty"`
72	Archived          *bool   `json:"archived,omitempty"`
73
74	// Creating an organization repository. Required for non-owners.
75	TeamID *int64 `json:"team_id,omitempty"`
76
77	// API URLs
78	URL              *string `json:"url,omitempty"`
79	ArchiveURL       *string `json:"archive_url,omitempty"`
80	AssigneesURL     *string `json:"assignees_url,omitempty"`
81	BlobsURL         *string `json:"blobs_url,omitempty"`
82	BranchesURL      *string `json:"branches_url,omitempty"`
83	CollaboratorsURL *string `json:"collaborators_url,omitempty"`
84	CommentsURL      *string `json:"comments_url,omitempty"`
85	CommitsURL       *string `json:"commits_url,omitempty"`
86	CompareURL       *string `json:"compare_url,omitempty"`
87	ContentsURL      *string `json:"contents_url,omitempty"`
88	ContributorsURL  *string `json:"contributors_url,omitempty"`
89	DeploymentsURL   *string `json:"deployments_url,omitempty"`
90	DownloadsURL     *string `json:"downloads_url,omitempty"`
91	EventsURL        *string `json:"events_url,omitempty"`
92	ForksURL         *string `json:"forks_url,omitempty"`
93	GitCommitsURL    *string `json:"git_commits_url,omitempty"`
94	GitRefsURL       *string `json:"git_refs_url,omitempty"`
95	GitTagsURL       *string `json:"git_tags_url,omitempty"`
96	HooksURL         *string `json:"hooks_url,omitempty"`
97	IssueCommentURL  *string `json:"issue_comment_url,omitempty"`
98	IssueEventsURL   *string `json:"issue_events_url,omitempty"`
99	IssuesURL        *string `json:"issues_url,omitempty"`
100	KeysURL          *string `json:"keys_url,omitempty"`
101	LabelsURL        *string `json:"labels_url,omitempty"`
102	LanguagesURL     *string `json:"languages_url,omitempty"`
103	MergesURL        *string `json:"merges_url,omitempty"`
104	MilestonesURL    *string `json:"milestones_url,omitempty"`
105	NotificationsURL *string `json:"notifications_url,omitempty"`
106	PullsURL         *string `json:"pulls_url,omitempty"`
107	ReleasesURL      *string `json:"releases_url,omitempty"`
108	StargazersURL    *string `json:"stargazers_url,omitempty"`
109	StatusesURL      *string `json:"statuses_url,omitempty"`
110	SubscribersURL   *string `json:"subscribers_url,omitempty"`
111	SubscriptionURL  *string `json:"subscription_url,omitempty"`
112	TagsURL          *string `json:"tags_url,omitempty"`
113	TreesURL         *string `json:"trees_url,omitempty"`
114	TeamsURL         *string `json:"teams_url,omitempty"`
115
116	// TextMatches is only populated from search results that request text matches
117	// See: search.go and https://developer.github.com/v3/search/#text-match-metadata
118	TextMatches []TextMatch `json:"text_matches,omitempty"`
119}
120
121func (r Repository) String() string {
122	return Stringify(r)
123}
124
125// RepositoryListOptions specifies the optional parameters to the
126// RepositoriesService.List method.
127type RepositoryListOptions struct {
128	// Visibility of repositories to list. Can be one of all, public, or private.
129	// Default: all
130	Visibility string `url:"visibility,omitempty"`
131
132	// List repos of given affiliation[s].
133	// Comma-separated list of values. Can include:
134	// * owner: Repositories that are owned by the authenticated user.
135	// * collaborator: Repositories that the user has been added to as a
136	//   collaborator.
137	// * organization_member: Repositories that the user has access to through
138	//   being a member of an organization. This includes every repository on
139	//   every team that the user is on.
140	// Default: owner,collaborator,organization_member
141	Affiliation string `url:"affiliation,omitempty"`
142
143	// Type of repositories to list.
144	// Can be one of all, owner, public, private, member. Default: all
145	// Will cause a 422 error if used in the same request as visibility or
146	// affiliation.
147	Type string `url:"type,omitempty"`
148
149	// How to sort the repository list. Can be one of created, updated, pushed,
150	// full_name. Default: full_name
151	Sort string `url:"sort,omitempty"`
152
153	// Direction in which to sort repositories. Can be one of asc or desc.
154	// Default: when using full_name: asc; otherwise desc
155	Direction string `url:"direction,omitempty"`
156
157	ListOptions
158}
159
160// List the repositories for a user. Passing the empty string will list
161// repositories for the authenticated user.
162//
163// GitHub API docs: https://developer.github.com/v3/repos/#list-user-repositories
164func (s *RepositoriesService) List(ctx context.Context, user string, opt *RepositoryListOptions) ([]*Repository, *Response, error) {
165	var u string
166	if user != "" {
167		u = fmt.Sprintf("users/%v/repos", user)
168	} else {
169		u = "user/repos"
170	}
171	u, err := addOptions(u, opt)
172	if err != nil {
173		return nil, nil, err
174	}
175
176	req, err := s.client.NewRequest("GET", u, nil)
177	if err != nil {
178		return nil, nil, err
179	}
180
181	// TODO: remove custom Accept headers when APIs fully launch.
182	acceptHeaders := []string{mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview}
183	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
184
185	var repos []*Repository
186	resp, err := s.client.Do(ctx, req, &repos)
187	if err != nil {
188		return nil, resp, err
189	}
190
191	return repos, resp, nil
192}
193
194// RepositoryListByOrgOptions specifies the optional parameters to the
195// RepositoriesService.ListByOrg method.
196type RepositoryListByOrgOptions struct {
197	// Type of repositories to list. Possible values are: all, public, private,
198	// forks, sources, member. Default is "all".
199	Type string `url:"type,omitempty"`
200
201	ListOptions
202}
203
204// ListByOrg lists the repositories for an organization.
205//
206// GitHub API docs: https://developer.github.com/v3/repos/#list-organization-repositories
207func (s *RepositoriesService) ListByOrg(ctx context.Context, org string, opt *RepositoryListByOrgOptions) ([]*Repository, *Response, error) {
208	u := fmt.Sprintf("orgs/%v/repos", org)
209	u, err := addOptions(u, opt)
210	if err != nil {
211		return nil, nil, err
212	}
213
214	req, err := s.client.NewRequest("GET", u, nil)
215	if err != nil {
216		return nil, nil, err
217	}
218
219	// TODO: remove custom Accept headers when APIs fully launch.
220	acceptHeaders := []string{mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview}
221	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
222
223	var repos []*Repository
224	resp, err := s.client.Do(ctx, req, &repos)
225	if err != nil {
226		return nil, resp, err
227	}
228
229	return repos, resp, nil
230}
231
232// RepositoryListAllOptions specifies the optional parameters to the
233// RepositoriesService.ListAll method.
234type RepositoryListAllOptions struct {
235	// ID of the last repository seen
236	Since int64 `url:"since,omitempty"`
237}
238
239// ListAll lists all GitHub repositories in the order that they were created.
240//
241// GitHub API docs: https://developer.github.com/v3/repos/#list-all-public-repositories
242func (s *RepositoriesService) ListAll(ctx context.Context, opt *RepositoryListAllOptions) ([]*Repository, *Response, error) {
243	u, err := addOptions("repositories", opt)
244	if err != nil {
245		return nil, nil, err
246	}
247
248	req, err := s.client.NewRequest("GET", u, nil)
249	if err != nil {
250		return nil, nil, err
251	}
252
253	var repos []*Repository
254	resp, err := s.client.Do(ctx, req, &repos)
255	if err != nil {
256		return nil, resp, err
257	}
258
259	return repos, resp, nil
260}
261
262// Create a new repository. If an organization is specified, the new
263// repository will be created under that org. If the empty string is
264// specified, it will be created for the authenticated user.
265//
266// GitHub API docs: https://developer.github.com/v3/repos/#create
267func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repository) (*Repository, *Response, error) {
268	var u string
269	if org != "" {
270		u = fmt.Sprintf("orgs/%v/repos", org)
271	} else {
272		u = "user/repos"
273	}
274
275	req, err := s.client.NewRequest("POST", u, repo)
276	if err != nil {
277		return nil, nil, err
278	}
279
280	r := new(Repository)
281	resp, err := s.client.Do(ctx, req, r)
282	if err != nil {
283		return nil, resp, err
284	}
285
286	return r, resp, nil
287}
288
289// Get fetches a repository.
290//
291// GitHub API docs: https://developer.github.com/v3/repos/#get
292func (s *RepositoriesService) Get(ctx context.Context, owner, repo string) (*Repository, *Response, error) {
293	u := fmt.Sprintf("repos/%v/%v", owner, repo)
294	req, err := s.client.NewRequest("GET", u, nil)
295	if err != nil {
296		return nil, nil, err
297	}
298
299	// TODO: remove custom Accept header when the license support fully launches
300	// https://developer.github.com/v3/licenses/#get-a-repositorys-license
301	acceptHeaders := []string{mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview}
302	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
303
304	repository := new(Repository)
305	resp, err := s.client.Do(ctx, req, repository)
306	if err != nil {
307		return nil, resp, err
308	}
309
310	return repository, resp, nil
311}
312
313// GetCodeOfConduct gets the contents of a repository's code of conduct.
314//
315// GitHub API docs: https://developer.github.com/v3/codes_of_conduct/#get-the-contents-of-a-repositorys-code-of-conduct
316func (s *RepositoriesService) GetCodeOfConduct(ctx context.Context, owner, repo string) (*CodeOfConduct, *Response, error) {
317	u := fmt.Sprintf("repos/%v/%v/community/code_of_conduct", owner, repo)
318	req, err := s.client.NewRequest("GET", u, nil)
319	if err != nil {
320		return nil, nil, err
321	}
322
323	// TODO: remove custom Accept header when this API fully launches.
324	req.Header.Set("Accept", mediaTypeCodesOfConductPreview)
325
326	coc := new(CodeOfConduct)
327	resp, err := s.client.Do(ctx, req, coc)
328	if err != nil {
329		return nil, resp, err
330	}
331
332	return coc, resp, nil
333}
334
335// GetByID fetches a repository.
336//
337// Note: GetByID uses the undocumented GitHub API endpoint /repositories/:id.
338func (s *RepositoriesService) GetByID(ctx context.Context, id int64) (*Repository, *Response, error) {
339	u := fmt.Sprintf("repositories/%d", id)
340	req, err := s.client.NewRequest("GET", u, nil)
341	if err != nil {
342		return nil, nil, err
343	}
344
345	repository := new(Repository)
346	resp, err := s.client.Do(ctx, req, repository)
347	if err != nil {
348		return nil, resp, err
349	}
350
351	return repository, resp, nil
352}
353
354// Edit updates a repository.
355//
356// GitHub API docs: https://developer.github.com/v3/repos/#edit
357func (s *RepositoriesService) Edit(ctx context.Context, owner, repo string, repository *Repository) (*Repository, *Response, error) {
358	u := fmt.Sprintf("repos/%v/%v", owner, repo)
359	req, err := s.client.NewRequest("PATCH", u, repository)
360	if err != nil {
361		return nil, nil, err
362	}
363
364	r := new(Repository)
365	resp, err := s.client.Do(ctx, req, r)
366	if err != nil {
367		return nil, resp, err
368	}
369
370	return r, resp, nil
371}
372
373// Delete a repository.
374//
375// GitHub API docs: https://developer.github.com/v3/repos/#delete-a-repository
376func (s *RepositoriesService) Delete(ctx context.Context, owner, repo string) (*Response, error) {
377	u := fmt.Sprintf("repos/%v/%v", owner, repo)
378	req, err := s.client.NewRequest("DELETE", u, nil)
379	if err != nil {
380		return nil, err
381	}
382
383	return s.client.Do(ctx, req, nil)
384}
385
386// Contributor represents a repository contributor
387type Contributor struct {
388	Login             *string `json:"login,omitempty"`
389	ID                *int64  `json:"id,omitempty"`
390	AvatarURL         *string `json:"avatar_url,omitempty"`
391	GravatarID        *string `json:"gravatar_id,omitempty"`
392	URL               *string `json:"url,omitempty"`
393	HTMLURL           *string `json:"html_url,omitempty"`
394	FollowersURL      *string `json:"followers_url,omitempty"`
395	FollowingURL      *string `json:"following_url,omitempty"`
396	GistsURL          *string `json:"gists_url,omitempty"`
397	StarredURL        *string `json:"starred_url,omitempty"`
398	SubscriptionsURL  *string `json:"subscriptions_url,omitempty"`
399	OrganizationsURL  *string `json:"organizations_url,omitempty"`
400	ReposURL          *string `json:"repos_url,omitempty"`
401	EventsURL         *string `json:"events_url,omitempty"`
402	ReceivedEventsURL *string `json:"received_events_url,omitempty"`
403	Type              *string `json:"type,omitempty"`
404	SiteAdmin         *bool   `json:"site_admin,omitempty"`
405	Contributions     *int    `json:"contributions,omitempty"`
406}
407
408// ListContributorsOptions specifies the optional parameters to the
409// RepositoriesService.ListContributors method.
410type ListContributorsOptions struct {
411	// Include anonymous contributors in results or not
412	Anon string `url:"anon,omitempty"`
413
414	ListOptions
415}
416
417// ListContributors lists contributors for a repository.
418//
419// GitHub API docs: https://developer.github.com/v3/repos/#list-contributors
420func (s *RepositoriesService) ListContributors(ctx context.Context, owner string, repository string, opt *ListContributorsOptions) ([]*Contributor, *Response, error) {
421	u := fmt.Sprintf("repos/%v/%v/contributors", owner, repository)
422	u, err := addOptions(u, opt)
423	if err != nil {
424		return nil, nil, err
425	}
426
427	req, err := s.client.NewRequest("GET", u, nil)
428	if err != nil {
429		return nil, nil, err
430	}
431
432	var contributor []*Contributor
433	resp, err := s.client.Do(ctx, req, &contributor)
434	if err != nil {
435		return nil, nil, err
436	}
437
438	return contributor, resp, nil
439}
440
441// ListLanguages lists languages for the specified repository. The returned map
442// specifies the languages and the number of bytes of code written in that
443// language. For example:
444//
445//     {
446//       "C": 78769,
447//       "Python": 7769
448//     }
449//
450// GitHub API docs: https://developer.github.com/v3/repos/#list-languages
451func (s *RepositoriesService) ListLanguages(ctx context.Context, owner string, repo string) (map[string]int, *Response, error) {
452	u := fmt.Sprintf("repos/%v/%v/languages", owner, repo)
453	req, err := s.client.NewRequest("GET", u, nil)
454	if err != nil {
455		return nil, nil, err
456	}
457
458	languages := make(map[string]int)
459	resp, err := s.client.Do(ctx, req, &languages)
460	if err != nil {
461		return nil, resp, err
462	}
463
464	return languages, resp, nil
465}
466
467// ListTeams lists the teams for the specified repository.
468//
469// GitHub API docs: https://developer.github.com/v3/repos/#list-teams
470func (s *RepositoriesService) ListTeams(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Team, *Response, error) {
471	u := fmt.Sprintf("repos/%v/%v/teams", owner, repo)
472	u, err := addOptions(u, opt)
473	if err != nil {
474		return nil, nil, err
475	}
476
477	req, err := s.client.NewRequest("GET", u, nil)
478	if err != nil {
479		return nil, nil, err
480	}
481
482	req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
483
484	var teams []*Team
485	resp, err := s.client.Do(ctx, req, &teams)
486	if err != nil {
487		return nil, resp, err
488	}
489
490	return teams, resp, nil
491}
492
493// RepositoryTag represents a repository tag.
494type RepositoryTag struct {
495	Name       *string `json:"name,omitempty"`
496	Commit     *Commit `json:"commit,omitempty"`
497	ZipballURL *string `json:"zipball_url,omitempty"`
498	TarballURL *string `json:"tarball_url,omitempty"`
499}
500
501// ListTags lists tags for the specified repository.
502//
503// GitHub API docs: https://developer.github.com/v3/repos/#list-tags
504func (s *RepositoriesService) ListTags(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*RepositoryTag, *Response, error) {
505	u := fmt.Sprintf("repos/%v/%v/tags", owner, repo)
506	u, err := addOptions(u, opt)
507	if err != nil {
508		return nil, nil, err
509	}
510
511	req, err := s.client.NewRequest("GET", u, nil)
512	if err != nil {
513		return nil, nil, err
514	}
515
516	var tags []*RepositoryTag
517	resp, err := s.client.Do(ctx, req, &tags)
518	if err != nil {
519		return nil, resp, err
520	}
521
522	return tags, resp, nil
523}
524
525// Branch represents a repository branch
526type Branch struct {
527	Name      *string           `json:"name,omitempty"`
528	Commit    *RepositoryCommit `json:"commit,omitempty"`
529	Protected *bool             `json:"protected,omitempty"`
530}
531
532// Protection represents a repository branch's protection.
533type Protection struct {
534	RequiredStatusChecks       *RequiredStatusChecks          `json:"required_status_checks"`
535	RequiredPullRequestReviews *PullRequestReviewsEnforcement `json:"required_pull_request_reviews"`
536	EnforceAdmins              *AdminEnforcement              `json:"enforce_admins"`
537	Restrictions               *BranchRestrictions            `json:"restrictions"`
538}
539
540// ProtectionRequest represents a request to create/edit a branch's protection.
541type ProtectionRequest struct {
542	RequiredStatusChecks       *RequiredStatusChecks                 `json:"required_status_checks"`
543	RequiredPullRequestReviews *PullRequestReviewsEnforcementRequest `json:"required_pull_request_reviews"`
544	EnforceAdmins              bool                                  `json:"enforce_admins"`
545	Restrictions               *BranchRestrictionsRequest            `json:"restrictions"`
546}
547
548// RequiredStatusChecks represents the protection status of a individual branch.
549type RequiredStatusChecks struct {
550	// Require branches to be up to date before merging. (Required.)
551	Strict bool `json:"strict"`
552	// The list of status checks to require in order to merge into this
553	// branch. (Required; use []string{} instead of nil for empty list.)
554	Contexts []string `json:"contexts"`
555}
556
557// RequiredStatusChecksRequest represents a request to edit a protected branch's status checks.
558type RequiredStatusChecksRequest struct {
559	Strict   *bool    `json:"strict,omitempty"`
560	Contexts []string `json:"contexts,omitempty"`
561}
562
563// PullRequestReviewsEnforcement represents the pull request reviews enforcement of a protected branch.
564type PullRequestReviewsEnforcement struct {
565	// Specifies which users and teams can dismiss pull request reviews.
566	DismissalRestrictions DismissalRestrictions `json:"dismissal_restrictions"`
567	// Specifies if approved reviews are dismissed automatically, when a new commit is pushed.
568	DismissStaleReviews bool `json:"dismiss_stale_reviews"`
569	// RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner.
570	RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"`
571	// RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged.
572	// Valid values are 1-6.
573	RequiredApprovingReviewCount int `json:"required_approving_review_count"`
574}
575
576// PullRequestReviewsEnforcementRequest represents request to set the pull request review
577// enforcement of a protected branch. It is separate from PullRequestReviewsEnforcement above
578// because the request structure is different from the response structure.
579type PullRequestReviewsEnforcementRequest struct {
580	// Specifies which users and teams should be allowed to dismiss pull request reviews.
581	// User and team dismissal restrictions are only available for
582	// organization-owned repositories. Must be nil for personal repositories.
583	DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"`
584	// Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. (Required)
585	DismissStaleReviews bool `json:"dismiss_stale_reviews"`
586	// RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner.
587	RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"`
588	// RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged.
589	// Valid values are 1-6.
590	RequiredApprovingReviewCount int `json:"required_approving_review_count"`
591}
592
593// PullRequestReviewsEnforcementUpdate represents request to patch the pull request review
594// enforcement of a protected branch. It is separate from PullRequestReviewsEnforcementRequest above
595// because the patch request does not require all fields to be initialized.
596type PullRequestReviewsEnforcementUpdate struct {
597	// Specifies which users and teams can dismiss pull request reviews. Can be omitted.
598	DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"`
599	// Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. Can be omitted.
600	DismissStaleReviews *bool `json:"dismiss_stale_reviews,omitempty"`
601	// RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner.
602	RequireCodeOwnerReviews bool `json:"require_code_owner_reviews,omitempty"`
603	// RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged.
604	// Valid values are 1 - 6.
605	RequiredApprovingReviewCount int `json:"required_approving_review_count"`
606}
607
608// AdminEnforcement represents the configuration to enforce required status checks for repository administrators.
609type AdminEnforcement struct {
610	URL     *string `json:"url,omitempty"`
611	Enabled bool    `json:"enabled"`
612}
613
614// BranchRestrictions represents the restriction that only certain users or
615// teams may push to a branch.
616type BranchRestrictions struct {
617	// The list of user logins with push access.
618	Users []*User `json:"users"`
619	// The list of team slugs with push access.
620	Teams []*Team `json:"teams"`
621}
622
623// BranchRestrictionsRequest represents the request to create/edit the
624// restriction that only certain users or teams may push to a branch. It is
625// separate from BranchRestrictions above because the request structure is
626// different from the response structure.
627type BranchRestrictionsRequest struct {
628	// The list of user logins with push access. (Required; use []string{} instead of nil for empty list.)
629	Users []string `json:"users"`
630	// The list of team slugs with push access. (Required; use []string{} instead of nil for empty list.)
631	Teams []string `json:"teams"`
632}
633
634// DismissalRestrictions specifies which users and teams can dismiss pull request reviews.
635type DismissalRestrictions struct {
636	// The list of users who can dimiss pull request reviews.
637	Users []*User `json:"users"`
638	// The list of teams which can dismiss pull request reviews.
639	Teams []*Team `json:"teams"`
640}
641
642// DismissalRestrictionsRequest represents the request to create/edit the
643// restriction to allows only specific users or teams to dimiss pull request reviews. It is
644// separate from DismissalRestrictions above because the request structure is
645// different from the response structure.
646// Note: Both Users and Teams must be nil, or both must be non-nil.
647type DismissalRestrictionsRequest struct {
648	// The list of user logins who can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.)
649	Users *[]string `json:"users,omitempty"`
650	// The list of team slugs which can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.)
651	Teams *[]string `json:"teams,omitempty"`
652}
653
654// ListBranches lists branches for the specified repository.
655//
656// GitHub API docs: https://developer.github.com/v3/repos/#list-branches
657func (s *RepositoriesService) ListBranches(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Branch, *Response, error) {
658	u := fmt.Sprintf("repos/%v/%v/branches", owner, repo)
659	u, err := addOptions(u, opt)
660	if err != nil {
661		return nil, nil, err
662	}
663
664	req, err := s.client.NewRequest("GET", u, nil)
665	if err != nil {
666		return nil, nil, err
667	}
668
669	// TODO: remove custom Accept header when this API fully launches
670	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
671
672	var branches []*Branch
673	resp, err := s.client.Do(ctx, req, &branches)
674	if err != nil {
675		return nil, resp, err
676	}
677
678	return branches, resp, nil
679}
680
681// GetBranch gets the specified branch for a repository.
682//
683// GitHub API docs: https://developer.github.com/v3/repos/#get-branch
684func (s *RepositoriesService) GetBranch(ctx context.Context, owner, repo, branch string) (*Branch, *Response, error) {
685	u := fmt.Sprintf("repos/%v/%v/branches/%v", owner, repo, branch)
686	req, err := s.client.NewRequest("GET", u, nil)
687	if err != nil {
688		return nil, nil, err
689	}
690
691	// TODO: remove custom Accept header when this API fully launches
692	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
693
694	b := new(Branch)
695	resp, err := s.client.Do(ctx, req, b)
696	if err != nil {
697		return nil, resp, err
698	}
699
700	return b, resp, nil
701}
702
703// GetBranchProtection gets the protection of a given branch.
704//
705// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-branch-protection
706func (s *RepositoriesService) GetBranchProtection(ctx context.Context, owner, repo, branch string) (*Protection, *Response, error) {
707	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch)
708	req, err := s.client.NewRequest("GET", u, nil)
709	if err != nil {
710		return nil, nil, err
711	}
712
713	// TODO: remove custom Accept header when this API fully launches
714	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
715
716	p := new(Protection)
717	resp, err := s.client.Do(ctx, req, p)
718	if err != nil {
719		return nil, resp, err
720	}
721
722	return p, resp, nil
723}
724
725// GetRequiredStatusChecks gets the required status checks for a given protected branch.
726//
727// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-required-status-checks-of-protected-branch
728func (s *RepositoriesService) GetRequiredStatusChecks(ctx context.Context, owner, repo, branch string) (*RequiredStatusChecks, *Response, error) {
729	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, branch)
730	req, err := s.client.NewRequest("GET", u, nil)
731	if err != nil {
732		return nil, nil, err
733	}
734
735	// TODO: remove custom Accept header when this API fully launches
736	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
737
738	p := new(RequiredStatusChecks)
739	resp, err := s.client.Do(ctx, req, p)
740	if err != nil {
741		return nil, resp, err
742	}
743
744	return p, resp, nil
745}
746
747// ListRequiredStatusChecksContexts lists the required status checks contexts for a given protected branch.
748//
749// GitHub API docs: https://developer.github.com/v3/repos/branches/#list-required-status-checks-contexts-of-protected-branch
750func (s *RepositoriesService) ListRequiredStatusChecksContexts(ctx context.Context, owner, repo, branch string) (contexts []string, resp *Response, err error) {
751	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks/contexts", owner, repo, branch)
752	req, err := s.client.NewRequest("GET", u, nil)
753	if err != nil {
754		return nil, nil, err
755	}
756
757	// TODO: remove custom Accept header when this API fully launches
758	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
759
760	resp, err = s.client.Do(ctx, req, &contexts)
761	if err != nil {
762		return nil, resp, err
763	}
764
765	return contexts, resp, nil
766}
767
768// UpdateBranchProtection updates the protection of a given branch.
769//
770// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-branch-protection
771func (s *RepositoriesService) UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *ProtectionRequest) (*Protection, *Response, error) {
772	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch)
773	req, err := s.client.NewRequest("PUT", u, preq)
774	if err != nil {
775		return nil, nil, err
776	}
777
778	// TODO: remove custom Accept header when this API fully launches
779	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
780
781	p := new(Protection)
782	resp, err := s.client.Do(ctx, req, p)
783	if err != nil {
784		return nil, resp, err
785	}
786
787	return p, resp, nil
788}
789
790// RemoveBranchProtection removes the protection of a given branch.
791//
792// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-branch-protection
793func (s *RepositoriesService) RemoveBranchProtection(ctx context.Context, owner, repo, branch string) (*Response, error) {
794	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch)
795	req, err := s.client.NewRequest("DELETE", u, nil)
796	if err != nil {
797		return nil, err
798	}
799
800	// TODO: remove custom Accept header when this API fully launches
801	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
802
803	return s.client.Do(ctx, req, nil)
804}
805
806// UpdateRequiredStatusChecks updates the required status checks for a given protected branch.
807//
808// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-required-status-checks-of-protected-branch
809func (s *RepositoriesService) UpdateRequiredStatusChecks(ctx context.Context, owner, repo, branch string, sreq *RequiredStatusChecksRequest) (*RequiredStatusChecks, *Response, error) {
810	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, branch)
811	req, err := s.client.NewRequest("PATCH", u, sreq)
812	if err != nil {
813		return nil, nil, err
814	}
815
816	sc := new(RequiredStatusChecks)
817	resp, err := s.client.Do(ctx, req, sc)
818	if err != nil {
819		return nil, resp, err
820	}
821
822	return sc, resp, nil
823}
824
825// License gets the contents of a repository's license if one is detected.
826//
827// GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license
828func (s *RepositoriesService) License(ctx context.Context, owner, repo string) (*RepositoryLicense, *Response, error) {
829	u := fmt.Sprintf("repos/%v/%v/license", owner, repo)
830	req, err := s.client.NewRequest("GET", u, nil)
831	if err != nil {
832		return nil, nil, err
833	}
834
835	r := &RepositoryLicense{}
836	resp, err := s.client.Do(ctx, req, r)
837	if err != nil {
838		return nil, resp, err
839	}
840
841	return r, resp, nil
842}
843
844// GetPullRequestReviewEnforcement gets pull request review enforcement of a protected branch.
845//
846// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-pull-request-review-enforcement-of-protected-branch
847func (s *RepositoriesService) GetPullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string) (*PullRequestReviewsEnforcement, *Response, error) {
848	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch)
849	req, err := s.client.NewRequest("GET", u, nil)
850	if err != nil {
851		return nil, nil, err
852	}
853
854	// TODO: remove custom Accept header when this API fully launches
855	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
856
857	r := new(PullRequestReviewsEnforcement)
858	resp, err := s.client.Do(ctx, req, r)
859	if err != nil {
860		return nil, resp, err
861	}
862
863	return r, resp, nil
864}
865
866// UpdatePullRequestReviewEnforcement patches pull request review enforcement of a protected branch.
867// It requires admin access and branch protection to be enabled.
868//
869// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-pull-request-review-enforcement-of-protected-branch
870func (s *RepositoriesService) UpdatePullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string, patch *PullRequestReviewsEnforcementUpdate) (*PullRequestReviewsEnforcement, *Response, error) {
871	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch)
872	req, err := s.client.NewRequest("PATCH", u, patch)
873	if err != nil {
874		return nil, nil, err
875	}
876
877	// TODO: remove custom Accept header when this API fully launches
878	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
879
880	r := new(PullRequestReviewsEnforcement)
881	resp, err := s.client.Do(ctx, req, r)
882	if err != nil {
883		return nil, resp, err
884	}
885
886	return r, resp, err
887}
888
889// DisableDismissalRestrictions disables dismissal restrictions of a protected branch.
890// It requires admin access and branch protection to be enabled.
891//
892// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-pull-request-review-enforcement-of-protected-branch
893func (s *RepositoriesService) DisableDismissalRestrictions(ctx context.Context, owner, repo, branch string) (*PullRequestReviewsEnforcement, *Response, error) {
894	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch)
895
896	data := struct {
897		R []interface{} `json:"dismissal_restrictions"`
898	}{[]interface{}{}}
899
900	req, err := s.client.NewRequest("PATCH", u, data)
901	if err != nil {
902		return nil, nil, err
903	}
904
905	// TODO: remove custom Accept header when this API fully launches
906	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
907
908	r := new(PullRequestReviewsEnforcement)
909	resp, err := s.client.Do(ctx, req, r)
910	if err != nil {
911		return nil, resp, err
912	}
913
914	return r, resp, err
915}
916
917// RemovePullRequestReviewEnforcement removes pull request enforcement of a protected branch.
918//
919// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-pull-request-review-enforcement-of-protected-branch
920func (s *RepositoriesService) RemovePullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string) (*Response, error) {
921	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch)
922	req, err := s.client.NewRequest("DELETE", u, nil)
923	if err != nil {
924		return nil, err
925	}
926
927	// TODO: remove custom Accept header when this API fully launches
928	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
929
930	return s.client.Do(ctx, req, nil)
931}
932
933// GetAdminEnforcement gets admin enforcement information of a protected branch.
934//
935// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-admin-enforcement-of-protected-branch
936func (s *RepositoriesService) GetAdminEnforcement(ctx context.Context, owner, repo, branch string) (*AdminEnforcement, *Response, error) {
937	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch)
938	req, err := s.client.NewRequest("GET", u, nil)
939	if err != nil {
940		return nil, nil, err
941	}
942
943	// TODO: remove custom Accept header when this API fully launches
944	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
945
946	r := new(AdminEnforcement)
947	resp, err := s.client.Do(ctx, req, r)
948	if err != nil {
949		return nil, resp, err
950	}
951
952	return r, resp, nil
953}
954
955// AddAdminEnforcement adds admin enforcement to a protected branch.
956// It requires admin access and branch protection to be enabled.
957//
958// GitHub API docs: https://developer.github.com/v3/repos/branches/#add-admin-enforcement-of-protected-branch
959func (s *RepositoriesService) AddAdminEnforcement(ctx context.Context, owner, repo, branch string) (*AdminEnforcement, *Response, error) {
960	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch)
961	req, err := s.client.NewRequest("POST", u, nil)
962	if err != nil {
963		return nil, nil, err
964	}
965
966	// TODO: remove custom Accept header when this API fully launches
967	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
968
969	r := new(AdminEnforcement)
970	resp, err := s.client.Do(ctx, req, r)
971	if err != nil {
972		return nil, resp, err
973	}
974
975	return r, resp, err
976}
977
978// RemoveAdminEnforcement removes admin enforcement from a protected branch.
979//
980// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-admin-enforcement-of-protected-branch
981func (s *RepositoriesService) RemoveAdminEnforcement(ctx context.Context, owner, repo, branch string) (*Response, error) {
982	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch)
983	req, err := s.client.NewRequest("DELETE", u, nil)
984	if err != nil {
985		return nil, err
986	}
987
988	// TODO: remove custom Accept header when this API fully launches
989	req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview)
990
991	return s.client.Do(ctx, req, nil)
992}
993
994// repositoryTopics represents a collection of repository topics.
995type repositoryTopics struct {
996	Names []string `json:"names"`
997}
998
999// ListAllTopics lists topics for a repository.
1000//
1001// GitHub API docs: https://developer.github.com/v3/repos/#list-all-topics-for-a-repository
1002func (s *RepositoriesService) ListAllTopics(ctx context.Context, owner, repo string) ([]string, *Response, error) {
1003	u := fmt.Sprintf("repos/%v/%v/topics", owner, repo)
1004	req, err := s.client.NewRequest("GET", u, nil)
1005	if err != nil {
1006		return nil, nil, err
1007	}
1008
1009	// TODO: remove custom Accept header when this API fully launches.
1010	req.Header.Set("Accept", mediaTypeTopicsPreview)
1011
1012	topics := new(repositoryTopics)
1013	resp, err := s.client.Do(ctx, req, topics)
1014	if err != nil {
1015		return nil, resp, err
1016	}
1017
1018	return topics.Names, resp, nil
1019}
1020
1021// ReplaceAllTopics replaces topics for a repository.
1022//
1023// GitHub API docs: https://developer.github.com/v3/repos/#replace-all-topics-for-a-repository
1024func (s *RepositoriesService) ReplaceAllTopics(ctx context.Context, owner, repo string, topics []string) ([]string, *Response, error) {
1025	u := fmt.Sprintf("repos/%v/%v/topics", owner, repo)
1026	t := &repositoryTopics{
1027		Names: topics,
1028	}
1029	if t.Names == nil {
1030		t.Names = []string{}
1031	}
1032	req, err := s.client.NewRequest("PUT", u, t)
1033	if err != nil {
1034		return nil, nil, err
1035	}
1036
1037	// TODO: remove custom Accept header when this API fully launches.
1038	req.Header.Set("Accept", mediaTypeTopicsPreview)
1039
1040	t = new(repositoryTopics)
1041	resp, err := s.client.Do(ctx, req, t)
1042	if err != nil {
1043		return nil, resp, err
1044	}
1045
1046	return t.Names, resp, nil
1047}
1048
1049// TransferRequest represents a request to transfer a repository.
1050type TransferRequest struct {
1051	NewOwner string  `json:"new_owner"`
1052	TeamID   []int64 `json:"team_ids,omitempty"`
1053}
1054
1055// Transfer transfers a repository from one account or organization to another.
1056//
1057// This method might return an *AcceptedError and a status code of
1058// 202. This is because this is the status that GitHub returns to signify that
1059// it has now scheduled the transfer of the repository in a background task.
1060// A follow up request, after a delay of a second or so, should result
1061// in a successful request.
1062//
1063// GitHub API docs: https://developer.github.com/v3/repos/#transfer-a-repository
1064func (s *RepositoriesService) Transfer(ctx context.Context, owner, repo string, transfer TransferRequest) (*Repository, *Response, error) {
1065	u := fmt.Sprintf("repos/%v/%v/transfer", owner, repo)
1066
1067	req, err := s.client.NewRequest("POST", u, &transfer)
1068	if err != nil {
1069		return nil, nil, err
1070	}
1071
1072	// TODO: remove custom Accept header when this API fully launches.
1073	req.Header.Set("Accept", mediaTypeRepositoryTransferPreview)
1074
1075	r := new(Repository)
1076	resp, err := s.client.Do(ctx, req, r)
1077	if err != nil {
1078		return nil, resp, err
1079	}
1080
1081	return r, resp, nil
1082}
1083