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