Skip to content

Commit

Permalink
Add support for GitHub Environments for Pro/Teams pricing plans (#2611)
Browse files Browse the repository at this point in the history
Fixes: #2602.
  • Loading branch information
AnitaErnszt committed Jan 4, 2023
1 parent c5583e7 commit eaa7559
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 0 deletions.
35 changes: 35 additions & 0 deletions github/repos_environments.go
Expand Up @@ -9,6 +9,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
)

// Environment represents a single environment in a repository.
Expand Down Expand Up @@ -168,6 +169,13 @@ type CreateUpdateEnvironment struct {
DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy"`
}

// createUpdateEnvironmentNoEnterprise represents the fields accepted for Pro/Teams private repos.
// Ref: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment
// See https://github.com/google/go-github/issues/2602 for more information.
type createUpdateEnvironmentNoEnterprise struct {
DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy"`
}

// CreateUpdateEnvironment create or update a new environment for a repository.
//
// GitHub API docs: https://docs.github.com/en/rest/deployments/environments#create-or-update-an-environment
Expand All @@ -179,6 +187,33 @@ func (s *RepositoriesService) CreateUpdateEnvironment(ctx context.Context, owner
return nil, nil, err
}

e := new(Environment)
resp, err := s.client.Do(ctx, req, e)
if err != nil {
// The API returns 422 when the pricing plan doesn't support all the fields sent.
// This path will be executed for Pro/Teams private repos.
// For public repos, regardless of the pricing plan, all fields supported.
// For Free plan private repos the returned error code is 404.
// We are checking that the user didn't try to send a value for unsupported fields,
// and return an error if they did.
if resp != nil && resp.StatusCode == http.StatusUnprocessableEntity && environment != nil && len(environment.Reviewers) == 0 && environment.GetWaitTimer() == 0 {
return s.createNewEnvNoEnterprise(ctx, u, environment)
}
return nil, resp, err
}
return e, resp, nil
}

// createNewEnvNoEnterprise is an internal function for cases where the original call returned 422.
// Currently only the `deployment_branch_policy` parameter is supported for Pro/Team private repos.
func (s *RepositoriesService) createNewEnvNoEnterprise(ctx context.Context, u string, environment *CreateUpdateEnvironment) (*Environment, *Response, error) {
req, err := s.client.NewRequest("PUT", u, &createUpdateEnvironmentNoEnterprise{
DeploymentBranchPolicy: environment.DeploymentBranchPolicy,
})
if err != nil {
return nil, nil, err
}

e := new(Environment)
resp, err := s.client.Do(ctx, req, e)
if err != nil {
Expand Down
104 changes: 104 additions & 0 deletions github/repos_environments_test.go
Expand Up @@ -220,6 +220,110 @@ func TestRepositoriesService_CreateEnvironment(t *testing.T) {
})
}

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

input := &CreateUpdateEnvironment{}
callCount := 0

mux.HandleFunc("/repos/o/r/environments/e", func(w http.ResponseWriter, r *http.Request) {
v := new(CreateUpdateEnvironment)
json.NewDecoder(r.Body).Decode(v)

testMethod(t, r, "PUT")
if callCount == 0 {
w.WriteHeader(http.StatusUnprocessableEntity)
callCount++
} else {
want := &CreateUpdateEnvironment{}
if !cmp.Equal(v, want) {
t.Errorf("Request body = %+v, want %+v", v, want)
}
fmt.Fprint(w, `{"id": 1, "name": "staging", "protection_rules": []}`)
}
})

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

want := &Environment{ID: Int64(1), Name: String("staging"), ProtectionRules: []*ProtectionRule{}}
if !cmp.Equal(release, want) {
t.Errorf("Repositories.CreateUpdateEnvironment returned %+v, want %+v", release, want)
}
}

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

input := &CreateUpdateEnvironment{
DeploymentBranchPolicy: &BranchPolicy{
ProtectedBranches: Bool(true),
CustomBranchPolicies: Bool(false),
},
}

mux.HandleFunc("/repos/o/r/environments/e", func(w http.ResponseWriter, r *http.Request) {
v := new(createUpdateEnvironmentNoEnterprise)
json.NewDecoder(r.Body).Decode(v)

testMethod(t, r, "PUT")
want := &createUpdateEnvironmentNoEnterprise{
DeploymentBranchPolicy: &BranchPolicy{
ProtectedBranches: Bool(true),
CustomBranchPolicies: Bool(false),
},
}
if !cmp.Equal(v, want) {
t.Errorf("Request body = %+v, want %+v", v, want)
}
fmt.Fprint(w, `{"id": 1, "name": "staging", "protection_rules": [{"id": 1, "node_id": "id", "type": "branch_policy"}], "deployment_branch_policy": {"protected_branches": true, "custom_branch_policies": false}}`)
})

ctx := context.Background()
release, _, err := client.Repositories.createNewEnvNoEnterprise(ctx, "repos/o/r/environments/e", input)
if err != nil {
t.Errorf("Repositories.createNewEnvNoEnterprise returned error: %v", err)
}

want := &Environment{
ID: Int64(1),
Name: String("staging"),
ProtectionRules: []*ProtectionRule{
{
ID: Int64(1),
NodeID: String("id"),
Type: String("branch_policy"),
},
},
DeploymentBranchPolicy: &BranchPolicy{
ProtectedBranches: Bool(true),
CustomBranchPolicies: Bool(false),
},
}
if !cmp.Equal(release, want) {
t.Errorf("Repositories.createNewEnvNoEnterprise returned %+v, want %+v", release, want)
}

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

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

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

0 comments on commit eaa7559

Please sign in to comment.