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  }