1// 2// Copyright 2021, Igor Varavko 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17package gitlab 18 19import ( 20 "fmt" 21 "net/http" 22 "time" 23) 24 25// PipelinesService handles communication with the repositories related 26// methods of the GitLab API. 27// 28// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html 29type PipelinesService struct { 30 client *Client 31} 32 33// PipelineVariable represents a pipeline variable. 34// 35// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html 36type PipelineVariable struct { 37 Key string `json:"key"` 38 Value string `json:"value"` 39 VariableType string `json:"variable_type"` 40} 41 42// Pipeline represents a GitLab pipeline. 43// 44// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html 45type Pipeline struct { 46 ID int `json:"id"` 47 ProjectID int `json:"project_id"` 48 Status string `json:"status"` 49 Ref string `json:"ref"` 50 SHA string `json:"sha"` 51 BeforeSHA string `json:"before_sha"` 52 Tag bool `json:"tag"` 53 YamlErrors string `json:"yaml_errors"` 54 User *BasicUser `json:"user"` 55 UpdatedAt *time.Time `json:"updated_at"` 56 CreatedAt *time.Time `json:"created_at"` 57 StartedAt *time.Time `json:"started_at"` 58 FinishedAt *time.Time `json:"finished_at"` 59 CommittedAt *time.Time `json:"committed_at"` 60 Duration int `json:"duration"` 61 QueuedDuration int `json:"queued_duration"` 62 Coverage string `json:"coverage"` 63 WebURL string `json:"web_url"` 64 DetailedStatus *DetailedStatus `json:"detailed_status"` 65} 66 67// DetailedStatus contains detailed information about the status of a pipeline. 68type DetailedStatus struct { 69 Icon string `json:"icon"` 70 Text string `json:"text"` 71 Label string `json:"label"` 72 Group string `json:"group"` 73 Tooltip string `json:"tooltip"` 74 HasDetails bool `json:"has_details"` 75 DetailsPath string `json:"details_path"` 76 Illustration struct { 77 Image string `json:"image"` 78 } `json:"illustration"` 79 Favicon string `json:"favicon"` 80} 81 82func (p Pipeline) String() string { 83 return Stringify(p) 84} 85 86// PipelineTestReport contains a detailed report of a test run. 87type PipelineTestReport struct { 88 TotalTime float64 `json:"total_time"` 89 TotalCount int `json:"total_count"` 90 SuccessCount int `json:"success_count"` 91 FailedCount int `json:"failed_count"` 92 SkippedCount int `json:"skipped_count"` 93 ErrorCount int `json:"error_count"` 94 TestSuites []PipelineTestSuites `json:"test_suites"` 95} 96 97// PipelineTestSuites contains test suites results. 98type PipelineTestSuites struct { 99 Name string `json:"name"` 100 TotalTime float64 `json:"total_time"` 101 TotalCount int `json:"total_count"` 102 SuccessCount int `json:"success_count"` 103 FailedCount int `json:"failed_count"` 104 SkippedCount int `json:"skipped_count"` 105 ErrorCount int `json:"error_count"` 106 TestCases []PipelineTestCases `json:"test_cases"` 107} 108 109// PipelineTestCases contains test cases details. 110type PipelineTestCases struct { 111 Status string `json:"status"` 112 Name string `json:"name"` 113 Classname string `json:"classname"` 114 File string `json:"file"` 115 ExecutionTime float64 `json:"execution_time"` 116 SystemOutput string `json:"system_output"` 117 StackTrace string `json:"stack_trace"` 118 AttachmentURL string `json:"attachment_url"` 119 RecentFailures RecentFailures `json:"recent_failures"` 120} 121 122// RecentFailures contains failures count for the project's default branch. 123type RecentFailures struct { 124 Count int `json:"count"` 125 BaseBranch string `json:"base_branch"` 126} 127 128func (p PipelineTestReport) String() string { 129 return Stringify(p) 130} 131 132// PipelineInfo shows the basic entities of a pipeline, mostly used as fields 133// on other assets, like Commit. 134type PipelineInfo struct { 135 ID int `json:"id"` 136 ProjectID int `json:"project_id"` 137 Status string `json:"status"` 138 Ref string `json:"ref"` 139 SHA string `json:"sha"` 140 WebURL string `json:"web_url"` 141 UpdatedAt *time.Time `json:"updated_at"` 142 CreatedAt *time.Time `json:"created_at"` 143} 144 145func (p PipelineInfo) String() string { 146 return Stringify(p) 147} 148 149// ListProjectPipelinesOptions represents the available ListProjectPipelines() options. 150// 151// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines 152type ListProjectPipelinesOptions struct { 153 ListOptions 154 Scope *string `url:"scope,omitempty" json:"scope,omitempty"` 155 Status *BuildStateValue `url:"status,omitempty" json:"status,omitempty"` 156 Ref *string `url:"ref,omitempty" json:"ref,omitempty"` 157 SHA *string `url:"sha,omitempty" json:"sha,omitempty"` 158 YamlErrors *bool `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"` 159 Name *string `url:"name,omitempty" json:"name,omitempty"` 160 Username *string `url:"username,omitempty" json:"username,omitempty"` 161 UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"` 162 UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"` 163 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` 164 Sort *string `url:"sort,omitempty" json:"sort,omitempty"` 165} 166 167// ListProjectPipelines gets a list of project piplines. 168// 169// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines 170func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjectPipelinesOptions, options ...RequestOptionFunc) ([]*PipelineInfo, *Response, error) { 171 project, err := parseID(pid) 172 if err != nil { 173 return nil, nil, err 174 } 175 u := fmt.Sprintf("projects/%s/pipelines", pathEscape(project)) 176 177 req, err := s.client.NewRequest(http.MethodGet, u, opt, options) 178 if err != nil { 179 return nil, nil, err 180 } 181 182 var p []*PipelineInfo 183 resp, err := s.client.Do(req, &p) 184 if err != nil { 185 return nil, resp, err 186 } 187 188 return p, resp, err 189} 190 191// GetPipeline gets a single project pipeline. 192// 193// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-a-single-pipeline 194func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) { 195 project, err := parseID(pid) 196 if err != nil { 197 return nil, nil, err 198 } 199 u := fmt.Sprintf("projects/%s/pipelines/%d", pathEscape(project), pipeline) 200 201 req, err := s.client.NewRequest(http.MethodGet, u, nil, options) 202 if err != nil { 203 return nil, nil, err 204 } 205 206 p := new(Pipeline) 207 resp, err := s.client.Do(req, p) 208 if err != nil { 209 return nil, resp, err 210 } 211 212 return p, resp, err 213} 214 215// GetPipelineVariables gets the variables of a single project pipeline. 216// 217// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-variables-of-a-pipeline 218func (s *PipelinesService) GetPipelineVariables(pid interface{}, pipeline int, options ...RequestOptionFunc) ([]*PipelineVariable, *Response, error) { 219 project, err := parseID(pid) 220 if err != nil { 221 return nil, nil, err 222 } 223 u := fmt.Sprintf("projects/%s/pipelines/%d/variables", pathEscape(project), pipeline) 224 225 req, err := s.client.NewRequest(http.MethodGet, u, nil, options) 226 if err != nil { 227 return nil, nil, err 228 } 229 230 var p []*PipelineVariable 231 resp, err := s.client.Do(req, &p) 232 if err != nil { 233 return nil, resp, err 234 } 235 236 return p, resp, err 237} 238 239// GetPipelineTestReport gets the test report of a single project pipeline. 240// 241// GitLab API docs: https://docs.gitlab.com/ee/api/pipelines.html#get-a-pipelines-test-report 242func (s *PipelinesService) GetPipelineTestReport(pid interface{}, pipeline int) (*PipelineTestReport, *Response, error) { 243 project, err := parseID(pid) 244 if err != nil { 245 return nil, nil, err 246 } 247 u := fmt.Sprintf("projects/%s/pipelines/%d/test_report", pathEscape(project), pipeline) 248 249 req, err := s.client.NewRequest(http.MethodGet, u, nil, nil) 250 if err != nil { 251 return nil, nil, err 252 } 253 254 p := new(PipelineTestReport) 255 resp, err := s.client.Do(req, p) 256 if err != nil { 257 return nil, resp, err 258 } 259 260 return p, resp, err 261} 262 263// CreatePipelineOptions represents the available CreatePipeline() options. 264// 265// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline 266type CreatePipelineOptions struct { 267 Ref *string `url:"ref" json:"ref"` 268 Variables []*PipelineVariable `url:"variables,omitempty" json:"variables,omitempty"` 269} 270 271// CreatePipeline creates a new project pipeline. 272// 273// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline 274func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOptions, options ...RequestOptionFunc) (*Pipeline, *Response, error) { 275 project, err := parseID(pid) 276 if err != nil { 277 return nil, nil, err 278 } 279 u := fmt.Sprintf("projects/%s/pipeline", pathEscape(project)) 280 281 req, err := s.client.NewRequest(http.MethodPost, u, opt, options) 282 if err != nil { 283 return nil, nil, err 284 } 285 286 p := new(Pipeline) 287 resp, err := s.client.Do(req, p) 288 if err != nil { 289 return nil, resp, err 290 } 291 292 return p, resp, err 293} 294 295// RetryPipelineBuild retries failed builds in a pipeline 296// 297// GitLab API docs: 298// https://docs.gitlab.com/ce/api/pipelines.html#retry-failed-builds-in-a-pipeline 299func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) { 300 project, err := parseID(pid) 301 if err != nil { 302 return nil, nil, err 303 } 304 u := fmt.Sprintf("projects/%s/pipelines/%d/retry", pathEscape(project), pipeline) 305 306 req, err := s.client.NewRequest(http.MethodPost, u, nil, options) 307 if err != nil { 308 return nil, nil, err 309 } 310 311 p := new(Pipeline) 312 resp, err := s.client.Do(req, p) 313 if err != nil { 314 return nil, resp, err 315 } 316 317 return p, resp, err 318} 319 320// CancelPipelineBuild cancels a pipeline builds 321// 322// GitLab API docs: 323//https://docs.gitlab.com/ce/api/pipelines.html#cancel-a-pipelines-builds 324func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) { 325 project, err := parseID(pid) 326 if err != nil { 327 return nil, nil, err 328 } 329 u := fmt.Sprintf("projects/%s/pipelines/%d/cancel", pathEscape(project), pipeline) 330 331 req, err := s.client.NewRequest(http.MethodPost, u, nil, options) 332 if err != nil { 333 return nil, nil, err 334 } 335 336 p := new(Pipeline) 337 resp, err := s.client.Do(req, p) 338 if err != nil { 339 return nil, resp, err 340 } 341 342 return p, resp, err 343} 344 345// DeletePipeline deletes an existing pipeline. 346// 347// GitLab API docs: 348// https://docs.gitlab.com/ce/api/pipelines.html#delete-a-pipeline 349func (s *PipelinesService) DeletePipeline(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Response, error) { 350 project, err := parseID(pid) 351 if err != nil { 352 return nil, err 353 } 354 u := fmt.Sprintf("projects/%s/pipelines/%d", pathEscape(project), pipeline) 355 356 req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) 357 if err != nil { 358 return nil, err 359 } 360 361 return s.client.Do(req, nil) 362} 363