1// Copyright 2018 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	"fmt"
11)
12
13// ChecksService provides access to the Checks API in the
14// GitHub API.
15//
16// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/
17type ChecksService service
18
19// CheckRun represents a GitHub check run on a repository associated with a GitHub app.
20type CheckRun struct {
21	ID           *int64          `json:"id,omitempty"`
22	NodeID       *string         `json:"node_id,omitempty"`
23	HeadSHA      *string         `json:"head_sha,omitempty"`
24	ExternalID   *string         `json:"external_id,omitempty"`
25	URL          *string         `json:"url,omitempty"`
26	HTMLURL      *string         `json:"html_url,omitempty"`
27	DetailsURL   *string         `json:"details_url,omitempty"`
28	Status       *string         `json:"status,omitempty"`
29	Conclusion   *string         `json:"conclusion,omitempty"`
30	StartedAt    *Timestamp      `json:"started_at,omitempty"`
31	CompletedAt  *Timestamp      `json:"completed_at,omitempty"`
32	Output       *CheckRunOutput `json:"output,omitempty"`
33	Name         *string         `json:"name,omitempty"`
34	CheckSuite   *CheckSuite     `json:"check_suite,omitempty"`
35	App          *App            `json:"app,omitempty"`
36	PullRequests []*PullRequest  `json:"pull_requests,omitempty"`
37}
38
39// CheckRunOutput represents the output of a CheckRun.
40type CheckRunOutput struct {
41	Title            *string               `json:"title,omitempty"`
42	Summary          *string               `json:"summary,omitempty"`
43	Text             *string               `json:"text,omitempty"`
44	AnnotationsCount *int                  `json:"annotations_count,omitempty"`
45	AnnotationsURL   *string               `json:"annotations_url,omitempty"`
46	Annotations      []*CheckRunAnnotation `json:"annotations,omitempty"`
47	Images           []*CheckRunImage      `json:"images,omitempty"`
48}
49
50// CheckRunAnnotation represents an annotation object for a CheckRun output.
51type CheckRunAnnotation struct {
52	Path            *string `json:"path,omitempty"`
53	StartLine       *int    `json:"start_line,omitempty"`
54	EndLine         *int    `json:"end_line,omitempty"`
55	StartColumn     *int    `json:"start_column,omitempty"`
56	EndColumn       *int    `json:"end_column,omitempty"`
57	AnnotationLevel *string `json:"annotation_level,omitempty"`
58	Message         *string `json:"message,omitempty"`
59	Title           *string `json:"title,omitempty"`
60	RawDetails      *string `json:"raw_details,omitempty"`
61}
62
63// CheckRunImage represents an image object for a CheckRun output.
64type CheckRunImage struct {
65	Alt      *string `json:"alt,omitempty"`
66	ImageURL *string `json:"image_url,omitempty"`
67	Caption  *string `json:"caption,omitempty"`
68}
69
70// CheckSuite represents a suite of check runs.
71type CheckSuite struct {
72	ID           *int64         `json:"id,omitempty"`
73	NodeID       *string        `json:"node_id,omitempty"`
74	HeadBranch   *string        `json:"head_branch,omitempty"`
75	HeadSHA      *string        `json:"head_sha,omitempty"`
76	URL          *string        `json:"url,omitempty"`
77	BeforeSHA    *string        `json:"before,omitempty"`
78	AfterSHA     *string        `json:"after,omitempty"`
79	Status       *string        `json:"status,omitempty"`
80	Conclusion   *string        `json:"conclusion,omitempty"`
81	App          *App           `json:"app,omitempty"`
82	Repository   *Repository    `json:"repository,omitempty"`
83	PullRequests []*PullRequest `json:"pull_requests,omitempty"`
84
85	// The following fields are only populated by Webhook events.
86	HeadCommit *Commit `json:"head_commit,omitempty"`
87}
88
89func (c CheckRun) String() string {
90	return Stringify(c)
91}
92
93func (c CheckSuite) String() string {
94	return Stringify(c)
95}
96
97// GetCheckRun gets a check-run for a repository.
98//
99// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#get-a-check-run
100func (s *ChecksService) GetCheckRun(ctx context.Context, owner, repo string, checkRunID int64) (*CheckRun, *Response, error) {
101	u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID)
102	req, err := s.client.NewRequest("GET", u, nil)
103	if err != nil {
104		return nil, nil, err
105	}
106
107	checkRun := new(CheckRun)
108	resp, err := s.client.Do(ctx, req, checkRun)
109	if err != nil {
110		return nil, resp, err
111	}
112
113	return checkRun, resp, nil
114}
115
116// GetCheckSuite gets a single check suite.
117//
118// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#get-a-check-suite
119func (s *ChecksService) GetCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*CheckSuite, *Response, error) {
120	u := fmt.Sprintf("repos/%v/%v/check-suites/%v", owner, repo, checkSuiteID)
121	req, err := s.client.NewRequest("GET", u, nil)
122	if err != nil {
123		return nil, nil, err
124	}
125
126	checkSuite := new(CheckSuite)
127	resp, err := s.client.Do(ctx, req, checkSuite)
128	if err != nil {
129		return nil, resp, err
130	}
131
132	return checkSuite, resp, nil
133}
134
135// CreateCheckRunOptions sets up parameters needed to create a CheckRun.
136type CreateCheckRunOptions struct {
137	Name        string            `json:"name"`                   // The name of the check (e.g., "code-coverage"). (Required.)
138	HeadSHA     string            `json:"head_sha"`               // The SHA of the commit. (Required.)
139	DetailsURL  *string           `json:"details_url,omitempty"`  // The URL of the integrator's site that has the full details of the check. (Optional.)
140	ExternalID  *string           `json:"external_id,omitempty"`  // A reference for the run on the integrator's system. (Optional.)
141	Status      *string           `json:"status,omitempty"`       // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.)
142	Conclusion  *string           `json:"conclusion,omitempty"`   // Can be one of "success", "failure", "neutral", "cancelled", "skipped", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".)
143	StartedAt   *Timestamp        `json:"started_at,omitempty"`   // The time that the check run began. (Optional.)
144	CompletedAt *Timestamp        `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.)
145	Output      *CheckRunOutput   `json:"output,omitempty"`       // Provide descriptive details about the run. (Optional)
146	Actions     []*CheckRunAction `json:"actions,omitempty"`      // Possible further actions the integrator can perform, which a user may trigger. (Optional.)
147}
148
149// CheckRunAction exposes further actions the integrator can perform, which a user may trigger.
150type CheckRunAction struct {
151	Label       string `json:"label"`       // The text to be displayed on a button in the web UI. The maximum size is 20 characters. (Required.)
152	Description string `json:"description"` // A short explanation of what this action would do. The maximum size is 40 characters. (Required.)
153	Identifier  string `json:"identifier"`  // A reference for the action on the integrator's system. The maximum size is 20 characters. (Required.)
154}
155
156// CreateCheckRun creates a check run for repository.
157//
158// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#create-a-check-run
159func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string, opts CreateCheckRunOptions) (*CheckRun, *Response, error) {
160	u := fmt.Sprintf("repos/%v/%v/check-runs", owner, repo)
161	req, err := s.client.NewRequest("POST", u, opts)
162	if err != nil {
163		return nil, nil, err
164	}
165
166	checkRun := new(CheckRun)
167	resp, err := s.client.Do(ctx, req, checkRun)
168	if err != nil {
169		return nil, resp, err
170	}
171
172	return checkRun, resp, nil
173}
174
175// UpdateCheckRunOptions sets up parameters needed to update a CheckRun.
176type UpdateCheckRunOptions struct {
177	Name        string            `json:"name"`                   // The name of the check (e.g., "code-coverage"). (Required.)
178	DetailsURL  *string           `json:"details_url,omitempty"`  // The URL of the integrator's site that has the full details of the check. (Optional.)
179	ExternalID  *string           `json:"external_id,omitempty"`  // A reference for the run on the integrator's system. (Optional.)
180	Status      *string           `json:"status,omitempty"`       // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.)
181	Conclusion  *string           `json:"conclusion,omitempty"`   // Can be one of "success", "failure", "neutral", "cancelled", "skipped", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".)
182	CompletedAt *Timestamp        `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.)
183	Output      *CheckRunOutput   `json:"output,omitempty"`       // Provide descriptive details about the run. (Optional)
184	Actions     []*CheckRunAction `json:"actions,omitempty"`      // Possible further actions the integrator can perform, which a user may trigger. (Optional.)
185}
186
187// UpdateCheckRun updates a check run for a specific commit in a repository.
188//
189// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#update-a-check-run
190func (s *ChecksService) UpdateCheckRun(ctx context.Context, owner, repo string, checkRunID int64, opts UpdateCheckRunOptions) (*CheckRun, *Response, error) {
191	u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID)
192	req, err := s.client.NewRequest("PATCH", u, opts)
193	if err != nil {
194		return nil, nil, err
195	}
196
197	checkRun := new(CheckRun)
198	resp, err := s.client.Do(ctx, req, checkRun)
199	if err != nil {
200		return nil, resp, err
201	}
202
203	return checkRun, resp, nil
204}
205
206// ListCheckRunAnnotations lists the annotations for a check run.
207//
208// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#list-check-run-annotations
209func (s *ChecksService) ListCheckRunAnnotations(ctx context.Context, owner, repo string, checkRunID int64, opts *ListOptions) ([]*CheckRunAnnotation, *Response, error) {
210	u := fmt.Sprintf("repos/%v/%v/check-runs/%v/annotations", owner, repo, checkRunID)
211	u, err := addOptions(u, opts)
212	if err != nil {
213		return nil, nil, err
214	}
215
216	req, err := s.client.NewRequest("GET", u, nil)
217	if err != nil {
218		return nil, nil, err
219	}
220
221	var checkRunAnnotations []*CheckRunAnnotation
222	resp, err := s.client.Do(ctx, req, &checkRunAnnotations)
223	if err != nil {
224		return nil, resp, err
225	}
226
227	return checkRunAnnotations, resp, nil
228}
229
230// ListCheckRunsOptions represents parameters to list check runs.
231type ListCheckRunsOptions struct {
232	CheckName *string `url:"check_name,omitempty"` // Returns check runs with the specified name.
233	Status    *string `url:"status,omitempty"`     // Returns check runs with the specified status. Can be one of "queued", "in_progress", or "completed".
234	Filter    *string `url:"filter,omitempty"`     // Filters check runs by their completed_at timestamp. Can be one of "latest" (returning the most recent check runs) or "all". Default: "latest"
235
236	ListOptions
237}
238
239// ListCheckRunsResults represents the result of a check run list.
240type ListCheckRunsResults struct {
241	Total     *int        `json:"total_count,omitempty"`
242	CheckRuns []*CheckRun `json:"check_runs,omitempty"`
243}
244
245// ListCheckRunsForRef lists check runs for a specific ref.
246//
247// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#list-check-runs-for-a-git-reference
248func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, ref string, opts *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) {
249	u := fmt.Sprintf("repos/%v/%v/commits/%v/check-runs", owner, repo, refURLEscape(ref))
250	u, err := addOptions(u, opts)
251	if err != nil {
252		return nil, nil, err
253	}
254
255	req, err := s.client.NewRequest("GET", u, nil)
256	if err != nil {
257		return nil, nil, err
258	}
259
260	var checkRunResults *ListCheckRunsResults
261	resp, err := s.client.Do(ctx, req, &checkRunResults)
262	if err != nil {
263		return nil, resp, err
264	}
265
266	return checkRunResults, resp, nil
267}
268
269// ListCheckRunsCheckSuite lists check runs for a check suite.
270//
271// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#list-check-runs-in-a-check-suite
272func (s *ChecksService) ListCheckRunsCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64, opts *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) {
273	u := fmt.Sprintf("repos/%v/%v/check-suites/%v/check-runs", owner, repo, checkSuiteID)
274	u, err := addOptions(u, opts)
275	if err != nil {
276		return nil, nil, err
277	}
278
279	req, err := s.client.NewRequest("GET", u, nil)
280	if err != nil {
281		return nil, nil, err
282	}
283
284	var checkRunResults *ListCheckRunsResults
285	resp, err := s.client.Do(ctx, req, &checkRunResults)
286	if err != nil {
287		return nil, resp, err
288	}
289
290	return checkRunResults, resp, nil
291}
292
293// ListCheckSuiteOptions represents parameters to list check suites.
294type ListCheckSuiteOptions struct {
295	CheckName *string `url:"check_name,omitempty"` // Filters checks suites by the name of the check run.
296	AppID     *int    `url:"app_id,omitempty"`     // Filters check suites by GitHub App id.
297
298	ListOptions
299}
300
301// ListCheckSuiteResults represents the result of a check run list.
302type ListCheckSuiteResults struct {
303	Total       *int          `json:"total_count,omitempty"`
304	CheckSuites []*CheckSuite `json:"check_suites,omitempty"`
305}
306
307// ListCheckSuitesForRef lists check suite for a specific ref.
308//
309// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#list-check-suites-for-a-git-reference
310func (s *ChecksService) ListCheckSuitesForRef(ctx context.Context, owner, repo, ref string, opts *ListCheckSuiteOptions) (*ListCheckSuiteResults, *Response, error) {
311	u := fmt.Sprintf("repos/%v/%v/commits/%v/check-suites", owner, repo, refURLEscape(ref))
312	u, err := addOptions(u, opts)
313	if err != nil {
314		return nil, nil, err
315	}
316
317	req, err := s.client.NewRequest("GET", u, nil)
318	if err != nil {
319		return nil, nil, err
320	}
321
322	var checkSuiteResults *ListCheckSuiteResults
323	resp, err := s.client.Do(ctx, req, &checkSuiteResults)
324	if err != nil {
325		return nil, resp, err
326	}
327
328	return checkSuiteResults, resp, nil
329}
330
331// AutoTriggerCheck enables or disables automatic creation of CheckSuite events upon pushes to the repository.
332type AutoTriggerCheck struct {
333	AppID   *int64 `json:"app_id,omitempty"`  // The id of the GitHub App. (Required.)
334	Setting *bool  `json:"setting,omitempty"` // Set to "true" to enable automatic creation of CheckSuite events upon pushes to the repository, or "false" to disable them. Default: "true" (Required.)
335}
336
337// CheckSuitePreferenceOptions set options for check suite preferences for a repository.
338type CheckSuitePreferenceOptions struct {
339	AutoTriggerChecks []*AutoTriggerCheck `json:"auto_trigger_checks,omitempty"` // A slice of auto trigger checks that can be set for a check suite in a repository.
340}
341
342// CheckSuitePreferenceResults represents the results of the preference set operation.
343type CheckSuitePreferenceResults struct {
344	Preferences *PreferenceList `json:"preferences,omitempty"`
345	Repository  *Repository     `json:"repository,omitempty"`
346}
347
348// PreferenceList represents a list of auto trigger checks for repository
349type PreferenceList struct {
350	AutoTriggerChecks []*AutoTriggerCheck `json:"auto_trigger_checks,omitempty"` // A slice of auto trigger checks that can be set for a check suite in a repository.
351}
352
353// SetCheckSuitePreferences changes the default automatic flow when creating check suites.
354//
355// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#update-repository-preferences-for-check-suites
356func (s *ChecksService) SetCheckSuitePreferences(ctx context.Context, owner, repo string, opts CheckSuitePreferenceOptions) (*CheckSuitePreferenceResults, *Response, error) {
357	u := fmt.Sprintf("repos/%v/%v/check-suites/preferences", owner, repo)
358	req, err := s.client.NewRequest("PATCH", u, opts)
359	if err != nil {
360		return nil, nil, err
361	}
362
363	var checkSuitePrefResults *CheckSuitePreferenceResults
364	resp, err := s.client.Do(ctx, req, &checkSuitePrefResults)
365	if err != nil {
366		return nil, resp, err
367	}
368
369	return checkSuitePrefResults, resp, nil
370}
371
372// CreateCheckSuiteOptions sets up parameters to manually create a check suites
373type CreateCheckSuiteOptions struct {
374	HeadSHA    string  `json:"head_sha"`              // The sha of the head commit. (Required.)
375	HeadBranch *string `json:"head_branch,omitempty"` // The name of the head branch where the code changes are implemented.
376}
377
378// CreateCheckSuite manually creates a check suite for a repository.
379//
380// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#create-a-check-suite
381func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string, opts CreateCheckSuiteOptions) (*CheckSuite, *Response, error) {
382	u := fmt.Sprintf("repos/%v/%v/check-suites", owner, repo)
383	req, err := s.client.NewRequest("POST", u, opts)
384	if err != nil {
385		return nil, nil, err
386	}
387
388	checkSuite := new(CheckSuite)
389	resp, err := s.client.Do(ctx, req, checkSuite)
390	if err != nil {
391		return nil, resp, err
392	}
393
394	return checkSuite, resp, nil
395}
396
397// ReRequestCheckSuite triggers GitHub to rerequest an existing check suite, without pushing new code to a repository.
398//
399// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/checks/#rerequest-a-check-suite
400func (s *ChecksService) ReRequestCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*Response, error) {
401	u := fmt.Sprintf("repos/%v/%v/check-suites/%v/rerequest", owner, repo, checkSuiteID)
402
403	req, err := s.client.NewRequest("POST", u, nil)
404	if err != nil {
405		return nil, err
406	}
407
408	resp, err := s.client.Do(ctx, req, nil)
409	return resp, err
410}
411