1// Copyright 2014 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
6package github
7
8import (
9	"context"
10	"encoding/json"
11	"fmt"
12	"strings"
13)
14
15// Deployment represents a deployment in a repo
16type Deployment struct {
17	URL           *string         `json:"url,omitempty"`
18	ID            *int64          `json:"id,omitempty"`
19	SHA           *string         `json:"sha,omitempty"`
20	Ref           *string         `json:"ref,omitempty"`
21	Task          *string         `json:"task,omitempty"`
22	Payload       json.RawMessage `json:"payload,omitempty"`
23	Environment   *string         `json:"environment,omitempty"`
24	Description   *string         `json:"description,omitempty"`
25	Creator       *User           `json:"creator,omitempty"`
26	CreatedAt     *Timestamp      `json:"created_at,omitempty"`
27	UpdatedAt     *Timestamp      `json:"updated_at,omitempty"`
28	StatusesURL   *string         `json:"statuses_url,omitempty"`
29	RepositoryURL *string         `json:"repository_url,omitempty"`
30	NodeID        *string         `json:"node_id,omitempty"`
31}
32
33// DeploymentRequest represents a deployment request
34type DeploymentRequest struct {
35	Ref                   *string   `json:"ref,omitempty"`
36	Task                  *string   `json:"task,omitempty"`
37	AutoMerge             *bool     `json:"auto_merge,omitempty"`
38	RequiredContexts      *[]string `json:"required_contexts,omitempty"`
39	Payload               *string   `json:"payload,omitempty"`
40	Environment           *string   `json:"environment,omitempty"`
41	Description           *string   `json:"description,omitempty"`
42	TransientEnvironment  *bool     `json:"transient_environment,omitempty"`
43	ProductionEnvironment *bool     `json:"production_environment,omitempty"`
44}
45
46// DeploymentsListOptions specifies the optional parameters to the
47// RepositoriesService.ListDeployments method.
48type DeploymentsListOptions struct {
49	// SHA of the Deployment.
50	SHA string `url:"sha,omitempty"`
51
52	// List deployments for a given ref.
53	Ref string `url:"ref,omitempty"`
54
55	// List deployments for a given task.
56	Task string `url:"task,omitempty"`
57
58	// List deployments for a given environment.
59	Environment string `url:"environment,omitempty"`
60
61	ListOptions
62}
63
64// ListDeployments lists the deployments of a repository.
65//
66// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployments
67func (s *RepositoriesService) ListDeployments(ctx context.Context, owner, repo string, opt *DeploymentsListOptions) ([]*Deployment, *Response, error) {
68	u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo)
69	u, err := addOptions(u, opt)
70	if err != nil {
71		return nil, nil, err
72	}
73
74	req, err := s.client.NewRequest("GET", u, nil)
75	if err != nil {
76		return nil, nil, err
77	}
78
79	var deployments []*Deployment
80	resp, err := s.client.Do(ctx, req, &deployments)
81	if err != nil {
82		return nil, resp, err
83	}
84
85	return deployments, resp, nil
86}
87
88// GetDeployment returns a single deployment of a repository.
89//
90// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment
91func (s *RepositoriesService) GetDeployment(ctx context.Context, owner, repo string, deploymentID int64) (*Deployment, *Response, error) {
92	u := fmt.Sprintf("repos/%v/%v/deployments/%v", owner, repo, deploymentID)
93
94	req, err := s.client.NewRequest("GET", u, nil)
95	if err != nil {
96		return nil, nil, err
97	}
98
99	deployment := new(Deployment)
100	resp, err := s.client.Do(ctx, req, deployment)
101	if err != nil {
102		return nil, resp, err
103	}
104
105	return deployment, resp, nil
106}
107
108// CreateDeployment creates a new deployment for a repository.
109//
110// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment
111func (s *RepositoriesService) CreateDeployment(ctx context.Context, owner, repo string, request *DeploymentRequest) (*Deployment, *Response, error) {
112	u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo)
113
114	req, err := s.client.NewRequest("POST", u, request)
115	if err != nil {
116		return nil, nil, err
117	}
118
119	// TODO: remove custom Accept headers when APIs fully launch.
120	acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview}
121	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
122
123	d := new(Deployment)
124	resp, err := s.client.Do(ctx, req, d)
125	if err != nil {
126		return nil, resp, err
127	}
128
129	return d, resp, nil
130}
131
132// DeploymentStatus represents the status of a
133// particular deployment.
134type DeploymentStatus struct {
135	ID *int64 `json:"id,omitempty"`
136	// State is the deployment state.
137	// Possible values are: "pending", "success", "failure", "error", "inactive".
138	State         *string    `json:"state,omitempty"`
139	Creator       *User      `json:"creator,omitempty"`
140	Description   *string    `json:"description,omitempty"`
141	TargetURL     *string    `json:"target_url,omitempty"`
142	CreatedAt     *Timestamp `json:"created_at,omitempty"`
143	UpdatedAt     *Timestamp `json:"updated_at,omitempty"`
144	DeploymentURL *string    `json:"deployment_url,omitempty"`
145	RepositoryURL *string    `json:"repository_url,omitempty"`
146	NodeID        *string    `json:"node_id,omitempty"`
147}
148
149// DeploymentStatusRequest represents a deployment request
150type DeploymentStatusRequest struct {
151	State          *string `json:"state,omitempty"`
152	LogURL         *string `json:"log_url,omitempty"`
153	Description    *string `json:"description,omitempty"`
154	Environment    *string `json:"environment,omitempty"`
155	EnvironmentURL *string `json:"environment_url,omitempty"`
156	AutoInactive   *bool   `json:"auto_inactive,omitempty"`
157}
158
159// ListDeploymentStatuses lists the statuses of a given deployment of a repository.
160//
161// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployment-statuses
162func (s *RepositoriesService) ListDeploymentStatuses(ctx context.Context, owner, repo string, deployment int64, opt *ListOptions) ([]*DeploymentStatus, *Response, error) {
163	u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment)
164	u, err := addOptions(u, opt)
165	if err != nil {
166		return nil, nil, err
167	}
168
169	req, err := s.client.NewRequest("GET", u, nil)
170	if err != nil {
171		return nil, nil, err
172	}
173
174	var statuses []*DeploymentStatus
175	resp, err := s.client.Do(ctx, req, &statuses)
176	if err != nil {
177		return nil, resp, err
178	}
179
180	return statuses, resp, nil
181}
182
183// GetDeploymentStatus returns a single deployment status of a repository.
184//
185// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment-status
186func (s *RepositoriesService) GetDeploymentStatus(ctx context.Context, owner, repo string, deploymentID, deploymentStatusID int64) (*DeploymentStatus, *Response, error) {
187	u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses/%v", owner, repo, deploymentID, deploymentStatusID)
188
189	req, err := s.client.NewRequest("GET", u, nil)
190	if err != nil {
191		return nil, nil, err
192	}
193
194	// TODO: remove custom Accept headers when APIs fully launch.
195	acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview}
196	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
197
198	d := new(DeploymentStatus)
199	resp, err := s.client.Do(ctx, req, d)
200	if err != nil {
201		return nil, resp, err
202	}
203
204	return d, resp, nil
205}
206
207// CreateDeploymentStatus creates a new status for a deployment.
208//
209// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment-status
210func (s *RepositoriesService) CreateDeploymentStatus(ctx context.Context, owner, repo string, deployment int64, request *DeploymentStatusRequest) (*DeploymentStatus, *Response, error) {
211	u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment)
212
213	req, err := s.client.NewRequest("POST", u, request)
214	if err != nil {
215		return nil, nil, err
216	}
217
218	// TODO: remove custom Accept headers when APIs fully launch.
219	acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview}
220	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
221
222	d := new(DeploymentStatus)
223	resp, err := s.client.Do(ctx, req, d)
224	if err != nil {
225		return nil, resp, err
226	}
227
228	return d, resp, nil
229}
230