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
case params[:state].to_s begin
when 'running' case params[:state].to_s
status.run when 'pending'
when 'success' status.enqueue!
status.success when 'running'
when 'failed' status.enqueue
status.drop status.run!
when 'canceled' when 'success'
status.cancel status.success!
else when 'failed'
status.status = params[:state].to_s status.drop!
end when 'canceled'
status.cancel!
else
render_api_error!('invalid state', 400)
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,17 +117,36 @@ describe API::CommitStatuses, api: true do ...@@ -117,17 +117,36 @@ 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
post api(post_url, developer), state: status
expect(response).to have_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
end
end
end
end
it 'creates commit status' do context 'transitions status from pending' do
expect(response).to have_http_status(201) before do
expect(json_response['sha']).to eq(commit.id) post api(post_url, developer), state: 'pending'
expect(json_response['status']).to eq('success') end
expect(json_response['name']).to eq('default')
expect(json_response['ref']).to be_nil %w[running success failed canceled].each do |status|
expect(json_response['target_url']).to be_nil it "to #{status}" do
expect(json_response['description']).to be_nil 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
end end
......
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