Commit c87c1cb3 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'api-empty-commit' into 'master'

Improve error messages when file editing fails

Give more specific errors in API responses and web UI flash messages when a file update fails.  See #1479.

Instead of returning false from `Gitlab::Satellite::Files::EditFileAction#commit!` when a `Grit::Git::CommandFailed` error is raised, now `#commit!` raises a different error depending on whether the failure happened during checkout, commit, or push.

@dzaporozhets Please let me know if you want to change the HTTP status codes or the error messages in `Files::UpdateService`

cc @sytse

See merge request !1569
parents 8601c502 5f232b56
v 7.9.0 (unreleased) v 7.9.0 (unreleased)
- Move labels/milestones tabs to sidebar - Move labels/milestones tabs to sidebar
- Upgrade Rails gem to version 4.1.9. - Upgrade Rails gem to version 4.1.9.
- Improve error messages for file edit failures
- Improve UI for commits, issues and merge request lists - Improve UI for commits, issues and merge request lists
- Fix commit comments on first line of diff not rendering in Merge Request Discussion view. - Fix commit comments on first line of diff not rendering in Merge Request Discussion view.
......
...@@ -37,11 +37,14 @@ class BaseService ...@@ -37,11 +37,14 @@ class BaseService
private private
def error(message) def error(message, http_status = nil)
{ result = {
message: message, message: message,
status: :error status: :error
} }
result[:http_status] = http_status if http_status
result
end end
def success def success
......
...@@ -20,17 +20,19 @@ module Files ...@@ -20,17 +20,19 @@ module Files
end end
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path) edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
created_successfully = edit_file_action.commit!( edit_file_action.commit!(
params[:content], params[:content],
params[:commit_message], params[:commit_message],
params[:encoding] params[:encoding]
) )
if created_successfully
success success
else rescue Gitlab::Satellite::CheckoutFailed => ex
error("Your changes could not be committed. Maybe the file was changed by another process or there was nothing to commit?") error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400)
end rescue Gitlab::Satellite::CommitFailed => ex
error("Your changes could not be committed. Maybe there was nothing to commit?", 409)
rescue Gitlab::Satellite::PushFailed => ex
error("Your changes could not be committed. Maybe the file was changed by another process?", 409)
end end
end end
end end
...@@ -117,7 +117,8 @@ module API ...@@ -117,7 +117,8 @@ module API
branch_name: branch_name branch_name: branch_name
} }
else else
render_api_error!(result[:message], 400) http_status = result[:http_status] || 400
render_api_error!(result[:message], http_status)
end end
end end
......
...@@ -15,7 +15,11 @@ module Gitlab ...@@ -15,7 +15,11 @@ module Gitlab
prepare_satellite!(repo) prepare_satellite!(repo)
# create target branch in satellite at the corresponding commit from bare repo # create target branch in satellite at the corresponding commit from bare repo
begin
repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}") repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}")
rescue Grit::Git::CommandFailed => ex
log_and_raise(CheckoutFailed, ex.message)
end
# update the file in the satellite's working dir # update the file in the satellite's working dir
file_path_in_satellite = File.join(repo.working_dir, file_path) file_path_in_satellite = File.join(repo.working_dir, file_path)
...@@ -31,19 +35,31 @@ module Gitlab ...@@ -31,19 +35,31 @@ module Gitlab
# commit the changes # commit the changes
# will raise CommandFailed when commit fails # will raise CommandFailed when commit fails
begin
repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
rescue Grit::Git::CommandFailed => ex
log_and_raise(CommitFailed, ex.message)
end
# push commit back to bare repo # push commit back to bare repo
# will raise CommandFailed when push fails # will raise CommandFailed when push fails
begin
repo.git.push({ raise: true, timeout: true }, :origin, ref) repo.git.push({ raise: true, timeout: true }, :origin, ref)
rescue Grit::Git::CommandFailed => ex
log_and_raise(PushFailed, ex.message)
end
# everything worked # everything worked
true true
end end
rescue Grit::Git::CommandFailed => ex end
Gitlab::GitLogger.error(ex.message)
false private
def log_and_raise(errorClass, message)
Gitlab::GitLogger.error(message)
raise(errorClass, message)
end end
end end
end end
......
module Gitlab module Gitlab
module Satellite module Satellite
class CheckoutFailed < StandardError; end
class CommitFailed < StandardError; end
class PushFailed < StandardError; end
class Satellite class Satellite
include Gitlab::Popen include Gitlab::Popen
......
...@@ -98,13 +98,33 @@ describe API::API, api: true do ...@@ -98,13 +98,33 @@ describe API::API, api: true do
expect(response.status).to eq(400) expect(response.status).to eq(400)
end end
it "should return a 400 if satellite fails to create file" do it 'should return a 400 if the checkout fails' do
Gitlab::Satellite::EditFileAction.any_instance.stub( Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!)
commit!: false, .and_raise(Gitlab::Satellite::CheckoutFailed)
)
put api("/projects/#{project.id}/repository/files", user), valid_params put api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(400) expect(response.status).to eq(400)
ref = valid_params[:branch_name]
expect(response.body).to match("ref '#{ref}' could not be checked out")
end
it 'should return a 409 if the file was not modified' do
Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!)
.and_raise(Gitlab::Satellite::CommitFailed)
put api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(409)
expect(response.body).to match("Maybe there was nothing to commit?")
end
it 'should return a 409 if the push fails' do
Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!)
.and_raise(Gitlab::Satellite::PushFailed)
put api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(409)
expect(response.body).to match("Maybe the file was changed by another process?")
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