diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index b34fd3f1ec96c5626697fe997bffef51d397cd72..85bb662579bc9eb455b846d247f5eda50aa76213 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -211,6 +211,8 @@ class Pipeline < ApplicationRecord scope :for_sha, -> (sha) { where(sha: sha) } scope :for_source_sha, -> (source_sha) { where(source_sha: source_sha) } scope :for_sha_or_source_sha, -> (sha) { for_sha(sha).or(for_source_sha(sha)) } + scope :for_ref, -> (ref) { where(ref: ref) } + scope :for_id, -> (id) { where(id: id) } scope :created_after, -> (time) { where('ci_pipelines.created_at > ?', time) } scope :triggered_by_merge_request, -> (merge_request) do diff --git a/changelogs/unreleased/14064-commit-status-on-any-pipelines.yml b/changelogs/unreleased/14064-commit-status-on-any-pipelines.yml new file mode 100644 index 0000000000000000000000000000000000000000..db55148cf656aacdfa857e26e12fe9adfb7a5f1d --- /dev/null +++ b/changelogs/unreleased/14064-commit-status-on-any-pipelines.yml @@ -0,0 +1,5 @@ +--- +title: Make commit status created for any pipelines +merge_request: 17524 +author: Aufar Gilbran +type: changed diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index d58a5e214ed09998235b7d5d8ee462102b4982b0..d108c811f4b41a1ea1dd38634d612951e3b975fe 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -58,7 +58,6 @@ class CommitStatuses < Grape::API post ':id/statuses/:sha' do authorize! :create_commit_status, user_project - commit = @project.commit(params[:sha]) not_found! 'Commit' unless commit # Since the CommitStatus is attached to Ci::Pipeline (in the future Pipeline) @@ -68,14 +67,15 @@ class CommitStatuses < Grape::API # If we don't receive it, we will attach the CommitStatus to # the first found branch on that commit + pipeline = all_matching_pipelines.first + ref = params[:ref] + ref ||= pipeline&.ref ref ||= @project.repository.branch_names_contains(commit.sha).first not_found! 'References for commit' unless ref name = params[:name] || params[:context] || 'default' - pipeline = @project.pipeline_for(ref, commit.sha, params[:pipeline_id]) - unless pipeline pipeline = @project.ci_pipelines.create!( source: :external, @@ -126,6 +126,20 @@ class CommitStatuses < Grape::API end end # rubocop: enable CodeReuse/ActiveRecord + 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 + end end end end diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index 1be8883bd3c05f5025266b16502e6d74294cce25..6cb02ba2f6ba2d838f55b5dd9be5b24b08976672 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -125,25 +125,55 @@ def create_status(commit, opts = {}) let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" } context 'developer user' do - %w[pending running success failed canceled].each do |status| - context "for #{status}" do - context 'uses only required parameters' do - it 'creates commit status' do - post api(post_url, developer), params: { state: status } + context 'uses only required parameters' do + %w[pending running success failed canceled].each do |status| + context "for #{status}" do + context 'when pipeline for sha does not exists' do + it 'creates commit status' do + post api(post_url, developer), params: { state: status } + + expect(response).to have_gitlab_http_status(201) + expect(json_response['sha']).to eq(commit.id) + expect(json_response['status']).to eq(status) + expect(json_response['name']).to eq('default') + expect(json_response['ref']).not_to be_empty + expect(json_response['target_url']).to be_nil + expect(json_response['description']).to be_nil + + if status == 'failed' + expect(CommitStatus.find(json_response['id'])).to be_api_failure + end + end + end + end + end + + context 'when pipeline already exists for the specified sha' do + let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') } + let(:params) { { state: 'pending' } } + + shared_examples_for 'creates a commit status for the existing pipeline' do + it do + expect do + post api(post_url, developer), params: params + end.not_to change { Ci::Pipeline.count } + + job = pipeline.statuses.find_by_name(json_response['name']) expect(response).to have_gitlab_http_status(201) - expect(json_response['sha']).to eq(commit.id) - expect(json_response['status']).to eq(status) - expect(json_response['name']).to eq('default') - expect(json_response['ref']).not_to be_empty - expect(json_response['target_url']).to be_nil - expect(json_response['description']).to be_nil - - if status == 'failed' - expect(CommitStatus.find(json_response['id'])).to be_api_failure - end + expect(job.status).to eq('pending') end end + + it_behaves_like 'creates a commit status for the existing pipeline' + + context 'with pipeline for merge request' do + let!(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline, source_project: project) } + let!(:pipeline) { merge_request.all_pipelines.last } + let(:sha) { pipeline.sha } + + it_behaves_like 'creates a commit status for the existing pipeline' + end end end