github.com/google/go-github/v49@v49.1.0/github/repos_environments.go (about) 1 // Copyright 2021 The go-github AUTHORS. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 package github 7 8 import ( 9 "context" 10 "encoding/json" 11 "fmt" 12 "net/http" 13 ) 14 15 // Environment represents a single environment in a repository. 16 type Environment struct { 17 Owner *string `json:"owner,omitempty"` 18 Repo *string `json:"repo,omitempty"` 19 EnvironmentName *string `json:"environment_name,omitempty"` 20 WaitTimer *int `json:"wait_timer,omitempty"` 21 Reviewers []*EnvReviewers `json:"reviewers,omitempty"` 22 DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy,omitempty"` 23 // Return/response only values 24 ID *int64 `json:"id,omitempty"` 25 NodeID *string `json:"node_id,omitempty"` 26 Name *string `json:"name,omitempty"` 27 URL *string `json:"url,omitempty"` 28 HTMLURL *string `json:"html_url,omitempty"` 29 CreatedAt *Timestamp `json:"created_at,omitempty"` 30 UpdatedAt *Timestamp `json:"updated_at,omitempty"` 31 ProtectionRules []*ProtectionRule `json:"protection_rules,omitempty"` 32 } 33 34 // EnvReviewers represents a single environment reviewer entry. 35 type EnvReviewers struct { 36 Type *string `json:"type,omitempty"` 37 ID *int64 `json:"id,omitempty"` 38 } 39 40 // BranchPolicy represents the options for whether a branch deployment policy is applied to this environment. 41 type BranchPolicy struct { 42 ProtectedBranches *bool `json:"protected_branches,omitempty"` 43 CustomBranchPolicies *bool `json:"custom_branch_policies,omitempty"` 44 } 45 46 // EnvResponse represents the slightly different format of response that comes back when you list an environment. 47 type EnvResponse struct { 48 TotalCount *int `json:"total_count,omitempty"` 49 Environments []*Environment `json:"environments,omitempty"` 50 } 51 52 // ProtectionRule represents a single protection rule applied to the environment. 53 type ProtectionRule struct { 54 ID *int64 `json:"id,omitempty"` 55 NodeID *string `json:"node_id,omitempty"` 56 Type *string `json:"type,omitempty"` 57 WaitTimer *int `json:"wait_timer,omitempty"` 58 Reviewers []*RequiredReviewer `json:"reviewers,omitempty"` 59 } 60 61 // RequiredReviewer represents a required reviewer. 62 type RequiredReviewer struct { 63 Type *string `json:"type,omitempty"` 64 Reviewer interface{} `json:"reviewer,omitempty"` 65 } 66 67 // EnvironmentListOptions specifies the optional parameters to the 68 // RepositoriesService.ListEnvironments method. 69 type EnvironmentListOptions struct { 70 ListOptions 71 } 72 73 // UnmarshalJSON implements the json.Unmarshaler interface. 74 // This helps us handle the fact that RequiredReviewer can have either a User or Team type reviewer field. 75 func (r *RequiredReviewer) UnmarshalJSON(data []byte) error { 76 type aliasReviewer RequiredReviewer 77 var reviewer aliasReviewer 78 if err := json.Unmarshal(data, &reviewer); err != nil { 79 return err 80 } 81 82 r.Type = reviewer.Type 83 84 switch *reviewer.Type { 85 case "User": 86 reviewer.Reviewer = &User{} 87 if err := json.Unmarshal(data, &reviewer); err != nil { 88 return err 89 } 90 r.Reviewer = reviewer.Reviewer 91 case "Team": 92 reviewer.Reviewer = &Team{} 93 if err := json.Unmarshal(data, &reviewer); err != nil { 94 return err 95 } 96 r.Reviewer = reviewer.Reviewer 97 default: 98 r.Type = nil 99 r.Reviewer = nil 100 return fmt.Errorf("reviewer.Type is %T, not a string of 'User' or 'Team', unable to unmarshal", reviewer.Type) 101 } 102 103 return nil 104 } 105 106 // ListEnvironments lists all environments for a repository. 107 // 108 // GitHub API docs: https://docs.github.com/en/rest/deployments/environments#get-all-environments 109 func (s *RepositoriesService) ListEnvironments(ctx context.Context, owner, repo string, opts *EnvironmentListOptions) (*EnvResponse, *Response, error) { 110 u := fmt.Sprintf("repos/%s/%s/environments", owner, repo) 111 u, err := addOptions(u, opts) 112 if err != nil { 113 return nil, nil, err 114 } 115 116 req, err := s.client.NewRequest("GET", u, nil) 117 if err != nil { 118 return nil, nil, err 119 } 120 121 var list *EnvResponse 122 resp, err := s.client.Do(ctx, req, &list) 123 if err != nil { 124 return nil, resp, err 125 } 126 return list, resp, nil 127 } 128 129 // GetEnvironment get a single environment for a repository. 130 // 131 // GitHub API docs: https://docs.github.com/en/rest/deployments/environments#get-an-environment 132 func (s *RepositoriesService) GetEnvironment(ctx context.Context, owner, repo, name string) (*Environment, *Response, error) { 133 u := fmt.Sprintf("repos/%s/%s/environments/%s", owner, repo, name) 134 135 req, err := s.client.NewRequest("GET", u, nil) 136 if err != nil { 137 return nil, nil, err 138 } 139 140 var env *Environment 141 resp, err := s.client.Do(ctx, req, &env) 142 if err != nil { 143 return nil, resp, err 144 } 145 return env, resp, nil 146 } 147 148 // MarshalJSON implements the json.Marshaler interface. 149 // As the only way to clear a WaitTimer is to set it to 0, a missing WaitTimer object should default to 0, not null. 150 func (c *CreateUpdateEnvironment) MarshalJSON() ([]byte, error) { 151 type Alias CreateUpdateEnvironment 152 if c.WaitTimer == nil { 153 c.WaitTimer = Int(0) 154 } 155 return json.Marshal(&struct { 156 *Alias 157 }{ 158 Alias: (*Alias)(c), 159 }) 160 } 161 162 // CreateUpdateEnvironment represents the fields required for the create/update operation 163 // following the Create/Update release example. 164 // See https://github.com/google/go-github/issues/992 for more information. 165 // Removed omitempty here as the API expects null values for reviewers and deployment_branch_policy to clear them. 166 type CreateUpdateEnvironment struct { 167 WaitTimer *int `json:"wait_timer"` 168 Reviewers []*EnvReviewers `json:"reviewers"` 169 DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy"` 170 } 171 172 // createUpdateEnvironmentNoEnterprise represents the fields accepted for Pro/Teams private repos. 173 // Ref: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment 174 // See https://github.com/google/go-github/issues/2602 for more information. 175 type createUpdateEnvironmentNoEnterprise struct { 176 DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy"` 177 } 178 179 // CreateUpdateEnvironment create or update a new environment for a repository. 180 // 181 // GitHub API docs: https://docs.github.com/en/rest/deployments/environments#create-or-update-an-environment 182 func (s *RepositoriesService) CreateUpdateEnvironment(ctx context.Context, owner, repo, name string, environment *CreateUpdateEnvironment) (*Environment, *Response, error) { 183 u := fmt.Sprintf("repos/%s/%s/environments/%s", owner, repo, name) 184 185 req, err := s.client.NewRequest("PUT", u, environment) 186 if err != nil { 187 return nil, nil, err 188 } 189 190 e := new(Environment) 191 resp, err := s.client.Do(ctx, req, e) 192 if err != nil { 193 // The API returns 422 when the pricing plan doesn't support all the fields sent. 194 // This path will be executed for Pro/Teams private repos. 195 // For public repos, regardless of the pricing plan, all fields supported. 196 // For Free plan private repos the returned error code is 404. 197 // We are checking that the user didn't try to send a value for unsupported fields, 198 // and return an error if they did. 199 if resp != nil && resp.StatusCode == http.StatusUnprocessableEntity && environment != nil && len(environment.Reviewers) == 0 && environment.GetWaitTimer() == 0 { 200 return s.createNewEnvNoEnterprise(ctx, u, environment) 201 } 202 return nil, resp, err 203 } 204 return e, resp, nil 205 } 206 207 // createNewEnvNoEnterprise is an internal function for cases where the original call returned 422. 208 // Currently only the `deployment_branch_policy` parameter is supported for Pro/Team private repos. 209 func (s *RepositoriesService) createNewEnvNoEnterprise(ctx context.Context, u string, environment *CreateUpdateEnvironment) (*Environment, *Response, error) { 210 req, err := s.client.NewRequest("PUT", u, &createUpdateEnvironmentNoEnterprise{ 211 DeploymentBranchPolicy: environment.DeploymentBranchPolicy, 212 }) 213 if err != nil { 214 return nil, nil, err 215 } 216 217 e := new(Environment) 218 resp, err := s.client.Do(ctx, req, e) 219 if err != nil { 220 return nil, resp, err 221 } 222 return e, resp, nil 223 } 224 225 // DeleteEnvironment delete an environment from a repository. 226 // 227 // GitHub API docs: https://docs.github.com/en/rest/deployments/environments#delete-an-environment 228 func (s *RepositoriesService) DeleteEnvironment(ctx context.Context, owner, repo, name string) (*Response, error) { 229 u := fmt.Sprintf("repos/%s/%s/environments/%s", owner, repo, name) 230 231 req, err := s.client.NewRequest("DELETE", u, nil) 232 if err != nil { 233 return nil, err 234 } 235 return s.client.Do(ctx, req, nil) 236 }