github.com/google/go-github/v71@v71.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  }