Skip to content

Commit

Permalink
Required signatures on protected branches (google#1039)
Browse files Browse the repository at this point in the history
Fixes google#902.
  • Loading branch information
dlnilsson authored and gmlewis committed Nov 9, 2018
1 parent 35254df commit 1155396
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 0 deletions.
16 changes: 16 additions & 0 deletions github/github-accessors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ const (

// https://developer.github.com/enterprise/2.13/v3/repos/pre_receive_hooks/
mediaTypePreReceiveHooksPreview = "application/vnd.github.eye-scream-preview"

// https://developer.github.com/changes/2018-02-22-protected-branches-required-signatures/
mediaTypeSignaturePreview = "application/vnd.github.zzzax-preview+json"
)

// A Client manages communication with the GitHub API.
Expand Down
68 changes: 68 additions & 0 deletions github/repos.go
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,13 @@ type DismissalRestrictionsRequest struct {
Teams *[]string `json:"teams,omitempty"`
}

// SignaturesProtectedBranch represents the protection status of an individual branch.
type SignaturesProtectedBranch struct {
URL *string `json:"url,omitempty"`
// Commits pushed to matching branches must have verified signatures.
Enabled *bool `json:"enabled,omitempty"`
}

// ListBranches lists branches for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/#list-branches
Expand Down Expand Up @@ -850,6 +857,67 @@ func (s *RepositoriesService) RemoveBranchProtection(ctx context.Context, owner,
return s.client.Do(ctx, req, nil)
}

// GetSignaturesProtectedBranch gets required signatures of protected branch.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-required-signatures-of-protected-branch
func (s *RepositoriesService) GetSignaturesProtectedBranch(ctx context.Context, owner, repo, branch string) (*SignaturesProtectedBranch, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_signatures", owner, repo, branch)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}

// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeSignaturePreview)

p := new(SignaturesProtectedBranch)
resp, err := s.client.Do(ctx, req, p)
if err != nil {
return nil, resp, err
}

return p, resp, nil
}

// RequireSignaturesOnProtectedBranch makes signed commits required on a protected branch.
// It requires admin access and branch protection to be enabled.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#add-required-signatures-of-protected-branch
func (s *RepositoriesService) RequireSignaturesOnProtectedBranch(ctx context.Context, owner, repo, branch string) (*SignaturesProtectedBranch, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_signatures", owner, repo, branch)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, nil, err
}

// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeSignaturePreview)

r := new(SignaturesProtectedBranch)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}

return r, resp, err
}

// OptionalSignaturesOnProtectedBranch removes required signed commits on a given branch.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-required-signatures-of-protected-branch
func (s *RepositoriesService) OptionalSignaturesOnProtectedBranch(ctx context.Context, owner, repo, branch string) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_signatures", owner, repo, branch)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}

// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeSignaturePreview)

return s.client.Do(ctx, req, nil)
}

// UpdateRequiredStatusChecks updates the required status checks for a given protected branch.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-required-status-checks-of-protected-branch
Expand Down
66 changes: 66 additions & 0 deletions github/repos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,72 @@ func TestRepositoriesService_RemoveAdminEnforcement(t *testing.T) {
}
}

func TestRepositoriesService_GetSignaturesProtectedBranch(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()

mux.HandleFunc("/repos/o/r/branches/b/protection/required_signatures", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeSignaturePreview)
fmt.Fprintf(w, `{"url":"/repos/o/r/branches/b/protection/required_signatures","enabled":false}`)
})

signature, _, err := client.Repositories.GetSignaturesProtectedBranch(context.Background(), "o", "r", "b")
if err != nil {
t.Errorf("Repositories.GetSignaturesProtectedBranch returned error: %v", err)
}

want := &SignaturesProtectedBranch{
URL: String("/repos/o/r/branches/b/protection/required_signatures"),
Enabled: Bool(false),
}

if !reflect.DeepEqual(signature, want) {
t.Errorf("Repositories.GetSignaturesProtectedBranch returned %+v, want %+v", signature, want)
}
}

func TestRepositoriesService_RequireSignaturesOnProtectedBranch(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()

mux.HandleFunc("/repos/o/r/branches/b/protection/required_signatures", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeSignaturePreview)
fmt.Fprintf(w, `{"url":"/repos/o/r/branches/b/protection/required_signatures","enabled":true}`)
})

signature, _, err := client.Repositories.RequireSignaturesOnProtectedBranch(context.Background(), "o", "r", "b")
if err != nil {
t.Errorf("Repositories.RequireSignaturesOnProtectedBranch returned error: %v", err)
}

want := &SignaturesProtectedBranch{
URL: String("/repos/o/r/branches/b/protection/required_signatures"),
Enabled: Bool(true),
}

if !reflect.DeepEqual(signature, want) {
t.Errorf("Repositories.RequireSignaturesOnProtectedBranch returned %+v, want %+v", signature, want)
}
}

func TestRepositoriesService_OptionalSignaturesOnProtectedBranch(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()

mux.HandleFunc("/repos/o/r/branches/b/protection/required_signatures", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testHeader(t, r, "Accept", mediaTypeSignaturePreview)
w.WriteHeader(http.StatusNoContent)
})

_, err := client.Repositories.OptionalSignaturesOnProtectedBranch(context.Background(), "o", "r", "b")
if err != nil {
t.Errorf("Repositories.OptionalSignaturesOnProtectedBranch returned error: %v", err)
}
}

func TestPullRequestReviewsEnforcementRequest_MarshalJSON_nilDismissalRestirctions(t *testing.T) {
req := PullRequestReviewsEnforcementRequest{}

Expand Down

0 comments on commit 1155396

Please sign in to comment.