Commit f7be904a authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'fix-commit-status' into 'master'

Fix an error where we were unable to create a CommitStatus for running state

Due to severe refactoring of Pipeline we introduced regression in how CommitStatus is handled. We received an report that it's impossible to create a CommitStatus with state `running` when there were not previous status. 

The support for Commit Statuses should be simplified. Right now I'm doing minimal change to move forward and fix a bug, but I'll create a new MR that will move all logic that is now part of `lib/api/commit_statuses.rb` to separate service to simplify the implementation.

This error happens due to the fact that we introduced additional status of builds: `created`.

Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/21345

See merge request !6107
parents 60dc0d16 46b83f06
...@@ -127,6 +127,7 @@ v 8.12.0 (unreleased) ...@@ -127,6 +127,7 @@ v 8.12.0 (unreleased)
- Remove duplication between project builds and admin builds view !5680 (Katarzyna Kobierska Ula Budziszewska) - Remove duplication between project builds and admin builds view !5680 (Katarzyna Kobierska Ula Budziszewska)
v 8.11.6 (unreleased) v 8.11.6 (unreleased)
- Fix an error where we were unable to create a CommitStatus for running state
v 8.11.5 v 8.11.5
- Optimize branch lookups and force a repository reload for Repository#find_branch. !6087 - Optimize branch lookups and force a repository reload for Repository#find_branch. !6087
......
...@@ -37,7 +37,7 @@ module API ...@@ -37,7 +37,7 @@ module API
# id (required) - The ID of a project # id (required) - The ID of a project
# sha (required) - The commit hash # sha (required) - The commit hash
# ref (optional) - The ref # ref (optional) - The ref
# state (required) - The state of the status. Can be: pending, running, success, error or failure # state (required) - The state of the status. Can be: pending, running, success, failed or canceled
# target_url (optional) - The target URL to associate with this status # target_url (optional) - The target URL to associate with this status
# description (optional) - A short description of the status # description (optional) - A short description of the status
# name or context (optional) - A string label to differentiate this status from the status of other systems. Default: "default" # name or context (optional) - A string label to differentiate this status from the status of other systems. Default: "default"
...@@ -46,7 +46,7 @@ module API ...@@ -46,7 +46,7 @@ module API
post ':id/statuses/:sha' do post ':id/statuses/:sha' do
authorize! :create_commit_status, user_project authorize! :create_commit_status, user_project
required_attributes! [:state] required_attributes! [:state]
attrs = attributes_for_keys [:ref, :target_url, :description, :context, :name] attrs = attributes_for_keys [:target_url, :description]
commit = @project.commit(params[:sha]) commit = @project.commit(params[:sha])
not_found! 'Commit' unless commit not_found! 'Commit' unless commit
...@@ -58,36 +58,38 @@ module API ...@@ -58,36 +58,38 @@ module API
# the first found branch on that commit # the first found branch on that commit
ref = params[:ref] ref = params[:ref]
unless ref ref ||= @project.repository.branch_names_contains(commit.sha).first
branches = @project.repository.branch_names_contains(commit.sha) not_found! 'References for commit' unless ref
not_found! 'References for commit' if branches.none?
ref = branches.first name = params[:name] || params[:context] || 'default'
end
pipeline = @project.ensure_pipeline(ref, commit.sha, current_user) pipeline = @project.ensure_pipeline(ref, commit.sha, current_user)
name = params[:name] || params[:context] status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref]) project: @project, pipeline: pipeline,
status ||= GenericCommitStatus.new(project: @project, pipeline: pipeline, user: current_user) user: current_user, name: name, ref: ref)
status.update(attrs) status.attributes = attrs
begin
case params[:state].to_s case params[:state].to_s
when 'pending'
status.enqueue!
when 'running' when 'running'
status.run status.enqueue
status.run!
when 'success' when 'success'
status.success status.success!
when 'failed' when 'failed'
status.drop status.drop!
when 'canceled' when 'canceled'
status.cancel status.cancel!
else else
status.status = params[:state].to_s render_api_error!('invalid state', 400)
end end
if status.save
present status, with: Entities::CommitStatus present status, with: Entities::CommitStatus
else rescue StateMachines::InvalidTransition => e
render_validation_error!(status) render_api_error!(e.message, 400)
end end
end end
end end
......
...@@ -117,19 +117,38 @@ describe API::CommitStatuses, api: true do ...@@ -117,19 +117,38 @@ describe API::CommitStatuses, api: true do
let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" } let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" }
context 'developer user' do context 'developer user' do
context 'only required parameters' do %w[pending running success failed canceled].each do |status|
before { post api(post_url, developer), state: 'success' } context "for #{status}" do
context 'uses only required parameters' do
it 'creates commit status' do it 'creates commit status' do
post api(post_url, developer), state: status
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
expect(json_response['sha']).to eq(commit.id) expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success') expect(json_response['status']).to eq(status)
expect(json_response['name']).to eq('default') expect(json_response['name']).to eq('default')
expect(json_response['ref']).to be_nil expect(json_response['ref']).not_to be_empty
expect(json_response['target_url']).to be_nil expect(json_response['target_url']).to be_nil
expect(json_response['description']).to be_nil expect(json_response['description']).to be_nil
end end
end end
end
end
context 'transitions status from pending' do
before do
post api(post_url, developer), state: 'pending'
end
%w[running success failed canceled].each do |status|
it "to #{status}" do
expect { post api(post_url, developer), state: status }.not_to change { CommitStatus.count }
expect(response).to have_http_status(201)
expect(json_response['status']).to eq(status)
end
end
end
context 'with all optional parameters' do context 'with all optional parameters' do
before do before do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment