diff --git a/app/models/repository.rb b/app/models/repository.rb index 32b3541907f307c5cfea434d18a7091753a3c014..99b6cd3cad1d8dcb87555a7614a78effb502ffb8 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1,4 +1,9 @@ +require 'securerandom' + class Repository + class PreReceiveError < StandardError; end + class CommitError < StandardError; end + include Gitlab::ShellAdapter attr_accessor :raw_repository, :path_with_namespace, :project @@ -364,43 +369,47 @@ class Repository @root_ref ||= raw_repository.root_ref end - def commit_file(user, path, content, message, ref) - path[0] = '' if path[0] == '/' + def commit_file(user, path, content, message, branch) + commit_with_hooks(user, branch) do |ref| + path[0] = '' if path[0] == '/' - committer = user_to_comitter(user) - options = {} - options[:committer] = committer - options[:author] = committer - options[:commit] = { - message: message, - branch: ref, - } + committer = user_to_comitter(user) + options = {} + options[:committer] = committer + options[:author] = committer + options[:commit] = { + message: message, + branch: ref, + } - options[:file] = { - content: content, - path: path - } + options[:file] = { + content: content, + path: path + } - Gitlab::Git::Blob.commit(raw_repository, options) + Gitlab::Git::Blob.commit(raw_repository, options) + end end - def remove_file(user, path, message, ref) - path[0] = '' if path[0] == '/' + def remove_file(user, path, message, branch) + commit_with_hooks(user, branch) do |branch| + path[0] = '' if path[0] == '/' - committer = user_to_comitter(user) - options = {} - options[:committer] = committer - options[:author] = committer - options[:commit] = { - message: message, - branch: ref - } + committer = user_to_comitter(user) + options = {} + options[:committer] = committer + options[:author] = committer + options[:commit] = { + message: message, + branch: ref + } - options[:file] = { - path: path - } + options[:file] = { + path: path + } - Gitlab::Git::Blob.remove(raw_repository, options) + Gitlab::Git::Blob.remove(raw_repository, options) + end end def user_to_comitter(user) @@ -479,6 +488,58 @@ class Repository Gitlab::Popen.popen(args, path_to_repo) end + def commit_with_hooks(current_user, branch) + oldrev = Gitlab::Git::BLANK_SHA + ref = Gitlab::Git::BRANCH_REF_PREFIX + branch + gl_id = Gitlab::ShellEnv.gl_id(current_user) + + # Create temporary ref + random_string = SecureRandom.hex + tmp_ref = "refs/tmp/#{random_string}/head" + + unless empty? + oldrev = find_branch(branch).target + rugged.references.create(tmp_ref, oldrev) + end + + # Make commit in tmp ref + newrev = yield(tmp_ref) + + unless newrev + raise CommitError.new('Failed to create commit') + end + + # Run GitLab pre-receive hook + pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo) + status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref) + + if status + if empty? + # Create branch + rugged.references.create(ref, newrev) + else + # Update head + current_head = find_branch(branch).target + + # Make sure target branch was not changed during pre-receive hook + if current_head == oldrev + rugged.references.update(ref, newrev) + else + raise CommitError.new('Commit was rejected because branch received new push') + end + end + + # Run GitLab post receive hook + post_receive_hook = Gitlab::Git::Hook.new('post-receive', path_to_repo) + status = post_receive_hook.trigger(gl_id, oldrev, newrev, ref) + else + # Remove tmp ref and return error to user + rugged.references.delete(tmp_ref) + + raise PreReceiveError.new('Commit was rejected by pre-reveive hook') + end + end + private def cache diff --git a/app/services/commit_service.rb b/app/services/commit_service.rb index 5000e73b4736ef7463ae62bcda6814575e07a4d6..c77da061a9c4fcf50e85a147faf357eaeca69446 100644 --- a/app/services/commit_service.rb +++ b/app/services/commit_service.rb @@ -1,61 +1,5 @@ -require 'securerandom' class CommitService - class PreReceiveError < StandardError; end - class CommitError < StandardError; end - def self.transaction(project, current_user, branch) - repository = project.repository - path_to_repo = repository.path_to_repo - empty_repo = repository.empty? - oldrev = Gitlab::Git::BLANK_SHA - ref = Gitlab::Git::BRANCH_REF_PREFIX + branch - gl_id = Gitlab::ShellEnv.gl_id(current_user) - - # Create temporary ref - random_string = SecureRandom.hex - tmp_ref = "refs/tmp/#{random_string}/head" - - unless empty_repo - oldrev = repository.find_branch(branch).target - repository.rugged.references.create(tmp_ref, oldrev) - end - - # Make commit in tmp ref - newrev = yield(tmp_ref) - - unless newrev - raise CommitError.new('Failed to create commit') - end - - # Run GitLab pre-receive hook - pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo) - status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref) - - if status - if empty_repo - # Create branch - repository.rugged.references.create(ref, newrev) - else - # Update head - current_head = repository.find_branch(branch).target - - # Make sure target branch was not changed during pre-receive hook - if current_head == oldrev - repository.rugged.references.update(ref, newrev) - else - raise CommitError.new('Commit was rejected because branch received new push') - end - end - - # Run GitLab post receive hook - post_receive_hook = Gitlab::Git::Hook.new('post-receive', path_to_repo) - status = post_receive_hook.trigger(gl_id, oldrev, newrev, ref) - else - # Remove tmp ref and return error to user - repository.rugged.references.delete(tmp_ref) - - raise PreReceiveError.new('Commit was rejected by pre-reveive hook') - end end end diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index 507e21f58182824c0c34d75874b2c2631918c5c3..7aecee217d856f12c967a6826e42bf063a250cd5 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -26,7 +26,7 @@ module Files else error("Something went wrong. Your changes were not committed") end - rescue CommitService::CommitError, CommitService::PreReceiveError, ValidationError => ex + rescue Repository::CommitError, Repository::PreReceiveError, ValidationError => ex error(ex.message) end diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index 3e00864f00d3ee8e6b730a9d8faa3b14f5d771df..91d715b2d637376ec18e268ee9aa3d4a1c46a340 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -3,9 +3,7 @@ require_relative "base_service" module Files class CreateService < Files::BaseService def commit - CommitService.transaction(project, current_user, @target_branch) do |tmp_ref| - repository.commit_file(current_user, @file_path, @file_content, @commit_message, tmp_ref) - end + repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch) end def validate diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb index d61ca31cf9d7eeabcb2a8d1b9a702445160bfc98..27c881c34308ad1bb360546bbfcee1bd49733d33 100644 --- a/app/services/files/delete_service.rb +++ b/app/services/files/delete_service.rb @@ -3,9 +3,7 @@ require_relative "base_service" module Files class DeleteService < Files::BaseService def commit - CommitService.transaction(project, current_user, @target_branch) do |tmp_ref| - repository.remove_file(current_user, @file_path, @commit_message, tmp_ref) - end + repository.remove_file(current_user, @file_path, @commit_message, @target_branch) end end end diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb index 69212b3699617eb6a3ba6afa20d4613079084ff1..a20903c6f02c13e8f4e014a56ce307fe1d829c3d 100644 --- a/app/services/files/update_service.rb +++ b/app/services/files/update_service.rb @@ -3,9 +3,7 @@ require_relative "base_service" module Files class UpdateService < Files::BaseService def commit - CommitService.transaction(project, current_user, @target_branch) do |tmp_ref| - repository.commit_file(current_user, @file_path, @file_content, @commit_message, tmp_ref) - end + repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch) end end end