diff --git a/github/github-accessors.go b/github/github-accessors.go index 073a3b9cea9..2ad12d3488d 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -7452,6 +7452,14 @@ func (p *PullRequestReviewRequest) GetEvent() string { return *p.Event } +// GetRequiredApprovingReviewCount returns the RequiredApprovingReviewCount field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewsEnforcement) GetRequiredApprovingReviewCount() int { + if p == nil || p.RequiredApprovingReviewCount == nil { + return 0 + } + return *p.RequiredApprovingReviewCount +} + // GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field. func (p *PullRequestReviewsEnforcementRequest) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest { if p == nil { diff --git a/github/github.go b/github/github.go index e10b9dde00c..8dda6c74f30 100644 --- a/github/github.go +++ b/github/github.go @@ -114,6 +114,9 @@ const ( // https://developer.github.com/changes/2018-02-07-team-discussions-api/ mediaTypeTeamDiscussionsPreview = "application/vnd.github.echo-preview+json" + // https://developer.github.com/changes/2018-03-16-protected-branches-required-approving-reviews/ + mediaTypeMultipleApprovingReviewsForProtectedBranches = "application/vnd.github.luke-cage-preview+json" + // https://developer.github.com/changes/2018-05-07-new-checks-api-public-beta/ mediaTypeCheckRunsPreview = "application/vnd.github.antiope-preview+json" diff --git a/github/repos.go b/github/repos.go index 56679004a35..e6f7d6fb3ab 100644 --- a/github/repos.go +++ b/github/repos.go @@ -563,11 +563,13 @@ type RequiredStatusChecksRequest struct { // PullRequestReviewsEnforcement represents the pull request reviews enforcement of a protected branch. type PullRequestReviewsEnforcement struct { // Specifies which users and teams can dismiss pull request reviews. - DismissalRestrictions DismissalRestrictions `json:"dismissal_restrictions"` + DismissalRestrictions DismissalRestrictions `json:"dismissal_restrictions,omitempty"` // Specifies if approved reviews are dismissed automatically, when a new commit is pushed. - DismissStaleReviews bool `json:"dismiss_stale_reviews"` + DismissStaleReviews bool `json:"dismiss_stale_reviews,omitempty"` // RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner. - RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"` + RequireCodeOwnerReviews bool `json:"require_code_owner_reviews,omitempty"` + // RequiredApprovingReviewCount specifies the number of reviewers required to approve pull requests, this will be a number between 1 and 6. + RequiredApprovingReviewCount *int `json:"required_approving_review_count,omitempty"` } // PullRequestReviewsEnforcementRequest represents request to set the pull request review @@ -702,7 +704,7 @@ func (s *RepositoriesService) GetBranchProtection(ctx context.Context, owner, re } // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + req.Header.Set("Accept", mediaTypeMultipleApprovingReviewsForProtectedBranches) p := new(Protection) resp, err := s.client.Do(ctx, req, p) @@ -767,7 +769,7 @@ func (s *RepositoriesService) UpdateBranchProtection(ctx context.Context, owner, } // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + req.Header.Set("Accept", mediaTypeMultipleApprovingReviewsForProtectedBranches) p := new(Protection) resp, err := s.client.Do(ctx, req, p) @@ -843,7 +845,7 @@ func (s *RepositoriesService) GetPullRequestReviewEnforcement(ctx context.Contex } // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + req.Header.Set("Accept", mediaTypeMultipleApprovingReviewsForProtectedBranches) r := new(PullRequestReviewsEnforcement) resp, err := s.client.Do(ctx, req, r) @@ -866,7 +868,7 @@ func (s *RepositoriesService) UpdatePullRequestReviewEnforcement(ctx context.Con } // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + req.Header.Set("Accept", mediaTypeMultipleApprovingReviewsForProtectedBranches) r := new(PullRequestReviewsEnforcement) resp, err := s.client.Do(ctx, req, r) diff --git a/github/repos_test.go b/github/repos_test.go index 32a79e5dd5c..66f75a1ef6f 100644 --- a/github/repos_test.go +++ b/github/repos_test.go @@ -530,8 +530,8 @@ func TestRepositoriesService_GetBranchProtection(t *testing.T) { json.NewDecoder(r.Body).Decode(v) testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) - fmt.Fprintf(w, `{"required_status_checks":{"strict":true,"contexts":["continuous-integration"]},"required_pull_request_reviews":{"dismissal_restrictions":{"users":[{"id":3,"login":"u"}],"teams":[{"id":4,"slug":"t"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true},"enforce_admins":{"url":"/repos/o/r/branches/b/protection/enforce_admins","enabled":true},"restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]}}`) + testHeader(t, r, "Accept", mediaTypeMultipleApprovingReviewsForProtectedBranches) + fmt.Fprintf(w, `{"required_status_checks":{"strict":true,"contexts":["continuous-integration"]},"required_pull_request_reviews":{"dismissal_restrictions":{"users":[{"id":3,"login":"u"}],"teams":[{"id":4,"slug":"t"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true,"required_approving_review_count": 2},"enforce_admins":{"url":"/repos/o/r/branches/b/protection/enforce_admins","enabled":true},"restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]}}`) }) protection, _, err := client.Repositories.GetBranchProtection(context.Background(), "o", "r", "b") @@ -554,7 +554,8 @@ func TestRepositoriesService_GetBranchProtection(t *testing.T) { {Slug: String("t"), ID: Int64(4)}, }, }, - RequireCodeOwnerReviews: true, + RequireCodeOwnerReviews: true, + RequiredApprovingReviewCount: Int(2), }, EnforceAdmins: &AdminEnforcement{ URL: String("/repos/o/r/branches/b/protection/enforce_admins"), @@ -604,8 +605,8 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) { if !reflect.DeepEqual(v, input) { t.Errorf("Request body = %+v, want %+v", v, input) } - testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) - fmt.Fprintf(w, `{"required_status_checks":{"strict":true,"contexts":["continuous-integration"]},"required_pull_request_reviews":{"dismissal_restrictions":{"users":[{"id":3,"login":"uu"}],"teams":[{"id":4,"slug":"tt"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true},"restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]}}`) + testHeader(t, r, "Accept", mediaTypeMultipleApprovingReviewsForProtectedBranches) + fmt.Fprintf(w, `{"required_status_checks":{"strict":true,"contexts":["continuous-integration"]},"required_pull_request_reviews":{"dismissal_restrictions":{"users":[{"id":3,"login":"uu"}],"teams":[{"id":4,"slug":"tt"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true,"required_approving_review_count":2},"restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]}}`) }) protection, _, err := client.Repositories.UpdateBranchProtection(context.Background(), "o", "r", "b", input) @@ -628,7 +629,8 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) { {Slug: String("tt"), ID: Int64(4)}, }, }, - RequireCodeOwnerReviews: true, + RequireCodeOwnerReviews: true, + RequiredApprovingReviewCount: Int(2), }, Restrictions: &BranchRestrictions{ Users: []*User{ @@ -791,8 +793,8 @@ func TestRepositoriesService_GetPullRequestReviewEnforcement(t *testing.T) { mux.HandleFunc("/repos/o/r/branches/b/protection/required_pull_request_reviews", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) - fmt.Fprintf(w, `{"dismissal_restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true}`) + testHeader(t, r, "Accept", mediaTypeMultipleApprovingReviewsForProtectedBranches) + fmt.Fprintf(w, `{"dismissal_restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true,"required_approving_review_count": 2}`) }) enforcement, _, err := client.Repositories.GetPullRequestReviewEnforcement(context.Background(), "o", "r", "b") @@ -810,7 +812,8 @@ func TestRepositoriesService_GetPullRequestReviewEnforcement(t *testing.T) { {Slug: String("t"), ID: Int64(2)}, }, }, - RequireCodeOwnerReviews: true, + RequireCodeOwnerReviews: true, + RequiredApprovingReviewCount: Int(2), } if !reflect.DeepEqual(enforcement, want) { @@ -837,8 +840,8 @@ func TestRepositoriesService_UpdatePullRequestReviewEnforcement(t *testing.T) { if !reflect.DeepEqual(v, input) { t.Errorf("Request body = %+v, want %+v", v, input) } - testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) - fmt.Fprintf(w, `{"dismissal_restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true}`) + testHeader(t, r, "Accept", mediaTypeMultipleApprovingReviewsForProtectedBranches) + fmt.Fprintf(w, `{"dismissal_restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true,"required_approving_review_count": 2}`) }) enforcement, _, err := client.Repositories.UpdatePullRequestReviewEnforcement(context.Background(), "o", "r", "b", input) @@ -856,7 +859,8 @@ func TestRepositoriesService_UpdatePullRequestReviewEnforcement(t *testing.T) { {Slug: String("t"), ID: Int64(2)}, }, }, - RequireCodeOwnerReviews: true, + RequireCodeOwnerReviews: true, + RequiredApprovingReviewCount: Int(2), } if !reflect.DeepEqual(enforcement, want) { t.Errorf("Repositories.UpdatePullRequestReviewEnforcement returned %+v, want %+v", enforcement, want)