Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for allowing apps to push to protected branches #1283

Merged
merged 6 commits into from
Dec 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions github/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@ type AppsService service

// App represents a GitHub App.
type App struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Owner *User `json:"owner,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
ExternalURL *string `json:"external_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
ID *int64 `json:"id,omitempty"`
Slug *string `json:"slug,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Owner *User `json:"owner,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
ExternalURL *string `json:"external_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
Permissions *InstallationPermissions `json:"permissions,omitempty"`
Events []*Event `json:"events,omitempty"`
}

// InstallationToken represents an installation token.
Expand Down Expand Up @@ -56,10 +59,13 @@ type InstallationTokenOptions struct {
// https://developer.github.com/enterprise/v3/apps/permissions/
type InstallationPermissions struct {
Administration *string `json:"administration,omitempty"`
Blocking *string `json:"blocking,omitempty"`
Checks *string `json:"checks,omitempty"`
Contents *string `json:"contents,omitempty"`
ContentReferences *string `json:"content_references,omitempty"`
Deployments *string `json:"deployments,omitempty"`
Emails *string `json:"emails,omitempty"`
Followers *string `json:"followers,omitempty"`
Issues *string `json:"issues,omitempty"`
Metadata *string `json:"metadata,omitempty"`
Members *string `json:"members,omitempty"`
Expand Down
40 changes: 40 additions & 0 deletions github/github-accessors.go

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

91 changes: 91 additions & 0 deletions github/repos.go
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,8 @@ type BranchRestrictions struct {
Users []*User `json:"users"`
// The list of team slugs with push access.
Teams []*Team `json:"teams"`
// The list of app slugs with push access.
Apps []*App `json:"apps"`
}

// BranchRestrictionsRequest represents the request to create/edit the
Expand All @@ -802,6 +804,8 @@ type BranchRestrictionsRequest struct {
Users []string `json:"users"`
// The list of team slugs with push access. (Required; use []string{} instead of nil for empty list.)
Teams []string `json:"teams"`
// The list of app slugs with push access.
Apps []string `json:"apps,omitempty"`
}

// DismissalRestrictions specifies which users and teams can dismiss pull request reviews.
Expand Down Expand Up @@ -1287,6 +1291,93 @@ func (s *RepositoriesService) ReplaceAllTopics(ctx context.Context, owner, repo
return t.Names, resp, nil
}

// ListApps lists the Github apps that have push access to a given protected branch.
// It requires the Github apps to have `write` access to the `content` permission.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#list-apps-with-access-to-protected-branch
func (s *RepositoriesService) ListApps(ctx context.Context, owner, repo, branch string) ([]*App, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}

var apps []*App
resp, err := s.client.Do(ctx, req, &apps)
if err != nil {
return nil, resp, err
}

return apps, resp, nil
}

// ReplaceAppRestrictions replaces the apps that have push access to a given protected branch.
// It removes all apps that previously had push access and grants push access to the new list of apps.
// It requires the Github apps to have `write` access to the `content` permission.
//
// Note: The list of users, apps, and teams in total is limited to 100 items.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#replace-app-restrictions-of-protected-branch
func (s *RepositoriesService) ReplaceAppRestrictions(ctx context.Context, owner, repo, branch string, slug []string) ([]*App, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch)
req, err := s.client.NewRequest("PUT", u, slug)
if err != nil {
return nil, nil, err
}

var apps []*App
resp, err := s.client.Do(ctx, req, &apps)
if err != nil {
return nil, nil, err
}

return apps, resp, nil
}

// AddAppRestrictions grants the specified apps push access to a given protected branch.
// It requires the Github apps to have `write` access to the `content` permission.
//
// Note: The list of users, apps, and teams in total is limited to 100 items.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#add-app-restrictions-of-protected-branch
func (s *RepositoriesService) AddAppRestrictions(ctx context.Context, owner, repo, branch string, slug []string) ([]*App, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch)
req, err := s.client.NewRequest("POST", u, slug)
if err != nil {
return nil, nil, err
}

var apps []*App
resp, err := s.client.Do(ctx, req, &apps)
if err != nil {
return nil, nil, err
}

return apps, resp, nil
}

// RemoveAppRestrictions removes the ability of an app to push to this branch.
// It requires the Github apps to have `write` access to the `content` permission.
//
// Note: The list of users, apps, and teams in total is limited to 100 items.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-app-restrictions-of-protected-branch
func (s *RepositoriesService) RemoveAppRestrictions(ctx context.Context, owner, repo, branch string, slug []string) ([]*App, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch)
req, err := s.client.NewRequest("DELETE", u, slug)
if err != nil {
return nil, nil, err
}

var apps []*App
resp, err := s.client.Do(ctx, req, &apps)
if err != nil {
return nil, nil, err
}

return apps, resp, nil
}

// TransferRequest represents a request to transfer a repository.
type TransferRequest struct {
NewOwner string `json:"new_owner"`
Expand Down
86 changes: 85 additions & 1 deletion github/repos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,7 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
Restrictions: &BranchRestrictionsRequest{
Users: []string{"u"},
Teams: []string{"t"},
Apps: []string{"a"},
},
}

Expand Down Expand Up @@ -990,7 +991,8 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
},
"restrictions":{
"users":[{"id":1,"login":"u"}],
"teams":[{"id":2,"slug":"t"}]
"teams":[{"id":2,"slug":"t"}],
"apps":[{"id":3,"slug":"a"}]
}
}`)
})
Expand Down Expand Up @@ -1024,6 +1026,9 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
Teams: []*Team{
{Slug: String("t"), ID: Int64(2)},
},
Apps: []*App{
{Slug: String("a"), ID: Int64(3)},
},
},
}
if !reflect.DeepEqual(protection, want) {
Expand Down Expand Up @@ -1603,6 +1608,85 @@ func TestRepositoriesService_ReplaceAllTopics_emptySlice(t *testing.T) {
}
}

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

mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/apps", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
})

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

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

mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/apps", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
fmt.Fprint(w, `[{
"name": "octocat"
}]`)
})
input := []string{"octocat"}
got, _, err := client.Repositories.ReplaceAppRestrictions(context.Background(), "o", "r", "b", input)
if err != nil {
t.Errorf("Repositories.ReplaceAppRestrictions returned error: %v", err)
}
want := []*App{
{Name: String("octocat")},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("Repositories.ReplaceAppRestrictions returned %+v, want %+v", got, want)
}
}

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

mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/apps", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
fmt.Fprint(w, `[{
"name": "octocat"
}]`)
})
input := []string{"octocat"}
got, _, err := client.Repositories.AddAppRestrictions(context.Background(), "o", "r", "b", input)
if err != nil {
t.Errorf("Repositories.AddAppRestrictions returned error: %v", err)
}
want := []*App{
{Name: String("octocat")},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("Repositories.AddAppRestrictions returned %+v, want %+v", got, want)
}
}

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

mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/apps", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
fmt.Fprint(w, `[]`)
})
input := []string{"octocat"}
got, _, err := client.Repositories.RemoveAppRestrictions(context.Background(), "o", "r", "b", input)
if err != nil {
t.Errorf("Repositories.RemoveAppRestrictions returned error: %v", err)
}
want := []*App{}
if !reflect.DeepEqual(got, want) {
t.Errorf("Repositories.RemoveAppRestrictions returned %+v, want %+v", got, want)
}
}

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