Skip to content
Open
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: 24 additions & 0 deletions github/github-accessors.go

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

30 changes: 30 additions & 0 deletions github/github-accessors_test.go

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

23 changes: 19 additions & 4 deletions github/repos.go
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,7 @@ type AuthorizedActorNames struct {
From []string `json:"from,omitempty"`
}

// AuthorizedActorsOnly represents if the branche rule can be edited by authorized actors only.
// AuthorizedActorsOnly represents if the branch rule can be edited by authorized actors only.
type AuthorizedActorsOnly struct {
From *bool `json:"from,omitempty"`
}
Expand All @@ -872,19 +872,34 @@ type ProtectionRequest struct {
RequiredConversationResolution *bool `json:"required_conversation_resolution,omitempty"`
}

// RequiredStatusChecks represents the protection status of a individual branch.
// Check represents a status check to require in order to merge into an individual branch.
type Check struct {
// The name of the required check. (Required.)
Context string `json:"context"`
// The ID of the GitHub App that must provide this check. Omit this field to automatically select the GitHub App
// that has recently provided this check, or any app if it was not set by a GitHub App. Pass -1 to explicitly
// allow any app to set the status.
AppID *int `json:"app_id"`
}

// RequiredStatusChecks represents the protection status of an individual branch.
type RequiredStatusChecks struct {
// Require branches to be up to date before merging. (Required.)
Strict bool `json:"strict"`
// The list of status checks to require in order to merge into this
// branch. (Required; use []string{} instead of nil for empty list.)
// Deprecated: The list of status checks to require in order to merge into this branch. If any of these checks have
// recently been set by a particular GitHub App, they will be required to come from that app in future for the
// branch to merge. Use checks instead of contexts for more fine-grained control.
// (Required; use []string{} instead of nil for empty list.)
Contexts []string `json:"contexts"`
// The list of status checks to require in order to merge into this branch.
Checks *[]Check `json:"checks"`
}

// RequiredStatusChecksRequest represents a request to edit a protected branch's status checks.
type RequiredStatusChecksRequest struct {
Strict *bool `json:"strict,omitempty"`
Contexts []string `json:"contexts,omitempty"`
Checks *[]Check `json:"checks,omitempty"`
}

// PullRequestReviewsEnforcement represents the pull request reviews enforcement of a protected branch.
Expand Down
180 changes: 177 additions & 3 deletions github/repos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,17 @@ func TestRepositoriesService_GetBranchProtection(t *testing.T) {
fmt.Fprintf(w, `{
"required_status_checks":{
"strict":true,
"contexts":["continuous-integration"]
"contexts":["continuous-integration", "ci-with-app-id"],
"checks": [
{
"context": "continuous-integration",
"app_id": null
},
{
"context": "ci-with-app-id",
"app_id": 123
}
]
},
"required_pull_request_reviews":{
"dismissal_restrictions":{
Expand Down Expand Up @@ -1080,7 +1090,16 @@ func TestRepositoriesService_GetBranchProtection(t *testing.T) {
want := &Protection{
RequiredStatusChecks: &RequiredStatusChecks{
Strict: true,
Contexts: []string{"continuous-integration"},
Contexts: []string{"continuous-integration", "ci-with-app-id"},
Checks: []Check{
{
Context: "continuous-integration",
},
{
Context: "ci-with-app-id",
AppID: Int(123),
},
},
},
RequiredPullRequestReviews: &PullRequestReviewsEnforcement{
DismissStaleReviews: true,
Expand Down Expand Up @@ -1228,6 +1247,16 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
RequiredStatusChecks: &RequiredStatusChecks{
Strict: true,
Contexts: []string{"continuous-integration"},
Checks: []Check{
{
Context: "continuous-integration",
AppID: Int(-1),
},
{
Context: "ci-with-app-id",
AppID: Int(123),
},
},
},
RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{
DismissStaleReviews: true,
Expand Down Expand Up @@ -1257,7 +1286,17 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
fmt.Fprintf(w, `{
"required_status_checks":{
"strict":true,
"contexts":["continuous-integration"]
"contexts":["continuous-integration"],
"checks": [
{
"context": "continuous-integration",
"app_id": null
},
{
"context": "ci-with-app-id",
"app_id": 123
}
]
},
"required_pull_request_reviews":{
"dismissal_restrictions":{
Expand Down Expand Up @@ -1291,6 +1330,141 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
RequiredStatusChecks: &RequiredStatusChecks{
Strict: true,
Contexts: []string{"continuous-integration"},
Checks: []Check{
{
Context: "continuous-integration",
},
{
Context: "ci-with-app-id",
AppID: Int(123),
},
},
},
RequiredPullRequestReviews: &PullRequestReviewsEnforcement{
DismissStaleReviews: true,
DismissalRestrictions: &DismissalRestrictions{
Users: []*User{
{Login: String("uu"), ID: Int64(3)},
},
Teams: []*Team{
{Slug: String("tt"), ID: Int64(4)},
},
},
RequireCodeOwnerReviews: true,
},
Restrictions: &BranchRestrictions{
Users: []*User{
{Login: String("u"), ID: Int64(1)},
},
Teams: []*Team{
{Slug: String("t"), ID: Int64(2)},
},
Apps: []*App{
{Slug: String("a"), ID: Int64(3)},
},
},
}
if !cmp.Equal(protection, want) {
t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want)
}

const methodName = "UpdateBranchProtection"
testBadOptions(t, methodName, func() (err error) {
_, _, err = client.Repositories.UpdateBranchProtection(ctx, "\n", "\n", "\n", input)
return err
})

testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", "b", input)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}

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

input := &ProtectionRequest{
RequiredStatusChecks: &RequiredStatusChecks{
Strict: true,
Contexts: []string{"continuous-integration"},
},
RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{
DismissStaleReviews: true,
DismissalRestrictionsRequest: &DismissalRestrictionsRequest{
Users: &[]string{"uu"},
Teams: &[]string{"tt"},
},
},
Restrictions: &BranchRestrictionsRequest{
Users: []string{"u"},
Teams: []string{"t"},
Apps: []string{"a"},
},
}

mux.HandleFunc("/repos/o/r/branches/b/protection", func(w http.ResponseWriter, r *http.Request) {
v := new(ProtectionRequest)
json.NewDecoder(r.Body).Decode(v)

testMethod(t, r, "PUT")
if !cmp.Equal(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}

// TODO: remove custom Accept header when this API fully launches
testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview)
fmt.Fprintf(w, `{
"required_status_checks":{
"strict":true,
"contexts":["continuous-integration"],
"checks": [
{
"context": "continuous-integration",
"app_id": null
}
]
},
"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"}],
"apps":[{"id":3,"slug":"a"}]
}
}`)
})

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

want := &Protection{
RequiredStatusChecks: &RequiredStatusChecks{
Strict: true,
Contexts: []string{"continuous-integration"},
Checks: []Check{
{
Context: "continuous-integration",
},
},
},
RequiredPullRequestReviews: &PullRequestReviewsEnforcement{
DismissStaleReviews: true,
Expand Down