From f4149bcddca9c0e7aac078b3e7c198f5624ea107 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 14 Aug 2015 16:23:40 +0200 Subject: [PATCH] Refactor how repository makes commit with pre/post receive hooks --- app/models/repository.rb | 119 ++++++++++++++++++++------- app/services/commit_service.rb | 56 ------------- app/services/files/base_service.rb | 2 +- app/services/files/create_service.rb | 4 +- app/services/files/delete_service.rb | 4 +- app/services/files/update_service.rb | 4 +- 6 files changed, 94 insertions(+), 95 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 32b3541907f..99b6cd3cad1 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 5000e73b473..c77da061a9c 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 507e21f5818..7aecee217d8 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 3e00864f00d..91d715b2d63 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 d61ca31cf9d..27c881c3430 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 69212b36996..a20903c6f02 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