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