2018-09-29 18:34:47 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-10-06 06:01:16 -04:00
|
|
|
require 'mime/types'
|
|
|
|
|
|
|
|
module API
|
2020-10-14 20:08:42 -04:00
|
|
|
class CommitStatuses < ::API::Base
|
2020-10-29 08:08:50 -04:00
|
|
|
feature_category :continuous_integration
|
|
|
|
|
2017-03-15 14:09:24 -04:00
|
|
|
params do
|
|
|
|
requires :id, type: String, desc: 'The ID of a project'
|
|
|
|
end
|
2018-11-08 07:18:17 -05:00
|
|
|
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
2016-12-04 12:11:19 -05:00
|
|
|
include PaginationParams
|
|
|
|
|
2015-10-06 06:01:16 -04:00
|
|
|
before { authenticate! }
|
|
|
|
|
2016-10-14 04:45:23 -04:00
|
|
|
desc "Get a commit's statuses" do
|
|
|
|
success Entities::CommitStatus
|
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :sha, type: String, desc: 'The commit hash'
|
|
|
|
optional :ref, type: String, desc: 'The ref'
|
|
|
|
optional :stage, type: String, desc: 'The stage'
|
|
|
|
optional :name, type: String, desc: 'The name'
|
|
|
|
optional :all, type: String, desc: 'Show all statuses, default: false'
|
2016-12-04 12:11:19 -05:00
|
|
|
use :pagination
|
2016-10-14 04:45:23 -04:00
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2015-10-06 06:01:16 -04:00
|
|
|
get ':id/repository/commits/:sha/statuses' do
|
2016-02-29 07:54:33 -05:00
|
|
|
authorize!(:read_commit_status, user_project)
|
|
|
|
|
|
|
|
not_found!('Commit') unless user_project.commit(params[:sha])
|
|
|
|
|
2018-12-05 09:39:15 -05:00
|
|
|
pipelines = user_project.ci_pipelines.where(sha: params[:sha])
|
2016-06-03 10:27:50 -04:00
|
|
|
statuses = ::CommitStatus.where(pipeline: pipelines)
|
2016-07-20 02:55:44 -04:00
|
|
|
statuses = statuses.latest unless to_boolean(params[:all])
|
2015-10-06 06:01:16 -04:00
|
|
|
statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
|
2015-10-12 09:45:46 -04:00
|
|
|
statuses = statuses.where(stage: params[:stage]) if params[:stage].present?
|
2015-10-06 06:01:16 -04:00
|
|
|
statuses = statuses.where(name: params[:name]) if params[:name].present?
|
|
|
|
present paginate(statuses), with: Entities::CommitStatus
|
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2015-10-06 06:01:16 -04:00
|
|
|
|
2016-10-14 04:45:23 -04:00
|
|
|
desc 'Post status to a commit' do
|
|
|
|
success Entities::CommitStatus
|
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :sha, type: String, desc: 'The commit hash'
|
|
|
|
requires :state, type: String, desc: 'The state of the status',
|
2017-02-22 12:46:57 -05:00
|
|
|
values: %w(pending running success failed canceled)
|
2016-10-14 04:45:23 -04:00
|
|
|
optional :ref, type: String, desc: 'The ref'
|
|
|
|
optional :target_url, type: String, desc: 'The target URL to associate with this status'
|
|
|
|
optional :description, type: String, desc: 'A short description of the status'
|
|
|
|
optional :name, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"'
|
|
|
|
optional :context, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"'
|
2017-02-13 21:28:45 -05:00
|
|
|
optional :coverage, type: Float, desc: 'The total code coverage'
|
2019-07-16 19:36:49 -04:00
|
|
|
optional :pipeline_id, type: Integer, desc: 'An existing pipeline ID, when multiple pipelines on the same commit SHA have been triggered'
|
2016-10-14 04:45:23 -04:00
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2015-10-06 06:01:16 -04:00
|
|
|
post ':id/statuses/:sha' do
|
2015-10-12 15:35:52 -04:00
|
|
|
authorize! :create_commit_status, user_project
|
2016-10-14 04:45:23 -04:00
|
|
|
|
2015-10-06 06:01:16 -04:00
|
|
|
not_found! 'Commit' unless commit
|
|
|
|
|
2020-06-25 20:09:13 -04:00
|
|
|
# Since the CommitStatus is attached to ::Ci::Pipeline (in the future Pipeline)
|
2016-04-16 16:43:40 -04:00
|
|
|
# We need to always have the pipeline object
|
|
|
|
# To have a valid pipeline object that can be attached to specific MR
|
|
|
|
# Other CI service needs to send `ref`
|
|
|
|
# If we don't receive it, we will attach the CommitStatus to
|
|
|
|
# the first found branch on that commit
|
|
|
|
|
2019-10-18 07:11:44 -04:00
|
|
|
pipeline = all_matching_pipelines.first
|
|
|
|
|
2016-04-16 16:43:40 -04:00
|
|
|
ref = params[:ref]
|
2019-10-18 07:11:44 -04:00
|
|
|
ref ||= pipeline&.ref
|
2020-01-03 10:08:33 -05:00
|
|
|
ref ||= user_project.repository.branch_names_contains(commit.sha).first
|
2016-08-25 11:52:09 -04:00
|
|
|
not_found! 'References for commit' unless ref
|
|
|
|
|
|
|
|
name = params[:name] || params[:context] || 'default'
|
2019-07-16 19:36:49 -04:00
|
|
|
|
2020-01-27 16:08:47 -05:00
|
|
|
pipeline ||= user_project.ci_pipelines.create!(
|
|
|
|
source: :external,
|
|
|
|
sha: commit.sha,
|
|
|
|
ref: ref,
|
|
|
|
user: current_user,
|
|
|
|
protected: user_project.protected_for?(ref))
|
2015-10-06 06:01:16 -04:00
|
|
|
|
2020-01-30 16:08:47 -05:00
|
|
|
authorize! :update_pipeline, pipeline
|
|
|
|
|
2016-08-25 11:52:09 -04:00
|
|
|
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
|
2020-01-03 10:08:33 -05:00
|
|
|
project: user_project,
|
2016-10-20 12:51:03 -04:00
|
|
|
pipeline: pipeline,
|
|
|
|
name: name,
|
|
|
|
ref: ref,
|
2017-09-01 04:54:07 -04:00
|
|
|
user: current_user,
|
2020-01-03 10:08:33 -05:00
|
|
|
protected: user_project.protected_for?(ref)
|
2016-10-20 12:51:03 -04:00
|
|
|
)
|
2015-10-06 06:01:16 -04:00
|
|
|
|
2021-05-19 05:10:19 -04:00
|
|
|
updatable_optional_attributes = %w[target_url description coverage]
|
|
|
|
status.assign_attributes(attributes_for_keys(updatable_optional_attributes))
|
2021-03-09 07:08:52 -05:00
|
|
|
|
2021-07-08 11:10:06 -04:00
|
|
|
render_validation_error!(status) unless status.valid?
|
2017-01-18 06:24:53 -05:00
|
|
|
|
2021-07-08 11:10:06 -04:00
|
|
|
response = ::Ci::Pipelines::AddJobService.new(pipeline).execute!(status) do |job|
|
|
|
|
apply_job_state!(job)
|
|
|
|
rescue ::StateMachines::InvalidTransition => e
|
|
|
|
render_api_error!(e.message, 400)
|
|
|
|
end
|
2015-10-06 06:01:16 -04:00
|
|
|
|
2021-07-08 11:10:06 -04:00
|
|
|
render_validation_error!(response.payload[:job]) unless response.success?
|
2017-06-27 08:08:40 -04:00
|
|
|
|
2021-07-08 11:10:06 -04:00
|
|
|
if pipeline.latest?
|
|
|
|
MergeRequest
|
|
|
|
.where(source_project: user_project, source_branch: ref)
|
|
|
|
.update_all(head_pipeline_id: pipeline.id)
|
2015-10-06 06:01:16 -04:00
|
|
|
end
|
2021-07-08 11:10:06 -04:00
|
|
|
|
|
|
|
present response.payload[:job], with: Entities::CommitStatus
|
2015-10-06 06:01:16 -04:00
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2021-07-08 11:10:06 -04:00
|
|
|
|
2019-10-18 07:11:44 -04:00
|
|
|
helpers do
|
|
|
|
def commit
|
|
|
|
strong_memoize(:commit) do
|
|
|
|
user_project.commit(params[:sha])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def all_matching_pipelines
|
|
|
|
pipelines = user_project.ci_pipelines.newest_first(sha: commit.sha)
|
|
|
|
pipelines = pipelines.for_ref(params[:ref]) if params[:ref]
|
|
|
|
pipelines = pipelines.for_id(params[:pipeline_id]) if params[:pipeline_id]
|
|
|
|
pipelines
|
|
|
|
end
|
2021-07-08 11:10:06 -04:00
|
|
|
|
|
|
|
def apply_job_state!(job)
|
|
|
|
case params[:state]
|
|
|
|
when 'pending'
|
|
|
|
job.enqueue!
|
|
|
|
when 'running'
|
|
|
|
job.enqueue
|
|
|
|
job.run!
|
|
|
|
when 'success'
|
|
|
|
job.success!
|
|
|
|
when 'failed'
|
|
|
|
job.drop!(:api_failure)
|
|
|
|
when 'canceled'
|
|
|
|
job.cancel!
|
|
|
|
else
|
|
|
|
render_api_error!('invalid state', 400)
|
|
|
|
end
|
|
|
|
end
|
2019-10-18 07:11:44 -04:00
|
|
|
end
|
2015-10-06 06:01:16 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|