Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2015-08-11 15:27:38 +0300
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2015-08-11 15:27:38 +0300
commit5daf44b7c86e0e2641a902b1da8b01d91fa3dbfa (patch)
treedbf754ad57c523759284cf5d8af84fd9f096701a /lib
parent2f706fbd231cabe7a76a5d17ac44285aaaf8592c (diff)
parent3a63c00505307a1d1e8196c0eae72a79b2a6885f (diff)
Merge branch 'revert-satellites' into 'master'
Revert satellites Return satellites to master for 7.14 We remove satellites in next release (8.0) See merge request !1136
Diffstat (limited to 'lib')
-rw-r--r--lib/api/files.rb50
-rw-r--r--lib/api/merge_requests.rb6
-rw-r--r--lib/gitlab.rb1
-rw-r--r--lib/gitlab/backend/shell.rb14
-rw-r--r--lib/gitlab/satellite/action.rb58
-rw-r--r--lib/gitlab/satellite/compare_action.rb44
-rw-r--r--lib/gitlab/satellite/files/delete_file_action.rb50
-rw-r--r--lib/gitlab/satellite/files/edit_file_action.rb68
-rw-r--r--lib/gitlab/satellite/files/file_action.rb25
-rw-r--r--lib/gitlab/satellite/files/new_file_action.rb67
-rw-r--r--lib/gitlab/satellite/logger.rb13
-rw-r--r--lib/gitlab/satellite/merge_action.rb146
-rw-r--r--lib/gitlab/satellite/satellite.rb148
-rw-r--r--lib/tasks/gitlab/check.rake56
-rw-r--r--lib/tasks/gitlab/enable_automerge.rake39
15 files changed, 754 insertions, 31 deletions
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 308c84dd135..83581cd3990 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -3,26 +3,6 @@ module API
class Files < Grape::API
before { authenticate! }
- helpers do
- def commit_params(attrs)
- {
- file_path: attrs[:file_path],
- current_branch: attrs[:branch_name],
- target_branch: attrs[:branch_name],
- commit_message: attrs[:commit_message],
- file_content: attrs[:content],
- file_content_encoding: attrs[:encoding]
- }
- end
-
- def commit_response(attrs)
- {
- file_path: attrs[:file_path],
- branch_name: attrs[:branch_name],
- }
- end
- end
-
resource :projects do
# Get file from repository
# File content is Base64 encoded
@@ -93,11 +73,17 @@ module API
required_attributes! [:file_path, :branch_name, :content, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding]
- result = ::Files::CreateService.new(user_project, current_user, commit_params(attrs)).execute
+ branch_name = attrs.delete(:branch_name)
+ file_path = attrs.delete(:file_path)
+ result = ::Files::CreateService.new(user_project, current_user, attrs, branch_name, file_path).execute
if result[:status] == :success
status(201)
- commit_response(attrs)
+
+ {
+ file_path: file_path,
+ branch_name: branch_name
+ }
else
render_api_error!(result[:message], 400)
end
@@ -119,11 +105,17 @@ module API
required_attributes! [:file_path, :branch_name, :content, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding]
- result = ::Files::UpdateService.new(user_project, current_user, commit_params(attrs)).execute
+ branch_name = attrs.delete(:branch_name)
+ file_path = attrs.delete(:file_path)
+ result = ::Files::UpdateService.new(user_project, current_user, attrs, branch_name, file_path).execute
if result[:status] == :success
status(200)
- commit_response(attrs)
+
+ {
+ file_path: file_path,
+ branch_name: branch_name
+ }
else
http_status = result[:http_status] || 400
render_api_error!(result[:message], http_status)
@@ -146,11 +138,17 @@ module API
required_attributes! [:file_path, :branch_name, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :commit_message]
- result = ::Files::DeleteService.new(user_project, current_user, commit_params(attrs)).execute
+ branch_name = attrs.delete(:branch_name)
+ file_path = attrs.delete(:file_path)
+ result = ::Files::DeleteService.new(user_project, current_user, attrs, branch_name, file_path).execute
if result[:status] == :success
status(200)
- commit_response(attrs)
+
+ {
+ file_path: file_path,
+ branch_name: branch_name
+ }
else
render_api_error!(result[:message], 400)
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 7412274b045..ce21c699e8f 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -198,11 +198,7 @@ module API
if merge_request.open? && !merge_request.work_in_progress?
if merge_request.can_be_merged?
- commit_message = params[:merge_commit_message] || merge_request.merge_commit_message
-
- ::MergeRequests::MergeService.new(merge_request.target_project, current_user).
- execute(merge_request, commit_message)
-
+ merge_request.automerge!(current_user, params[:merge_commit_message] || merge_request.merge_commit_message)
present merge_request, with: Entities::MergeRequest
else
render_api_error!('Branch cannot be merged', 405)
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index 6108697bc20..5fc1862c3e9 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -1,4 +1,5 @@
require 'gitlab/git'
module Gitlab
+ autoload :Satellite, 'gitlab/satellite/satellite'
end
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 14ee4701e7b..172d4902add 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -217,6 +217,20 @@ module Gitlab
FileUtils.mv(full_path(old_name), full_path(new_name))
end
+ # Remove GitLab Satellites for provided path (namespace or repo dir)
+ #
+ # Ex.
+ # rm_satellites("gitlab")
+ #
+ # rm_satellites("gitlab/gitlab-ci.git")
+ #
+ def rm_satellites(path)
+ raise ArgumentError.new("Path can't be blank") if path.blank?
+
+ satellites_path = File.join(Gitlab.config.satellites.path, path)
+ FileUtils.rm_r(satellites_path, force: true)
+ end
+
def url_to_repo(path)
Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git"
end
diff --git a/lib/gitlab/satellite/action.rb b/lib/gitlab/satellite/action.rb
new file mode 100644
index 00000000000..489070f1a3f
--- /dev/null
+++ b/lib/gitlab/satellite/action.rb
@@ -0,0 +1,58 @@
+module Gitlab
+ module Satellite
+ class Action
+ DEFAULT_OPTIONS = { git_timeout: Gitlab.config.satellites.timeout.seconds }
+
+ attr_accessor :options, :project, :user
+
+ def initialize(user, project, options = {})
+ @options = DEFAULT_OPTIONS.merge(options)
+ @project = project
+ @user = user
+ end
+
+ protected
+
+ # * Sets a 30s timeout for Git
+ # * Locks the satellite repo
+ # * Yields the prepared satellite repo
+ def in_locked_and_timed_satellite
+ Gitlab::ShellEnv.set_env(user)
+
+ Grit::Git.with_timeout(options[:git_timeout]) do
+ project.satellite.lock do
+ return yield project.satellite.repo
+ end
+ end
+ rescue Errno::ENOMEM => ex
+ return handle_exception(ex)
+ rescue Grit::Git::GitTimeout => ex
+ return handle_exception(ex)
+ ensure
+ Gitlab::ShellEnv.reset_env
+ end
+
+ # * Recreates the satellite
+ # * Sets up Git variables for the user
+ #
+ # Note: use this within #in_locked_and_timed_satellite
+ def prepare_satellite!(repo)
+ project.satellite.clear_and_update!
+
+ if user
+ repo.config['user.name'] = user.name
+ repo.config['user.email'] = user.email
+ end
+ end
+
+ def default_options(options = {})
+ { raise: true, timeout: true }.merge(options)
+ end
+
+ def handle_exception(exception)
+ Gitlab::GitLogger.error(exception.message)
+ false
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/compare_action.rb b/lib/gitlab/satellite/compare_action.rb
new file mode 100644
index 00000000000..46c98a8f4ca
--- /dev/null
+++ b/lib/gitlab/satellite/compare_action.rb
@@ -0,0 +1,44 @@
+module Gitlab
+ module Satellite
+ class BranchesWithoutParent < StandardError; end
+
+ class CompareAction < Action
+ def initialize(user, target_project, target_branch, source_project, source_branch)
+ super user, target_project
+
+ @target_project, @target_branch = target_project, target_branch
+ @source_project, @source_branch = source_project, source_branch
+ end
+
+ # Compare 2 repositories and return Gitlab::CompareResult object
+ def result
+ in_locked_and_timed_satellite do |target_repo|
+ prepare_satellite!(target_repo)
+ update_satellite_source_and_target!(target_repo)
+
+ Gitlab::CompareResult.new(compare(target_repo))
+ end
+ rescue Grit::Git::CommandFailed => ex
+ raise BranchesWithoutParent
+ end
+
+ private
+
+ # Assumes a satellite exists that is a fresh clone of the projects repo, prepares satellite for diffs
+ def update_satellite_source_and_target!(target_repo)
+ target_repo.remote_add('source', @source_project.repository.path_to_repo)
+ target_repo.remote_fetch('source')
+ rescue Grit::Git::CommandFailed => ex
+ handle_exception(ex)
+ end
+
+ def compare(repo)
+ @compare ||= Gitlab::Git::Compare.new(
+ Gitlab::Git::Repository.new(repo.path),
+ "origin/#{@target_branch}",
+ "source/#{@source_branch}"
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/files/delete_file_action.rb b/lib/gitlab/satellite/files/delete_file_action.rb
new file mode 100644
index 00000000000..0d37b9dea85
--- /dev/null
+++ b/lib/gitlab/satellite/files/delete_file_action.rb
@@ -0,0 +1,50 @@
+require_relative 'file_action'
+
+module Gitlab
+ module Satellite
+ class DeleteFileAction < FileAction
+ # Deletes file and creates a new commit for it
+ #
+ # Returns false if committing the change fails
+ # Returns false if pushing from the satellite to bare repo failed or was rejected
+ # Returns true otherwise
+ def commit!(content, commit_message)
+ in_locked_and_timed_satellite do |repo|
+ prepare_satellite!(repo)
+
+ # create target branch in satellite at the corresponding commit from bare repo
+ repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}")
+
+ # update the file in the satellite's working dir
+ file_path_in_satellite = File.join(repo.working_dir, file_path)
+
+ # Prevent relative links
+ unless safe_path?(file_path_in_satellite)
+ Gitlab::GitLogger.error("FileAction: Relative path not allowed")
+ return false
+ end
+
+ File.delete(file_path_in_satellite)
+
+ # add removed file
+ repo.remove(file_path_in_satellite)
+
+ # commit the changes
+ # will raise CommandFailed when commit fails
+ repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
+
+
+ # push commit back to bare repo
+ # will raise CommandFailed when push fails
+ repo.git.push({ raise: true, timeout: true }, :origin, ref)
+
+ # everything worked
+ true
+ end
+ rescue Grit::Git::CommandFailed => ex
+ Gitlab::GitLogger.error(ex.message)
+ false
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/files/edit_file_action.rb b/lib/gitlab/satellite/files/edit_file_action.rb
new file mode 100644
index 00000000000..3cb9c0b5ecb
--- /dev/null
+++ b/lib/gitlab/satellite/files/edit_file_action.rb
@@ -0,0 +1,68 @@
+require_relative 'file_action'
+
+module Gitlab
+ module Satellite
+ # GitLab server-side file update and commit
+ class EditFileAction < FileAction
+ # Updates the files content and creates a new commit for it
+ #
+ # Returns false if the ref has been updated while editing the file
+ # Returns false if committing the change fails
+ # Returns false if pushing from the satellite to bare repo failed or was rejected
+ # Returns true otherwise
+ def commit!(content, commit_message, encoding, new_branch = nil)
+ in_locked_and_timed_satellite do |repo|
+ prepare_satellite!(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}")
+ rescue Grit::Git::CommandFailed => ex
+ log_and_raise(CheckoutFailed, ex.message)
+ end
+
+ # update the file in the satellite's working dir
+ file_path_in_satellite = File.join(repo.working_dir, file_path)
+
+ # Prevent relative links
+ unless safe_path?(file_path_in_satellite)
+ Gitlab::GitLogger.error("FileAction: Relative path not allowed")
+ return false
+ end
+
+ # Write file
+ write_file(file_path_in_satellite, content, encoding)
+
+ # commit the changes
+ # will raise CommandFailed when commit fails
+ begin
+ repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
+ rescue Grit::Git::CommandFailed => ex
+ log_and_raise(CommitFailed, ex.message)
+ end
+
+
+ target_branch = new_branch.present? ? "#{ref}:#{new_branch}" : ref
+
+ # push commit back to bare repo
+ # will raise CommandFailed when push fails
+ begin
+ repo.git.push({ raise: true, timeout: true }, :origin, target_branch)
+ rescue Grit::Git::CommandFailed => ex
+ log_and_raise(PushFailed, ex.message)
+ end
+
+ # everything worked
+ true
+ end
+ end
+
+ private
+
+ def log_and_raise(errorClass, message)
+ Gitlab::GitLogger.error(message)
+ raise(errorClass, message)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/files/file_action.rb b/lib/gitlab/satellite/files/file_action.rb
new file mode 100644
index 00000000000..6446b14568a
--- /dev/null
+++ b/lib/gitlab/satellite/files/file_action.rb
@@ -0,0 +1,25 @@
+module Gitlab
+ module Satellite
+ class FileAction < Action
+ attr_accessor :file_path, :ref
+
+ def initialize(user, project, ref, file_path)
+ super user, project
+ @file_path = file_path
+ @ref = ref
+ end
+
+ def safe_path?(path)
+ File.absolute_path(path) == path
+ end
+
+ def write_file(abs_file_path, content, file_encoding = 'text')
+ if file_encoding == 'base64'
+ File.open(abs_file_path, 'wb') { |f| f.write(Base64.decode64(content)) }
+ else
+ File.open(abs_file_path, 'w') { |f| f.write(content) }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/files/new_file_action.rb b/lib/gitlab/satellite/files/new_file_action.rb
new file mode 100644
index 00000000000..724dfa0d042
--- /dev/null
+++ b/lib/gitlab/satellite/files/new_file_action.rb
@@ -0,0 +1,67 @@
+require_relative 'file_action'
+
+module Gitlab
+ module Satellite
+ class NewFileAction < FileAction
+ # Updates the files content and creates a new commit for it
+ #
+ # Returns false if the ref has been updated while editing the file
+ # Returns false if committing the change fails
+ # Returns false if pushing from the satellite to bare repo failed or was rejected
+ # Returns true otherwise
+ def commit!(content, commit_message, encoding, new_branch = nil)
+ in_locked_and_timed_satellite do |repo|
+ prepare_satellite!(repo)
+
+ # create target branch in satellite at the corresponding commit from bare repo
+ current_ref =
+ if @project.empty_repo?
+ # skip this step if we want to add first file to empty repo
+ Satellite::PARKING_BRANCH
+ else
+ repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}")
+ ref
+ end
+
+ file_path_in_satellite = File.join(repo.working_dir, file_path)
+ dir_name_in_satellite = File.dirname(file_path_in_satellite)
+
+ # Prevent relative links
+ unless safe_path?(file_path_in_satellite)
+ Gitlab::GitLogger.error("FileAction: Relative path not allowed")
+ return false
+ end
+
+ # Create dir if not exists
+ FileUtils.mkdir_p(dir_name_in_satellite)
+
+ # Write file
+ write_file(file_path_in_satellite, content, encoding)
+
+ # add new file
+ repo.add(file_path_in_satellite)
+
+ # commit the changes
+ # will raise CommandFailed when commit fails
+ repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
+
+ target_branch = if new_branch.present? && !@project.empty_repo?
+ "#{ref}:#{new_branch}"
+ else
+ "#{current_ref}:#{ref}"
+ end
+
+ # push commit back to bare repo
+ # will raise CommandFailed when push fails
+ repo.git.push({ raise: true, timeout: true }, :origin, target_branch)
+
+ # everything worked
+ true
+ end
+ rescue Grit::Git::CommandFailed => ex
+ Gitlab::GitLogger.error(ex.message)
+ false
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/logger.rb b/lib/gitlab/satellite/logger.rb
new file mode 100644
index 00000000000..6f3f8255aca
--- /dev/null
+++ b/lib/gitlab/satellite/logger.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module Satellite
+ class Logger < Gitlab::Logger
+ def self.file_name
+ 'satellites.log'
+ end
+
+ def format_message(severity, timestamp, progname, msg)
+ "#{timestamp.to_s(:long)}: #{msg}\n"
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/merge_action.rb b/lib/gitlab/satellite/merge_action.rb
new file mode 100644
index 00000000000..f9bf286697e
--- /dev/null
+++ b/lib/gitlab/satellite/merge_action.rb
@@ -0,0 +1,146 @@
+module Gitlab
+ module Satellite
+ # GitLab server-side merge
+ class MergeAction < Action
+ attr_accessor :merge_request
+
+ def initialize(user, merge_request)
+ super user, merge_request.target_project
+ @merge_request = merge_request
+ end
+
+ # Checks if a merge request can be executed without user interaction
+ def can_be_merged?
+ in_locked_and_timed_satellite do |merge_repo|
+ prepare_satellite!(merge_repo)
+ merge_in_satellite!(merge_repo)
+ end
+ end
+
+ # Merges the source branch into the target branch in the satellite and
+ # pushes it back to the repository.
+ # It also removes the source branch if requested in the merge request (and this is permitted by the merge request).
+ #
+ # Returns false if the merge produced conflicts
+ # Returns false if pushing from the satellite to the repository failed or was rejected
+ # Returns true otherwise
+ def merge!(merge_commit_message = nil)
+ in_locked_and_timed_satellite do |merge_repo|
+ prepare_satellite!(merge_repo)
+ if merge_in_satellite!(merge_repo, merge_commit_message)
+ # push merge back to bare repo
+ # will raise CommandFailed when push fails
+ merge_repo.git.push(default_options, :origin, merge_request.target_branch)
+
+ # remove source branch
+ if merge_request.remove_source_branch?
+ # will raise CommandFailed when push fails
+ merge_repo.git.push(default_options, :origin, ":#{merge_request.source_branch}")
+ end
+ # merge, push and branch removal successful
+ true
+ end
+ end
+ rescue Grit::Git::CommandFailed => ex
+ handle_exception(ex)
+ end
+
+ def diff_in_satellite
+ in_locked_and_timed_satellite do |merge_repo|
+ prepare_satellite!(merge_repo)
+ update_satellite_source_and_target!(merge_repo)
+
+ # Only show what is new in the source branch compared to the target branch, not the other way around.
+ # The line below with merge_base is equivalent to diff with three dots (git diff branch1...branch2)
+ # From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B"
+ common_commit = merge_repo.git.native(:merge_base, default_options, ["origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}"]).strip
+ merge_repo.git.native(:diff, default_options, common_commit, "source/#{merge_request.source_branch}")
+ end
+ rescue Grit::Git::CommandFailed => ex
+ handle_exception(ex)
+ end
+
+ def diffs_between_satellite
+ in_locked_and_timed_satellite do |merge_repo|
+ prepare_satellite!(merge_repo)
+ update_satellite_source_and_target!(merge_repo)
+ if merge_request.for_fork?
+ repository = Gitlab::Git::Repository.new(merge_repo.path)
+ diffs = Gitlab::Git::Diff.between(
+ repository,
+ "source/#{merge_request.source_branch}",
+ "origin/#{merge_request.target_branch}"
+ )
+ else
+ raise "Attempt to determine diffs between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]"
+ end
+
+ return diffs
+ end
+ rescue Grit::Git::CommandFailed => ex
+ handle_exception(ex)
+ end
+
+ # Get commit as an email patch
+ def format_patch
+ in_locked_and_timed_satellite do |merge_repo|
+ prepare_satellite!(merge_repo)
+ update_satellite_source_and_target!(merge_repo)
+ patch = merge_repo.git.format_patch(default_options({ stdout: true }), "origin/#{merge_request.target_branch}..source/#{merge_request.source_branch}")
+ end
+ rescue Grit::Git::CommandFailed => ex
+ handle_exception(ex)
+ end
+
+ # Retrieve an array of commits between the source and the target
+ def commits_between
+ in_locked_and_timed_satellite do |merge_repo|
+ prepare_satellite!(merge_repo)
+ update_satellite_source_and_target!(merge_repo)
+ if merge_request.for_fork?
+ repository = Gitlab::Git::Repository.new(merge_repo.path)
+ commits = Gitlab::Git::Commit.between(
+ repository,
+ "origin/#{merge_request.target_branch}",
+ "source/#{merge_request.source_branch}"
+ )
+ else
+ raise "Attempt to determine commits between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]"
+ end
+
+ return commits
+ end
+ rescue Grit::Git::CommandFailed => ex
+ handle_exception(ex)
+ end
+
+ private
+ # Merges the source_branch into the target_branch in the satellite.
+ #
+ # Note: it will clear out the satellite before doing anything
+ #
+ # Returns false if the merge produced conflicts
+ # Returns true otherwise
+ def merge_in_satellite!(repo, message = nil)
+ update_satellite_source_and_target!(repo)
+
+ message ||= "Merge branch '#{merge_request.source_branch}' into '#{merge_request.target_branch}'"
+
+ # merge the source branch into the satellite
+ # will raise CommandFailed when merge fails
+ repo.git.merge(default_options({ no_ff: true }), "-m#{message}", "source/#{merge_request.source_branch}")
+ rescue Grit::Git::CommandFailed => ex
+ handle_exception(ex)
+ end
+
+ # Assumes a satellite exists that is a fresh clone of the projects repo, prepares satellite for merges, diffs etc
+ def update_satellite_source_and_target!(repo)
+ repo.remote_add('source', merge_request.source_project.repository.path_to_repo)
+ repo.remote_fetch('source')
+ repo.git.checkout(default_options({ b: true }), merge_request.target_branch, "origin/#{merge_request.target_branch}")
+ rescue Grit::Git::CommandFailed => ex
+ handle_exception(ex)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb
new file mode 100644
index 00000000000..398643d68de
--- /dev/null
+++ b/lib/gitlab/satellite/satellite.rb
@@ -0,0 +1,148 @@
+module Gitlab
+ module Satellite
+ autoload :DeleteFileAction, 'gitlab/satellite/files/delete_file_action'
+ autoload :EditFileAction, 'gitlab/satellite/files/edit_file_action'
+ autoload :FileAction, 'gitlab/satellite/files/file_action'
+ autoload :NewFileAction, 'gitlab/satellite/files/new_file_action'
+
+ class CheckoutFailed < StandardError; end
+ class CommitFailed < StandardError; end
+ class PushFailed < StandardError; end
+
+ class Satellite
+ include Gitlab::Popen
+
+ PARKING_BRANCH = "__parking_branch"
+
+ attr_accessor :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def log(message)
+ Gitlab::Satellite::Logger.error(message)
+ end
+
+ def clear_and_update!
+ project.ensure_satellite_exists
+
+ @repo = nil
+ clear_working_dir!
+ delete_heads!
+ remove_remotes!
+ update_from_source!
+ end
+
+ def create
+ output, status = popen(%W(git clone -- #{project.repository.path_to_repo} #{path}),
+ Gitlab.config.satellites.path)
+
+ log("PID: #{project.id}: git clone #{project.repository.path_to_repo} #{path}")
+ log("PID: #{project.id}: -> #{output}")
+
+ if status.zero?
+ true
+ else
+ log("Failed to create satellite for #{project.name_with_namespace}")
+ false
+ end
+ end
+
+ def exists?
+ File.exists? path
+ end
+
+ # * Locks the satellite
+ # * Changes the current directory to the satellite's working dir
+ # * Yields
+ def lock
+ project.ensure_satellite_exists
+
+ File.open(lock_file, "w+") do |f|
+ begin
+ f.flock File::LOCK_EX
+ yield
+ ensure
+ f.flock File::LOCK_UN
+ end
+ end
+ end
+
+ def lock_file
+ create_locks_dir unless File.exists?(lock_files_dir)
+ File.join(lock_files_dir, "satellite_#{project.id}.lock")
+ end
+
+ def path
+ File.join(Gitlab.config.satellites.path, project.path_with_namespace)
+ end
+
+ def repo
+ project.ensure_satellite_exists
+
+ @repo ||= Grit::Repo.new(path)
+ end
+
+ def destroy
+ FileUtils.rm_rf(path)
+ end
+
+ private
+
+ # Clear the working directory
+ def clear_working_dir!
+ repo.git.reset(hard: true)
+ repo.git.clean(f: true, d: true, x: true)
+ end
+
+ # Deletes all branches except the parking branch
+ #
+ # This ensures we have no name clashes or issues updating branches when
+ # working with the satellite.
+ def delete_heads!
+ heads = repo.heads.map(&:name)
+
+ # update or create the parking branch
+ repo.git.checkout(default_options({ B: true }), PARKING_BRANCH)
+
+ # remove the parking branch from the list of heads ...
+ heads.delete(PARKING_BRANCH)
+ # ... and delete all others
+ heads.each { |head| repo.git.branch(default_options({ D: true }), head) }
+ end
+
+ # Deletes all remotes except origin
+ #
+ # This ensures we have no remote name clashes or issues updating branches when
+ # working with the satellite.
+ def remove_remotes!
+ remotes = repo.git.remote.split(' ')
+ remotes.delete('origin')
+ remotes.each { |name| repo.git.remote(default_options,'rm', name)}
+ end
+
+ # Updates the satellite from bare repo
+ #
+ # Note: this will only update remote branches (i.e. origin/*)
+ def update_from_source!
+ repo.git.remote(default_options, 'set-url', :origin, project.repository.path_to_repo)
+ repo.git.fetch(default_options, :origin)
+ end
+
+ def default_options(options = {})
+ { raise: true, timeout: true }.merge(options)
+ end
+
+ # Create directory for storing
+ # satellites lock files
+ def create_locks_dir
+ FileUtils.mkdir_p(lock_files_dir)
+ end
+
+ def lock_files_dir
+ @lock_files_dir ||= File.join(Gitlab.config.satellites.path, "tmp")
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 8acb6a7fd19..badb47c6779 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -25,6 +25,7 @@ namespace :gitlab do
check_init_script_exists
check_init_script_up_to_date
check_projects_have_namespace
+ check_satellites_exist
check_redis_version
check_ruby_version
check_git_version
@@ -237,6 +238,37 @@ namespace :gitlab do
end
end
+ def check_satellites_exist
+ print "Projects have satellites? ... "
+
+ unless Project.count > 0
+ puts "can't check, you have no projects".magenta
+ return
+ end
+ puts ""
+
+ Project.find_each(batch_size: 100) do |project|
+ print sanitized_message(project)
+
+ if project.satellite.exists?
+ puts "yes".green
+ elsif project.empty_repo?
+ puts "can't create, repository is empty".magenta
+ else
+ puts "no".red
+ try_fixing_it(
+ sudo_gitlab("bundle exec rake gitlab:satellites:create RAILS_ENV=production"),
+ "If necessary, remove the tmp/repo_satellites directory ...",
+ "... and rerun the above command"
+ )
+ for_more_information(
+ "doc/raketasks/maintenance.md "
+ )
+ fix_and_rerun
+ end
+ end
+ end
+
def check_log_writable
print "Log directory writable? ... "
@@ -307,6 +339,7 @@ namespace :gitlab do
check_repo_base_is_not_symlink
check_repo_base_user_and_group
check_repo_base_permissions
+ check_satellites_permissions
check_repos_hooks_directory_is_link
check_gitlab_shell_self_test
@@ -384,6 +417,29 @@ namespace :gitlab do
end
end
+ def check_satellites_permissions
+ print "Satellites access is drwxr-x---? ... "
+
+ satellites_path = Gitlab.config.satellites.path
+ unless File.exists?(satellites_path)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ if File.stat(satellites_path).mode.to_s(8).ends_with?("0750")
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo chmod u+rwx,g=rx,o-rwx #{satellites_path}",
+ )
+ for_more_information(
+ see_installation_guide_section "GitLab"
+ )
+ fix_and_rerun
+ end
+ end
+
def check_repo_base_user_and_group
gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user
gitlab_shell_owner_group = Gitlab.config.gitlab_shell.owner_group
diff --git a/lib/tasks/gitlab/enable_automerge.rake b/lib/tasks/gitlab/enable_automerge.rake
new file mode 100644
index 00000000000..3dade9d75b8
--- /dev/null
+++ b/lib/tasks/gitlab/enable_automerge.rake
@@ -0,0 +1,39 @@
+namespace :gitlab do
+ namespace :satellites do
+ desc "GitLab | Create satellite repos"
+ task create: :environment do
+ create_satellites
+ end
+ end
+
+ def create_satellites
+ warn_user_is_not_gitlab
+
+ print "Creating satellites for ..."
+ unless Project.count > 0
+ puts "skipping, because you have no projects".magenta
+ return
+ end
+ puts ""
+
+ Project.find_each(batch_size: 100) do |project|
+ print "#{project.name_with_namespace.yellow} ... "
+
+ unless project.repo_exists?
+ puts "skipping, because the repo is empty".magenta
+ next
+ end
+
+ if project.satellite.exists?
+ puts "exists already".green
+ else
+ print "\n... "
+ if project.satellite.create
+ puts "created".green
+ else
+ puts "error".red
+ end
+ end
+ end
+ end
+end