...

Source file src/github.com/xanzy/go-gitlab/jobs.go

Documentation: github.com/xanzy/go-gitlab

     1  //
     2  // Copyright 2021, Arkbriar
     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  
    17  package gitlab
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"net/http"
    23  	"time"
    24  )
    25  
    26  // JobsService handles communication with the ci builds related methods
    27  // of the GitLab API.
    28  //
    29  // GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html
    30  type JobsService struct {
    31  	client *Client
    32  }
    33  
    34  // Job represents a ci build.
    35  //
    36  // GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html
    37  type Job struct {
    38  	Commit            *Commit    `json:"commit"`
    39  	Coverage          float64    `json:"coverage"`
    40  	AllowFailure      bool       `json:"allow_failure"`
    41  	CreatedAt         *time.Time `json:"created_at"`
    42  	StartedAt         *time.Time `json:"started_at"`
    43  	FinishedAt        *time.Time `json:"finished_at"`
    44  	ErasedAt          *time.Time `json:"erased_at"`
    45  	Duration          float64    `json:"duration"`
    46  	QueuedDuration    float64    `json:"queued_duration"`
    47  	ArtifactsExpireAt *time.Time `json:"artifacts_expire_at"`
    48  	TagList           []string   `json:"tag_list"`
    49  	ID                int        `json:"id"`
    50  	Name              string     `json:"name"`
    51  	Pipeline          struct {
    52  		ID        int    `json:"id"`
    53  		ProjectID int    `json:"project_id"`
    54  		Ref       string `json:"ref"`
    55  		Sha       string `json:"sha"`
    56  		Status    string `json:"status"`
    57  	} `json:"pipeline"`
    58  	Ref       string `json:"ref"`
    59  	Artifacts []struct {
    60  		FileType   string `json:"file_type"`
    61  		Filename   string `json:"filename"`
    62  		Size       int    `json:"size"`
    63  		FileFormat string `json:"file_format"`
    64  	} `json:"artifacts"`
    65  	ArtifactsFile struct {
    66  		Filename string `json:"filename"`
    67  		Size     int    `json:"size"`
    68  	} `json:"artifacts_file"`
    69  	Runner struct {
    70  		ID          int    `json:"id"`
    71  		Description string `json:"description"`
    72  		Active      bool   `json:"active"`
    73  		IsShared    bool   `json:"is_shared"`
    74  		Name        string `json:"name"`
    75  	} `json:"runner"`
    76  	Stage         string   `json:"stage"`
    77  	Status        string   `json:"status"`
    78  	FailureReason string   `json:"failure_reason"`
    79  	Tag           bool     `json:"tag"`
    80  	WebURL        string   `json:"web_url"`
    81  	Project       *Project `json:"project"`
    82  	User          *User    `json:"user"`
    83  }
    84  
    85  // Bridge represents a pipeline bridge.
    86  //
    87  // GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html#list-pipeline-bridges
    88  type Bridge struct {
    89  	Commit             *Commit       `json:"commit"`
    90  	Coverage           float64       `json:"coverage"`
    91  	AllowFailure       bool          `json:"allow_failure"`
    92  	CreatedAt          *time.Time    `json:"created_at"`
    93  	StartedAt          *time.Time    `json:"started_at"`
    94  	FinishedAt         *time.Time    `json:"finished_at"`
    95  	ErasedAt           *time.Time    `json:"erased_at"`
    96  	Duration           float64       `json:"duration"`
    97  	QueuedDuration     float64       `json:"queued_duration"`
    98  	ID                 int           `json:"id"`
    99  	Name               string        `json:"name"`
   100  	Pipeline           PipelineInfo  `json:"pipeline"`
   101  	Ref                string        `json:"ref"`
   102  	Stage              string        `json:"stage"`
   103  	Status             string        `json:"status"`
   104  	FailureReason      string        `json:"failure_reason"`
   105  	Tag                bool          `json:"tag"`
   106  	WebURL             string        `json:"web_url"`
   107  	User               *User         `json:"user"`
   108  	DownstreamPipeline *PipelineInfo `json:"downstream_pipeline"`
   109  }
   110  
   111  // ListJobsOptions represents the available ListProjectJobs() options.
   112  //
   113  // GitLab API docs:
   114  // https://docs.gitlab.com/ee/api/jobs.html#list-project-jobs
   115  type ListJobsOptions struct {
   116  	ListOptions
   117  	Scope          *[]BuildStateValue `url:"scope[],omitempty" json:"scope,omitempty"`
   118  	IncludeRetried *bool              `url:"include_retried,omitempty" json:"include_retried,omitempty"`
   119  }
   120  
   121  // ListProjectJobs gets a list of jobs in a project.
   122  //
   123  // The scope of jobs to show, one or array of: created, pending, running,
   124  // failed, success, canceled, skipped; showing all jobs if none provided
   125  //
   126  // GitLab API docs:
   127  // https://docs.gitlab.com/ee/api/jobs.html#list-project-jobs
   128  func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...RequestOptionFunc) ([]*Job, *Response, error) {
   129  	project, err := parseID(pid)
   130  	if err != nil {
   131  		return nil, nil, err
   132  	}
   133  	u := fmt.Sprintf("projects/%s/jobs", PathEscape(project))
   134  
   135  	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
   136  	if err != nil {
   137  		return nil, nil, err
   138  	}
   139  
   140  	var jobs []*Job
   141  	resp, err := s.client.Do(req, &jobs)
   142  	if err != nil {
   143  		return nil, resp, err
   144  	}
   145  
   146  	return jobs, resp, nil
   147  }
   148  
   149  // ListPipelineJobs gets a list of jobs for specific pipeline in a
   150  // project. If the pipeline ID is not found, it will respond with 404.
   151  //
   152  // GitLab API docs:
   153  // https://docs.gitlab.com/ee/api/jobs.html#list-pipeline-jobs
   154  func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...RequestOptionFunc) ([]*Job, *Response, error) {
   155  	project, err := parseID(pid)
   156  	if err != nil {
   157  		return nil, nil, err
   158  	}
   159  	u := fmt.Sprintf("projects/%s/pipelines/%d/jobs", PathEscape(project), pipelineID)
   160  
   161  	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
   162  	if err != nil {
   163  		return nil, nil, err
   164  	}
   165  
   166  	var jobs []*Job
   167  	resp, err := s.client.Do(req, &jobs)
   168  	if err != nil {
   169  		return nil, resp, err
   170  	}
   171  
   172  	return jobs, resp, nil
   173  }
   174  
   175  // ListPipelineBridges gets a list of bridges for specific pipeline in a
   176  // project.
   177  //
   178  // GitLab API docs:
   179  // https://docs.gitlab.com/ee/api/jobs.html#list-pipeline-jobs
   180  func (s *JobsService) ListPipelineBridges(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...RequestOptionFunc) ([]*Bridge, *Response, error) {
   181  	project, err := parseID(pid)
   182  	if err != nil {
   183  		return nil, nil, err
   184  	}
   185  	u := fmt.Sprintf("projects/%s/pipelines/%d/bridges", PathEscape(project), pipelineID)
   186  
   187  	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
   188  	if err != nil {
   189  		return nil, nil, err
   190  	}
   191  
   192  	var bridges []*Bridge
   193  	resp, err := s.client.Do(req, &bridges)
   194  	if err != nil {
   195  		return nil, resp, err
   196  	}
   197  
   198  	return bridges, resp, nil
   199  }
   200  
   201  // GetJobTokensJobOptions represents the available GetJobTokensJob() options.
   202  //
   203  // GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html#get-job-tokens-job
   204  type GetJobTokensJobOptions struct {
   205  	JobToken *string `url:"job_token,omitempty" json:"job_token,omitempty"`
   206  }
   207  
   208  // GetJobTokensJob retrieves the job that generated a job token.
   209  //
   210  // GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html#get-job-tokens-job
   211  func (s *JobsService) GetJobTokensJob(opts *GetJobTokensJobOptions, options ...RequestOptionFunc) (*Job, *Response, error) {
   212  	req, err := s.client.NewRequest(http.MethodGet, "job", opts, options)
   213  	if err != nil {
   214  		return nil, nil, err
   215  	}
   216  
   217  	job := new(Job)
   218  	resp, err := s.client.Do(req, job)
   219  	if err != nil {
   220  		return nil, resp, err
   221  	}
   222  
   223  	return job, resp, nil
   224  }
   225  
   226  // GetJob gets a single job of a project.
   227  //
   228  // GitLab API docs:
   229  // https://docs.gitlab.com/ee/api/jobs.html#get-a-single-job
   230  func (s *JobsService) GetJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
   231  	project, err := parseID(pid)
   232  	if err != nil {
   233  		return nil, nil, err
   234  	}
   235  	u := fmt.Sprintf("projects/%s/jobs/%d", PathEscape(project), jobID)
   236  
   237  	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
   238  	if err != nil {
   239  		return nil, nil, err
   240  	}
   241  
   242  	job := new(Job)
   243  	resp, err := s.client.Do(req, job)
   244  	if err != nil {
   245  		return nil, resp, err
   246  	}
   247  
   248  	return job, resp, nil
   249  }
   250  
   251  // GetJobArtifacts get jobs artifacts of a project
   252  //
   253  // GitLab API docs:
   254  // https://docs.gitlab.com/ee/api/job_artifacts.html#get-job-artifacts
   255  func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
   256  	project, err := parseID(pid)
   257  	if err != nil {
   258  		return nil, nil, err
   259  	}
   260  	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts", PathEscape(project), jobID)
   261  
   262  	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
   263  	if err != nil {
   264  		return nil, nil, err
   265  	}
   266  
   267  	artifactsBuf := new(bytes.Buffer)
   268  	resp, err := s.client.Do(req, artifactsBuf)
   269  	if err != nil {
   270  		return nil, resp, err
   271  	}
   272  
   273  	return bytes.NewReader(artifactsBuf.Bytes()), resp, err
   274  }
   275  
   276  // DownloadArtifactsFileOptions represents the available DownloadArtifactsFile()
   277  // options.
   278  //
   279  // GitLab API docs:
   280  // https://docs.gitlab.com/ee/api/job_artifacts.html#download-the-artifacts-archive
   281  type DownloadArtifactsFileOptions struct {
   282  	Job *string `url:"job" json:"job"`
   283  }
   284  
   285  // DownloadArtifactsFile download the artifacts file from the given
   286  // reference name and job provided the job finished successfully.
   287  //
   288  // GitLab API docs:
   289  // https://docs.gitlab.com/ee/api/job_artifacts.html#download-the-artifacts-archive
   290  func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, opt *DownloadArtifactsFileOptions, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
   291  	project, err := parseID(pid)
   292  	if err != nil {
   293  		return nil, nil, err
   294  	}
   295  	u := fmt.Sprintf("projects/%s/jobs/artifacts/%s/download", PathEscape(project), refName)
   296  
   297  	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
   298  	if err != nil {
   299  		return nil, nil, err
   300  	}
   301  
   302  	artifactsBuf := new(bytes.Buffer)
   303  	resp, err := s.client.Do(req, artifactsBuf)
   304  	if err != nil {
   305  		return nil, resp, err
   306  	}
   307  
   308  	return bytes.NewReader(artifactsBuf.Bytes()), resp, err
   309  }
   310  
   311  // DownloadSingleArtifactsFile download a file from the artifacts from the
   312  // given reference name and job provided the job finished successfully.
   313  // Only a single file is going to be extracted from the archive and streamed
   314  // to a client.
   315  //
   316  // GitLab API docs:
   317  // https://docs.gitlab.com/ee/api/job_artifacts.html#download-a-single-artifact-file-by-job-id
   318  func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, artifactPath string, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
   319  	project, err := parseID(pid)
   320  	if err != nil {
   321  		return nil, nil, err
   322  	}
   323  
   324  	u := fmt.Sprintf(
   325  		"projects/%s/jobs/%d/artifacts/%s",
   326  		PathEscape(project),
   327  		jobID,
   328  		artifactPath,
   329  	)
   330  
   331  	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
   332  	if err != nil {
   333  		return nil, nil, err
   334  	}
   335  
   336  	artifactBuf := new(bytes.Buffer)
   337  	resp, err := s.client.Do(req, artifactBuf)
   338  	if err != nil {
   339  		return nil, resp, err
   340  	}
   341  
   342  	return bytes.NewReader(artifactBuf.Bytes()), resp, err
   343  }
   344  
   345  // DownloadSingleArtifactsFile download a single artifact file for a specific
   346  // job of the latest successful pipeline for the given reference name from
   347  // inside the job’s artifacts archive. The file is extracted from the archive
   348  // and streamed to the client.
   349  //
   350  // GitLab API docs:
   351  // https://docs.gitlab.com/ee/api/job_artifacts.html#download-a-single-artifact-file-from-specific-tag-or-branch
   352  func (s *JobsService) DownloadSingleArtifactsFileByTagOrBranch(pid interface{}, refName string, artifactPath string, opt *DownloadArtifactsFileOptions, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
   353  	project, err := parseID(pid)
   354  	if err != nil {
   355  		return nil, nil, err
   356  	}
   357  
   358  	u := fmt.Sprintf(
   359  		"projects/%s/jobs/artifacts/%s/raw/%s",
   360  		PathEscape(project),
   361  		PathEscape(refName),
   362  		artifactPath,
   363  	)
   364  
   365  	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
   366  	if err != nil {
   367  		return nil, nil, err
   368  	}
   369  
   370  	artifactBuf := new(bytes.Buffer)
   371  	resp, err := s.client.Do(req, artifactBuf)
   372  	if err != nil {
   373  		return nil, resp, err
   374  	}
   375  
   376  	return bytes.NewReader(artifactBuf.Bytes()), resp, err
   377  }
   378  
   379  // GetTraceFile gets a trace of a specific job of a project
   380  //
   381  // GitLab API docs:
   382  // https://docs.gitlab.com/ee/api/jobs.html#get-a-log-file
   383  func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
   384  	project, err := parseID(pid)
   385  	if err != nil {
   386  		return nil, nil, err
   387  	}
   388  	u := fmt.Sprintf("projects/%s/jobs/%d/trace", PathEscape(project), jobID)
   389  
   390  	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
   391  	if err != nil {
   392  		return nil, nil, err
   393  	}
   394  
   395  	traceBuf := new(bytes.Buffer)
   396  	resp, err := s.client.Do(req, traceBuf)
   397  	if err != nil {
   398  		return nil, resp, err
   399  	}
   400  
   401  	return bytes.NewReader(traceBuf.Bytes()), resp, err
   402  }
   403  
   404  // CancelJob cancels a single job of a project.
   405  //
   406  // GitLab API docs:
   407  // https://docs.gitlab.com/ee/api/jobs.html#cancel-a-job
   408  func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
   409  	project, err := parseID(pid)
   410  	if err != nil {
   411  		return nil, nil, err
   412  	}
   413  	u := fmt.Sprintf("projects/%s/jobs/%d/cancel", PathEscape(project), jobID)
   414  
   415  	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
   416  	if err != nil {
   417  		return nil, nil, err
   418  	}
   419  
   420  	job := new(Job)
   421  	resp, err := s.client.Do(req, job)
   422  	if err != nil {
   423  		return nil, resp, err
   424  	}
   425  
   426  	return job, resp, nil
   427  }
   428  
   429  // RetryJob retries a single job of a project
   430  //
   431  // GitLab API docs:
   432  // https://docs.gitlab.com/ee/api/jobs.html#retry-a-job
   433  func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
   434  	project, err := parseID(pid)
   435  	if err != nil {
   436  		return nil, nil, err
   437  	}
   438  	u := fmt.Sprintf("projects/%s/jobs/%d/retry", PathEscape(project), jobID)
   439  
   440  	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
   441  	if err != nil {
   442  		return nil, nil, err
   443  	}
   444  
   445  	job := new(Job)
   446  	resp, err := s.client.Do(req, job)
   447  	if err != nil {
   448  		return nil, resp, err
   449  	}
   450  
   451  	return job, resp, nil
   452  }
   453  
   454  // EraseJob erases a single job of a project, removes a job
   455  // artifacts and a job trace.
   456  //
   457  // GitLab API docs:
   458  // https://docs.gitlab.com/ee/api/jobs.html#erase-a-job
   459  func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
   460  	project, err := parseID(pid)
   461  	if err != nil {
   462  		return nil, nil, err
   463  	}
   464  	u := fmt.Sprintf("projects/%s/jobs/%d/erase", PathEscape(project), jobID)
   465  
   466  	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
   467  	if err != nil {
   468  		return nil, nil, err
   469  	}
   470  
   471  	job := new(Job)
   472  	resp, err := s.client.Do(req, job)
   473  	if err != nil {
   474  		return nil, resp, err
   475  	}
   476  
   477  	return job, resp, nil
   478  }
   479  
   480  // KeepArtifacts prevents artifacts from being deleted when
   481  // expiration is set.
   482  //
   483  // GitLab API docs:
   484  // https://docs.gitlab.com/ee/api/job_artifacts.html#keep-artifacts
   485  func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
   486  	project, err := parseID(pid)
   487  	if err != nil {
   488  		return nil, nil, err
   489  	}
   490  	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts/keep", PathEscape(project), jobID)
   491  
   492  	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
   493  	if err != nil {
   494  		return nil, nil, err
   495  	}
   496  
   497  	job := new(Job)
   498  	resp, err := s.client.Do(req, job)
   499  	if err != nil {
   500  		return nil, resp, err
   501  	}
   502  
   503  	return job, resp, nil
   504  }
   505  
   506  // PlayJobOptions represents the available PlayJob() options.
   507  //
   508  // GitLab API docs:
   509  // https://docs.gitlab.com/ee/api/jobs.html#run-a-job
   510  type PlayJobOptions struct {
   511  	JobVariablesAttributes *[]*JobVariableOptions `url:"job_variables_attributes,omitempty" json:"job_variables_attributes,omitempty"`
   512  }
   513  
   514  // JobVariableOptions represents a single job variable.
   515  //
   516  // GitLab API docs:
   517  // https://docs.gitlab.com/ee/api/jobs.html#run-a-job
   518  type JobVariableOptions struct {
   519  	Key          *string `url:"key,omitempty" json:"key,omitempty"`
   520  	Value        *string `url:"value,omitempty" json:"value,omitempty"`
   521  	VariableType *string `url:"variable_type,omitempty" json:"variable_type,omitempty"`
   522  }
   523  
   524  // PlayJob triggers a manual action to start a job.
   525  //
   526  // GitLab API docs:
   527  // https://docs.gitlab.com/ee/api/jobs.html#run-a-job
   528  func (s *JobsService) PlayJob(pid interface{}, jobID int, opt *PlayJobOptions, options ...RequestOptionFunc) (*Job, *Response, error) {
   529  	project, err := parseID(pid)
   530  	if err != nil {
   531  		return nil, nil, err
   532  	}
   533  	u := fmt.Sprintf("projects/%s/jobs/%d/play", PathEscape(project), jobID)
   534  
   535  	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
   536  	if err != nil {
   537  		return nil, nil, err
   538  	}
   539  
   540  	job := new(Job)
   541  	resp, err := s.client.Do(req, job)
   542  	if err != nil {
   543  		return nil, resp, err
   544  	}
   545  
   546  	return job, resp, nil
   547  }
   548  
   549  // DeleteArtifacts delete artifacts of a job
   550  //
   551  // GitLab API docs:
   552  // https://docs.gitlab.com/ee/api/job_artifacts.html#delete-job-artifacts
   553  func (s *JobsService) DeleteArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (*Response, error) {
   554  	project, err := parseID(pid)
   555  	if err != nil {
   556  		return nil, err
   557  	}
   558  	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts", PathEscape(project), jobID)
   559  
   560  	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
   561  	if err != nil {
   562  		return nil, err
   563  	}
   564  
   565  	return s.client.Do(req, nil)
   566  }
   567  
   568  // DeleteProjectArtifacts delete artifacts eligible for deletion in a project
   569  //
   570  // GitLab API docs:
   571  // https://docs.gitlab.com/ee/api/job_artifacts.html#delete-project-artifacts
   572  func (s *JobsService) DeleteProjectArtifacts(pid interface{}, options ...RequestOptionFunc) (*Response, error) {
   573  	project, err := parseID(pid)
   574  	if err != nil {
   575  		return nil, err
   576  	}
   577  	u := fmt.Sprintf("projects/%s/artifacts", PathEscape(project))
   578  
   579  	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
   580  	if err != nil {
   581  		return nil, err
   582  	}
   583  
   584  	return s.client.Do(req, nil)
   585  }
   586  

View as plain text