mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-09 04:36:11 +10:00
Merge branch 'drop-satellites'
This commit is contained in:
@@ -32,6 +32,8 @@ v 7.14.0 (unreleased)
|
||||
- Mention group and project name in creation, update and deletion notices (Achilleas Pipinellis)
|
||||
- Remove redis-store TTL monkey patch
|
||||
- Add support for CI skipped status
|
||||
- Fetch code from forks to refs/merge-requests/:id/head when merge request created
|
||||
- Remove satellites
|
||||
|
||||
v 7.13.2
|
||||
- Fix randomly failed spec
|
||||
|
||||
+1
-1
@@ -875,4 +875,4 @@ DEPENDENCIES
|
||||
wikicloth (= 0.8.1)
|
||||
|
||||
BUNDLED WITH
|
||||
1.10.5
|
||||
1.10.4
|
||||
|
||||
@@ -19,7 +19,7 @@ class @MergeRequestWidget
|
||||
when 'merged'
|
||||
location.reload()
|
||||
else
|
||||
setTimeout(merge_request_widget.mergeInProgress, 3000)
|
||||
setTimeout(merge_request_widget.mergeInProgress, 2000)
|
||||
dataType: 'json'
|
||||
|
||||
getMergeStatus: ->
|
||||
|
||||
@@ -13,13 +13,8 @@ class Projects::CompareController < Projects::ApplicationController
|
||||
base_ref = Addressable::URI.unescape(params[:from])
|
||||
@ref = head_ref = Addressable::URI.unescape(params[:to])
|
||||
|
||||
compare_result = CompareService.new.execute(
|
||||
current_user,
|
||||
@project,
|
||||
head_ref,
|
||||
@project,
|
||||
base_ref
|
||||
)
|
||||
compare_result = CompareService.new.
|
||||
execute(@project, head_ref, @project, base_ref)
|
||||
|
||||
@commits = compare_result.commits
|
||||
@diffs = compare_result.diffs
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
require 'gitlab/satellite/satellite'
|
||||
|
||||
class Projects::MergeRequestsController < Projects::ApplicationController
|
||||
before_action :module_enabled
|
||||
before_action :merge_request, only: [
|
||||
:edit, :update, :show, :diffs, :commits, :automerge, :automerge_check,
|
||||
:edit, :update, :show, :diffs, :commits, :merge, :merge_check,
|
||||
:ci_status, :toggle_subscription
|
||||
]
|
||||
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
|
||||
@@ -137,7 +135,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def automerge_check
|
||||
def merge_check
|
||||
if @merge_request.unchecked?
|
||||
@merge_request.check_if_can_be_merged
|
||||
end
|
||||
@@ -147,11 +145,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
|
||||
end
|
||||
|
||||
def automerge
|
||||
def merge
|
||||
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
|
||||
|
||||
if @merge_request.automergeable?
|
||||
AutoMergeWorker.perform_async(@merge_request.id, current_user.id, params)
|
||||
if @merge_request.mergeable?
|
||||
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
|
||||
@status = true
|
||||
else
|
||||
@status = false
|
||||
|
||||
+32
-24
@@ -41,8 +41,6 @@ class MergeRequest < ActiveRecord::Base
|
||||
|
||||
delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
|
||||
|
||||
attr_accessor :should_remove_source_branch
|
||||
|
||||
# When this attribute is true some MR validation is ignored
|
||||
# It allows us to close or modify broken merge requests
|
||||
attr_accessor :allow_broken
|
||||
@@ -57,7 +55,7 @@ class MergeRequest < ActiveRecord::Base
|
||||
transition [:reopened, :opened] => :closed
|
||||
end
|
||||
|
||||
event :merge do
|
||||
event :mark_as_merged do
|
||||
transition [:reopened, :opened, :locked] => :merged
|
||||
end
|
||||
|
||||
@@ -206,11 +204,7 @@ class MergeRequest < ActiveRecord::Base
|
||||
|
||||
def check_if_can_be_merged
|
||||
can_be_merged =
|
||||
if for_fork?
|
||||
Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
|
||||
else
|
||||
project.repository.can_be_merged?(source_branch, target_branch)
|
||||
end
|
||||
project.repository.can_be_merged?(source_sha, target_branch)
|
||||
|
||||
if can_be_merged
|
||||
mark_as_mergeable
|
||||
@@ -227,18 +221,6 @@ class MergeRequest < ActiveRecord::Base
|
||||
self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
|
||||
end
|
||||
|
||||
def automerge!(current_user, commit_message = nil)
|
||||
return unless automergeable?
|
||||
|
||||
MergeRequests::AutoMergeService.
|
||||
new(target_project, current_user).
|
||||
execute(self, commit_message)
|
||||
end
|
||||
|
||||
def remove_source_branch?
|
||||
self.should_remove_source_branch && !self.source_project.root_ref?(self.source_branch) && !self.for_fork?
|
||||
end
|
||||
|
||||
def open?
|
||||
opened? || reopened?
|
||||
end
|
||||
@@ -247,11 +229,11 @@ class MergeRequest < ActiveRecord::Base
|
||||
title =~ /\A\[?WIP\]?:? /i
|
||||
end
|
||||
|
||||
def automergeable?
|
||||
def mergeable?
|
||||
open? && !work_in_progress? && can_be_merged?
|
||||
end
|
||||
|
||||
def automerge_status
|
||||
def gitlab_merge_status
|
||||
if work_in_progress?
|
||||
"work_in_progress"
|
||||
else
|
||||
@@ -278,14 +260,14 @@ class MergeRequest < ActiveRecord::Base
|
||||
#
|
||||
# see "git diff"
|
||||
def to_diff(current_user)
|
||||
Gitlab::Satellite::MergeAction.new(current_user, self).diff_in_satellite
|
||||
target_project.repository.diff_text(target_branch, source_sha)
|
||||
end
|
||||
|
||||
# Returns the commit as a series of email patches.
|
||||
#
|
||||
# see "git format-patch"
|
||||
def to_patch(current_user)
|
||||
Gitlab::Satellite::MergeAction.new(current_user, self).format_patch
|
||||
target_project.repository.format_patch(target_branch, source_sha)
|
||||
end
|
||||
|
||||
def hook_attrs
|
||||
@@ -436,4 +418,30 @@ class MergeRequest < ActiveRecord::Base
|
||||
"Open"
|
||||
end
|
||||
end
|
||||
|
||||
def target_sha
|
||||
@target_sha ||= target_project.
|
||||
repository.commit(target_branch).sha
|
||||
end
|
||||
|
||||
def source_sha
|
||||
commits.first.sha
|
||||
end
|
||||
|
||||
def fetch_ref
|
||||
target_project.repository.fetch_ref(
|
||||
source_project.repository.path_to_repo,
|
||||
"refs/heads/#{source_branch}",
|
||||
"refs/merge-requests/#{id}/head"
|
||||
)
|
||||
end
|
||||
|
||||
def in_locked_state
|
||||
begin
|
||||
lock_mr
|
||||
yield
|
||||
ensure
|
||||
unlock_mr if locked?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,9 +16,8 @@ require Rails.root.join("app/models/commit")
|
||||
class MergeRequestDiff < ActiveRecord::Base
|
||||
include Sortable
|
||||
|
||||
# Prevent store of diff
|
||||
# if commits amount more then 200
|
||||
COMMITS_SAFE_SIZE = 200
|
||||
# Prevent store of diff if commits amount more then 500
|
||||
COMMITS_SAFE_SIZE = 500
|
||||
|
||||
attr_reader :commits, :diffs
|
||||
|
||||
@@ -124,12 +123,12 @@ class MergeRequestDiff < ActiveRecord::Base
|
||||
if new_diffs.any?
|
||||
if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES
|
||||
self.state = :overflow_diff_files_limit
|
||||
new_diffs = []
|
||||
new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES]
|
||||
end
|
||||
|
||||
if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES
|
||||
self.state = :overflow_diff_lines_limit
|
||||
new_diffs = []
|
||||
new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -160,12 +159,21 @@ class MergeRequestDiff < ActiveRecord::Base
|
||||
private
|
||||
|
||||
def compare_result
|
||||
@compare_result ||= CompareService.new.execute(
|
||||
merge_request.author,
|
||||
merge_request.source_project,
|
||||
merge_request.source_branch,
|
||||
merge_request.target_project,
|
||||
merge_request.target_branch,
|
||||
)
|
||||
@compare_result ||=
|
||||
begin
|
||||
# Update ref if merge request is from fork
|
||||
merge_request.fetch_ref if merge_request.for_fork?
|
||||
|
||||
# Get latest sha of branch from source project
|
||||
source_sha = merge_request.source_project.commit(source_branch).sha
|
||||
|
||||
Gitlab::CompareResult.new(
|
||||
Gitlab::Git::Compare.new(
|
||||
merge_request.target_project.repository.raw_repository,
|
||||
merge_request.target_branch,
|
||||
source_sha,
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -115,12 +115,11 @@ class Namespace < ActiveRecord::Base
|
||||
|
||||
def move_dir
|
||||
if gitlab_shell.mv_namespace(path_was, path)
|
||||
# If repositories moved successfully we need to remove old satellites
|
||||
# and send update instructions to users.
|
||||
# If repositories moved successfully we need to
|
||||
# send update instructions to users.
|
||||
# However we cannot allow rollback since we moved namespace dir
|
||||
# So we basically we mute exceptions in next actions
|
||||
begin
|
||||
gitlab_shell.rm_satellites(path_was)
|
||||
send_update_instructions
|
||||
rescue
|
||||
# Returning false does not rollback after_* transaction but gives
|
||||
|
||||
+1
-13
@@ -520,14 +520,6 @@ class Project < ActiveRecord::Base
|
||||
!repository.exists? || repository.empty?
|
||||
end
|
||||
|
||||
def ensure_satellite_exists
|
||||
self.satellite.create unless self.satellite.exists?
|
||||
end
|
||||
|
||||
def satellite
|
||||
@satellite ||= Gitlab::Satellite::Satellite.new(self)
|
||||
end
|
||||
|
||||
def repo
|
||||
repository.raw
|
||||
end
|
||||
@@ -597,14 +589,11 @@ class Project < ActiveRecord::Base
|
||||
new_path_with_namespace = File.join(namespace_dir, path)
|
||||
|
||||
if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace)
|
||||
# If repository moved successfully we need to remove old satellite
|
||||
# and send update instructions to users.
|
||||
# If repository moved successfully we need to send update instructions to users.
|
||||
# However we cannot allow rollback since we moved repository
|
||||
# So we basically we mute exceptions in next actions
|
||||
begin
|
||||
gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
|
||||
gitlab_shell.rm_satellites(old_path_with_namespace)
|
||||
ensure_satellite_exists
|
||||
send_move_instructions
|
||||
reset_events_cache
|
||||
rescue
|
||||
@@ -702,7 +691,6 @@ class Project < ActiveRecord::Base
|
||||
def create_repository
|
||||
if forked?
|
||||
if gitlab_shell.fork_repository(forked_from_project.path_with_namespace, self.namespace.path)
|
||||
ensure_satellite_exists
|
||||
true
|
||||
else
|
||||
errors.add(:base, 'Failed to fork repository via gitlab-shell')
|
||||
|
||||
@@ -74,6 +74,8 @@ class GitlabCiService < CiService
|
||||
else
|
||||
:error
|
||||
end
|
||||
rescue Errno::ECONNREFUSED
|
||||
:error
|
||||
end
|
||||
|
||||
def fork_registration(new_project, private_token)
|
||||
@@ -103,6 +105,8 @@ class GitlabCiService < CiService
|
||||
if response.code == 200 and response["coverage"]
|
||||
response["coverage"]
|
||||
end
|
||||
rescue Errno::ECONNREFUSED
|
||||
nil
|
||||
end
|
||||
|
||||
def build_page(sha, ref)
|
||||
|
||||
@@ -411,15 +411,36 @@ class Repository
|
||||
}
|
||||
end
|
||||
|
||||
def can_be_merged?(source_branch, target_branch)
|
||||
def can_be_merged?(source_sha, target_branch)
|
||||
our_commit = rugged.branches[target_branch].target
|
||||
their_commit = rugged.branches[source_branch].target
|
||||
their_commit = rugged.lookup(source_sha)
|
||||
|
||||
if our_commit && their_commit
|
||||
!rugged.merge_commits(our_commit, their_commit).conflicts?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def merge(source_sha, target_branch, options = {})
|
||||
our_commit = rugged.branches[target_branch].target
|
||||
their_commit = rugged.lookup(source_sha)
|
||||
|
||||
raise "Invalid merge target" if our_commit.nil?
|
||||
raise "Invalid merge source" if their_commit.nil?
|
||||
|
||||
merge_index = rugged.merge_commits(our_commit, their_commit)
|
||||
return false if merge_index.conflicts?
|
||||
|
||||
actual_options = options.merge(
|
||||
parents: [our_commit, their_commit],
|
||||
tree: merge_index.write_tree(rugged),
|
||||
update_ref: "refs/heads/#{target_branch}"
|
||||
)
|
||||
|
||||
Rugged::Commit.create(rugged, actual_options)
|
||||
end
|
||||
|
||||
def search_files(query, ref)
|
||||
offset = 2
|
||||
args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} #{query} #{ref || root_ref})
|
||||
@@ -453,6 +474,11 @@ class Repository
|
||||
)
|
||||
end
|
||||
|
||||
def fetch_ref(source_path, source_ref, target_ref)
|
||||
args = %W(git fetch #{source_path} #{source_ref}:#{target_ref})
|
||||
Gitlab::Popen.popen(args, path_to_repo)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cache
|
||||
|
||||
@@ -31,6 +31,10 @@ class BaseService
|
||||
SystemHooksService.new
|
||||
end
|
||||
|
||||
def repository
|
||||
project.repository
|
||||
end
|
||||
|
||||
# Add an error to the specified model for restricted visibility levels
|
||||
def deny_visibility_level(model, denied_visibility_level = nil)
|
||||
denied_visibility_level ||= model.visibility_level
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
require 'securerandom'
|
||||
|
||||
# Compare 2 branches for one repo or between repositories
|
||||
# and return Gitlab::CompareResult object that responds to commits and diffs
|
||||
class CompareService
|
||||
def execute(current_user, source_project, source_branch, target_project, target_branch)
|
||||
# Try to compare branches to get commits list and diffs
|
||||
#
|
||||
# Note: Use satellite only when need to compare between two repos
|
||||
# because satellites are slower than operations on bare repo
|
||||
if target_project == source_project
|
||||
Gitlab::CompareResult.new(
|
||||
Gitlab::Git::Compare.new(
|
||||
target_project.repository.raw_repository,
|
||||
target_branch,
|
||||
source_branch,
|
||||
)
|
||||
def execute(source_project, source_branch, target_project, target_branch)
|
||||
source_sha = source_project.commit(source_branch).sha
|
||||
|
||||
# If compare with other project we need to fetch ref first
|
||||
unless target_project == source_project
|
||||
random_string = SecureRandom.hex
|
||||
|
||||
target_project.repository.fetch_ref(
|
||||
source_project.repository.path_to_repo,
|
||||
"refs/heads/#{source_branch}",
|
||||
"refs/tmp/#{random_string}/head"
|
||||
)
|
||||
else
|
||||
Gitlab::Satellite::CompareAction.new(
|
||||
current_user,
|
||||
target_project,
|
||||
target_branch,
|
||||
source_project,
|
||||
source_branch
|
||||
).result
|
||||
end
|
||||
|
||||
Gitlab::CompareResult.new(
|
||||
Gitlab::Git::Compare.new(
|
||||
target_project.repository.raw_repository,
|
||||
target_branch,
|
||||
source_sha,
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,15 +33,8 @@ module Files
|
||||
|
||||
private
|
||||
|
||||
def repository
|
||||
project.repository
|
||||
end
|
||||
|
||||
def after_commit(sha, branch)
|
||||
commit = repository.commit(sha)
|
||||
full_ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch}"
|
||||
old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA
|
||||
GitPushService.new.execute(project, current_user, old_sha, sha, full_ref)
|
||||
PostCommitService.new(project, current_user).execute(sha, branch)
|
||||
end
|
||||
|
||||
def current_branch
|
||||
|
||||
@@ -10,16 +10,14 @@ class GitPushService
|
||||
#
|
||||
# Next, this method:
|
||||
# 1. Creates the push event
|
||||
# 2. Ensures that the project satellite exists
|
||||
# 3. Updates merge requests
|
||||
# 4. Recognizes cross-references from commit messages
|
||||
# 5. Executes the project's web hooks
|
||||
# 6. Executes the project's services
|
||||
# 2. Updates merge requests
|
||||
# 3. Recognizes cross-references from commit messages
|
||||
# 4. Executes the project's web hooks
|
||||
# 5. Executes the project's services
|
||||
#
|
||||
def execute(project, user, oldrev, newrev, ref)
|
||||
@project, @user = project, user
|
||||
|
||||
project.ensure_satellite_exists
|
||||
project.repository.expire_cache
|
||||
|
||||
if push_remove_branch?(ref, newrev)
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
module MergeRequests
|
||||
# AutoMergeService class
|
||||
#
|
||||
# Do git merge in satellite and in case of success
|
||||
# mark merge request as merged and execute all hooks and notifications
|
||||
# Called when you do merge via GitLab UI
|
||||
class AutoMergeService < BaseMergeService
|
||||
attr_reader :merge_request, :commit_message
|
||||
|
||||
def execute(merge_request, commit_message)
|
||||
@commit_message = commit_message
|
||||
@merge_request = merge_request
|
||||
|
||||
merge_request.lock_mr
|
||||
|
||||
if merge!
|
||||
merge_request.merge
|
||||
create_merge_event(merge_request, current_user)
|
||||
create_note(merge_request)
|
||||
notification_service.merge_mr(merge_request, current_user)
|
||||
execute_hooks(merge_request, 'merge')
|
||||
true
|
||||
else
|
||||
merge_request.unlock_mr
|
||||
false
|
||||
end
|
||||
rescue
|
||||
merge_request.unlock_mr if merge_request.locked?
|
||||
merge_request.mark_as_unmergeable
|
||||
false
|
||||
end
|
||||
|
||||
def merge!
|
||||
if merge_request.for_fork?
|
||||
Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
|
||||
else
|
||||
# Merge local branches using rugged instead of satellites
|
||||
if sha = commit
|
||||
after_commit(sha, merge_request.target_branch)
|
||||
|
||||
if merge_request.remove_source_branch?
|
||||
DeleteBranchService.new(merge_request.source_project, current_user).execute(merge_request.source_branch)
|
||||
end
|
||||
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def commit
|
||||
committer = repository.user_to_comitter(current_user)
|
||||
|
||||
options = {
|
||||
message: commit_message,
|
||||
author: committer,
|
||||
committer: committer
|
||||
}
|
||||
|
||||
repository.merge(merge_request.source_branch, merge_request.target_branch, options)
|
||||
end
|
||||
|
||||
def after_commit(sha, branch)
|
||||
commit = repository.commit(sha)
|
||||
full_ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch}"
|
||||
old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA
|
||||
GitPushService.new.execute(project, current_user, old_sha, sha, full_ref)
|
||||
end
|
||||
|
||||
def repository
|
||||
project.repository
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,10 +0,0 @@
|
||||
module MergeRequests
|
||||
class BaseMergeService < MergeRequests::BaseService
|
||||
|
||||
private
|
||||
|
||||
def create_merge_event(merge_request, current_user)
|
||||
EventCreateService.new.merge_mr(merge_request, current_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -12,12 +12,16 @@ module MergeRequests
|
||||
merge_request.target_project ||= (project.forked_from_project || project)
|
||||
merge_request.target_branch ||= merge_request.target_project.default_branch
|
||||
|
||||
unless merge_request.target_branch && merge_request.source_branch
|
||||
return build_failed(merge_request, nil)
|
||||
if merge_request.target_branch.blank? || merge_request.source_branch.blank?
|
||||
message =
|
||||
if params[:source_branch] || params[:target_branch]
|
||||
"You must select source and target branch"
|
||||
end
|
||||
|
||||
return build_failed(merge_request, message)
|
||||
end
|
||||
|
||||
compare_result = CompareService.new.execute(
|
||||
current_user,
|
||||
merge_request.source_project,
|
||||
merge_request.source_branch,
|
||||
merge_request.target_project,
|
||||
@@ -40,7 +44,6 @@ module MergeRequests
|
||||
merge_request.compare_diffs = diffs
|
||||
|
||||
elsif diffs == false
|
||||
# satellite timeout return false
|
||||
merge_request.can_be_created = false
|
||||
merge_request.compare_failed = true
|
||||
end
|
||||
@@ -59,9 +62,6 @@ module MergeRequests
|
||||
end
|
||||
|
||||
merge_request
|
||||
|
||||
rescue Gitlab::Satellite::BranchesWithoutParent
|
||||
return build_failed(merge_request, "Selected branches have no common commit so they cannot be merged.")
|
||||
end
|
||||
|
||||
def build_failed(merge_request, message)
|
||||
|
||||
@@ -1,22 +1,57 @@
|
||||
module MergeRequests
|
||||
# MergeService class
|
||||
#
|
||||
# Mark existing merge request as merged
|
||||
# and execute all hooks and notifications
|
||||
# Called when you do merge via command line and push code
|
||||
# to target branch
|
||||
class MergeService < BaseMergeService
|
||||
# Do git merge and in case of success
|
||||
# mark merge request as merged and execute all hooks and notifications
|
||||
# Executed when you do merge via GitLab UI
|
||||
#
|
||||
class MergeService < MergeRequests::BaseService
|
||||
attr_reader :merge_request, :commit_message
|
||||
|
||||
def execute(merge_request, commit_message)
|
||||
merge_request.merge
|
||||
@commit_message = commit_message
|
||||
@merge_request = merge_request
|
||||
|
||||
create_merge_event(merge_request, current_user)
|
||||
create_note(merge_request)
|
||||
notification_service.merge_mr(merge_request, current_user)
|
||||
execute_hooks(merge_request, 'merge')
|
||||
unless @merge_request.mergeable?
|
||||
return error('Merge request is not mergeable')
|
||||
end
|
||||
|
||||
true
|
||||
rescue
|
||||
false
|
||||
merge_request.in_locked_state do
|
||||
if merge_changes
|
||||
after_merge
|
||||
success
|
||||
else
|
||||
error('Can not merge changes')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def merge_changes
|
||||
if sha = commit
|
||||
after_commit(sha, merge_request.target_branch)
|
||||
end
|
||||
end
|
||||
|
||||
def commit
|
||||
committer = repository.user_to_comitter(current_user)
|
||||
|
||||
options = {
|
||||
message: commit_message,
|
||||
author: committer,
|
||||
committer: committer
|
||||
}
|
||||
|
||||
repository.merge(merge_request.source_sha, merge_request.target_branch, options)
|
||||
end
|
||||
|
||||
def after_commit(sha, branch)
|
||||
PostCommitService.new(project, current_user).execute(sha, branch)
|
||||
end
|
||||
|
||||
def after_merge
|
||||
MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
module MergeRequests
|
||||
# PostMergeService class
|
||||
#
|
||||
# Mark existing merge request as merged
|
||||
# and execute all hooks and notifications
|
||||
#
|
||||
class PostMergeService < MergeRequests::BaseService
|
||||
def execute(merge_request)
|
||||
merge_request.mark_as_merged
|
||||
create_merge_event(merge_request, current_user)
|
||||
create_note(merge_request)
|
||||
notification_service.merge_mr(merge_request, current_user)
|
||||
execute_hooks(merge_request, 'merge')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_merge_event(merge_request, current_user)
|
||||
EventCreateService.new.merge_mr(merge_request, current_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -33,9 +33,9 @@ module MergeRequests
|
||||
|
||||
|
||||
merge_requests.uniq.select(&:source_project).each do |merge_request|
|
||||
MergeRequests::MergeService.
|
||||
MergeRequests::PostMergeService.
|
||||
new(merge_request.target_project, @current_user).
|
||||
execute(merge_request, nil)
|
||||
execute(merge_request)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
class PostCommitService < BaseService
|
||||
def execute(sha, branch)
|
||||
commit = repository.commit(sha)
|
||||
full_ref = 'refs/heads/' + branch
|
||||
old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA
|
||||
GitPushService.new.execute(project, current_user, old_sha, sha, full_ref)
|
||||
end
|
||||
end
|
||||
@@ -27,7 +27,6 @@ module Projects
|
||||
end
|
||||
end
|
||||
|
||||
project.satellite.destroy
|
||||
log_info("Project \"#{project.name}\" was removed")
|
||||
system_hook_service.execute_hooks_for(project, :destroy)
|
||||
true
|
||||
|
||||
@@ -33,9 +33,6 @@ module Projects
|
||||
raise TransferError.new("Project with same path in target namespace already exists")
|
||||
end
|
||||
|
||||
# Remove old satellite
|
||||
project.satellite.destroy
|
||||
|
||||
# Apply new namespace id
|
||||
project.namespace = new_namespace
|
||||
project.save!
|
||||
@@ -51,9 +48,6 @@ module Projects
|
||||
# Move wiki repo also if present
|
||||
gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
|
||||
|
||||
# Create a new satellite (reload project from DB)
|
||||
Project.find(project.id).ensure_satellite_exists
|
||||
|
||||
# clear project cached events
|
||||
project.reset_events_cache
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
- if @merge_request.compare_failed
|
||||
.alert.alert-danger
|
||||
%h4 Compare failed
|
||||
%p We can't compare selected branches. It may be because of huge diff or satellite timeout. Please try again or select different branches.
|
||||
%p We can't compare selected branches. It may be because of huge diff. Please try again or select different branches.
|
||||
- else
|
||||
.light-well
|
||||
.center
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
= icon('history')
|
||||
Commits
|
||||
%span.badge= @commits.size
|
||||
%li.diffs-tab
|
||||
%li.diffs-tab.active
|
||||
= link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
|
||||
= icon('list-alt')
|
||||
Changes
|
||||
@@ -33,7 +33,7 @@
|
||||
.tab-content
|
||||
#commits.commits.tab-pane
|
||||
= render "projects/commits/commits", project: @project
|
||||
#diffs.diffs.tab-pane
|
||||
#diffs.diffs.tab-pane.active
|
||||
- if @diffs.present?
|
||||
= render "projects/diffs/diffs", diffs: @diffs, project: @project
|
||||
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
.mr-widget-body
|
||||
- if @project.archived?
|
||||
= render 'projects/merge_requests/widget/open/archived'
|
||||
- elsif !@project.satellite.exists?
|
||||
= render 'projects/merge_requests/widget/open/no_satellite'
|
||||
- elsif @merge_request.commits.blank?
|
||||
= render 'projects/merge_requests/widget/open/nothing'
|
||||
- elsif @merge_request.branch_missing?
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
var merge_request_widget;
|
||||
|
||||
merge_request_widget = new MergeRequestWidget({
|
||||
url_to_automerge_check: "#{automerge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
|
||||
url_to_automerge_check: "#{merge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
|
||||
check_enable: #{@merge_request.unchecked? ? "true" : "false"},
|
||||
url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
|
||||
ci_enable: #{@project.ci_service ? "true" : "false"},
|
||||
current_status: "#{@merge_request.automerge_status}",
|
||||
current_status: "#{@merge_request.gitlab_merge_status}",
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= form_for [:automerge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f|
|
||||
= form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f|
|
||||
= hidden_field_tag :authenticity_token, form_authenticity_token
|
||||
.accept-merge-holder.clearfix.js-toggle-container
|
||||
.accept-action
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
%p
|
||||
%span
|
||||
%strong This repository does not have a satellite. Please ask an administrator to fix this issue!
|
||||
@@ -1,13 +0,0 @@
|
||||
class AutoMergeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: :default
|
||||
|
||||
def perform(merge_request_id, current_user_id, params)
|
||||
params = params.with_indifferent_access
|
||||
current_user = User.find(current_user_id)
|
||||
merge_request = MergeRequest.find(merge_request_id)
|
||||
merge_request.should_remove_source_branch = params[:should_remove_source_branch]
|
||||
merge_request.automerge!(current_user, params[:commit_message])
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
class MergeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: :default
|
||||
|
||||
def perform(merge_request_id, current_user_id, params)
|
||||
params = params.with_indifferent_access
|
||||
current_user = User.find(current_user_id)
|
||||
merge_request = MergeRequest.find(merge_request_id)
|
||||
|
||||
result = MergeRequests::MergeService.new(merge_request.target_project, current_user).
|
||||
execute(merge_request, params[:commit_message])
|
||||
|
||||
if result[:status] == :success && params[:should_remove_source_branch].present?
|
||||
DeleteBranchService.new(merge_request.source_project, current_user).
|
||||
execute(merge_request.source_branch)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -27,7 +27,6 @@ class RepositoryImportWorker
|
||||
|
||||
project.import_finish
|
||||
project.save
|
||||
project.satellite.create unless project.satellite.exists?
|
||||
ProjectCacheWorker.perform_async(project.id)
|
||||
Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket'
|
||||
end
|
||||
|
||||
+2
-2
@@ -459,8 +459,8 @@ Gitlab::Application.routes.draw do
|
||||
member do
|
||||
get :diffs
|
||||
get :commits
|
||||
post :automerge
|
||||
get :automerge_check
|
||||
post :merge
|
||||
get :merge_check
|
||||
get :ci_status
|
||||
post :toggle_subscription
|
||||
end
|
||||
|
||||
@@ -56,9 +56,9 @@ To serve repositories over SSH there's an add-on application called gitlab-shell
|
||||
|
||||
A typical install of GitLab will be on GNU/Linux. It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. By default, communication between Unicorn and the front end is via a Unix domain socket but forwarding requests via TCP is also supported. The web front end accesses `/home/git/gitlab/public` bypassing the Unicorn server to serve static pages, uploads (e.g. avatar images or attachments), and precompiled assets. GitLab serves web pages and a [GitLab API](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/api) using the Unicorn web server. It uses Sidekiq as a job queue which, in turn, uses redis as a non-persistent database backend for job information, meta data, and incoming jobs.
|
||||
|
||||
The GitLab web app uses MySQL or PostgreSQL for persistent database information (e.g. users, permissions, issues, other meta data). GitLab stores the bare git repositories it serves in `/home/git/repositories` by default. It also keeps default branch and hook information with the bare repository. `/home/git/gitlab-satellites` keeps checked out repositories when performing actions such as a merge request, editing files in the web interface, etc.
|
||||
The GitLab web app uses MySQL or PostgreSQL for persistent database information (e.g. users, permissions, issues, other meta data). GitLab stores the bare git repositories it serves in `/home/git/repositories` by default. It also keeps default branch and hook information with the bare repository.
|
||||
|
||||
The satellite repository is used by the web interface for editing repositories and the wiki which is also a git repository. When serving repositories over HTTP/HTTPS GitLab utilizes the GitLab API to resolve authorization and access as well as serving git objects.
|
||||
When serving repositories over HTTP/HTTPS GitLab utilizes the GitLab API to resolve authorization and access as well as serving git objects.
|
||||
|
||||
The add-on component gitlab-shell serves repositories over SSH. It manages the SSH keys within `/home/git/.ssh/authorized_keys` which should not be manually edited. gitlab-shell accesses the bare repositories directly to serve git objects and communicates with redis to submit jobs to Sidekiq for GitLab to process. gitlab-shell queries the GitLab API to determine authorization and access.
|
||||
|
||||
@@ -129,7 +129,7 @@ Note: `/home/git/` is shorthand for `/home/git`.
|
||||
|
||||
gitlabhq (includes Unicorn and Sidekiq logs)
|
||||
|
||||
- `/home/git/gitlab/log/` contains `application.log`, `production.log`, `sidekiq.log`, `unicorn.stdout.log`, `githost.log`, `satellites.log`, and `unicorn.stderr.log` normally.
|
||||
- `/home/git/gitlab/log/` contains `application.log`, `production.log`, `sidekiq.log`, `unicorn.stdout.log`, `githost.log` and `unicorn.stderr.log` normally.
|
||||
|
||||
gitlab-shell
|
||||
|
||||
|
||||
@@ -216,10 +216,6 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
|
||||
sudo chmod -R u+rwX,go-w log/
|
||||
sudo chmod -R u+rwX tmp/
|
||||
|
||||
# Create directory for satellites
|
||||
sudo -u git -H mkdir /home/git/gitlab-satellites
|
||||
sudo chmod u+rwx,g=rx,o-rwx /home/git/gitlab-satellites
|
||||
|
||||
# Make sure GitLab can write to the tmp/pids/ and tmp/sockets/ directories
|
||||
sudo chmod -R u+rwX tmp/pids/
|
||||
sudo chmod -R u+rwX tmp/sockets/
|
||||
|
||||
@@ -40,7 +40,7 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab
|
||||
|
||||
### Storage
|
||||
|
||||
The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least twice as much free space as all your repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo.
|
||||
The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least as much free space as all your repos combined take up.
|
||||
|
||||
If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them.
|
||||
|
||||
|
||||
@@ -6,16 +6,14 @@ This is the directory structure you will end up with following the instructions
|
||||
| |-- git
|
||||
| |-- .ssh
|
||||
| |-- gitlab
|
||||
| |-- gitlab-satellites
|
||||
| |-- gitlab-shell
|
||||
| |-- repositories
|
||||
|
||||
* `/home/git/.ssh` - contains openssh settings. Specifically the `authorized_keys` file managed by gitlab-shell.
|
||||
* `/home/git/gitlab` - GitLab core software.
|
||||
* `/home/git/gitlab-satellites` - checked out repositories for merge requests and file editing from web UI. This can be treated as a temporary files directory.
|
||||
* `/home/git/gitlab-shell` - Core add-on component of GitLab. Maintains SSH cloning and other functionality.
|
||||
* `/home/git/repositories` - bare repositories for all projects organized by namespace. This is where the git repositories which are pushed/pulled are maintained for all projects. **This area is critical data for projects. [Keep a backup](../raketasks/backup_restore.md)**
|
||||
|
||||
*Note: the default locations for gitlab-satellites and repositories can be configured in `config/gitlab.yml` of GitLab and `config.yml` of gitlab-shell.*
|
||||
*Note: the default locations for repositories can be configured in `config/gitlab.yml` of GitLab and `config.yml` of gitlab-shell.*
|
||||
|
||||
To see a more in-depth overview see the [GitLab architecture doc](../development/architecture.md).
|
||||
|
||||
+1
-11
@@ -51,16 +51,6 @@ December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/
|
||||
error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'
|
||||
```
|
||||
|
||||
#### satellites.log
|
||||
This file lives in `/var/log/gitlab/gitlab-rails/satellites.log` for omnibus package or in `/home/git/gitlab/log/satellites.log` for installations from the source.
|
||||
|
||||
In some cases GitLab should perform write actions to git repository, for example when it is needed to merge the merge request or edit a file with online editor. If something went wrong you can look into this file to find out what exactly happened.
|
||||
```
|
||||
October 07, 2014 11:36: Failed to create satellite for Chesley Weimann III / project1817
|
||||
October 07, 2014 11:36: PID: 1872: git clone /Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/repositories/conrad6841/gitlabhq.git /Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/conrad6841/gitlabhq
|
||||
October 07, 2014 11:36: PID: 1872: -> fatal: repository '/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/repositories/conrad6841/gitlabhq.git' does not exist
|
||||
```
|
||||
|
||||
#### sidekiq.log
|
||||
This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for omnibus package or in `/home/git/gitlab/log/sidekiq.log` for installations from the source.
|
||||
|
||||
@@ -99,4 +89,4 @@ W, [2015-02-13T07:16:01.313000 #9094] WARN -- : Unicorn::WorkerKiller send SIGQ
|
||||
I, [2015-02-13T07:16:01.530733 #9047] INFO -- : reaped #<Process::Status: pid 9094 exit 0> worker=1
|
||||
I, [2015-02-13T07:16:01.534501 #13379] INFO -- : worker=1 spawned pid=13379
|
||||
I, [2015-02-13T07:16:01.534848 #13379] INFO -- : worker=1 ready
|
||||
```
|
||||
```
|
||||
|
||||
@@ -105,24 +105,11 @@ Log directory writable? ... yes
|
||||
Tmp directory writable? ... yes
|
||||
Init script exists? ... yes
|
||||
Init script up-to-date? ... yes
|
||||
Projects have satellites? ... yes
|
||||
Redis version >= 2.0.0? ... yes
|
||||
|
||||
Checking GitLab ... Finished
|
||||
```
|
||||
|
||||
## (Re-)Create satellite repositories
|
||||
|
||||
This will create satellite repositories for all your projects.
|
||||
|
||||
If necessary, remove the `repo_satellites` directory and rerun the commands below.
|
||||
|
||||
```
|
||||
sudo -u git -H mkdir -p /home/git/gitlab-satellites
|
||||
sudo -u git -H bundle exec rake gitlab:satellites:create RAILS_ENV=production
|
||||
sudo chmod u+rwx,g=rx,o-rwx /home/git/gitlab-satellites
|
||||
```
|
||||
|
||||
## Rebuild authorized_keys file
|
||||
|
||||
In some case it is necessary to rebuild the `authorized_keys` file.
|
||||
|
||||
@@ -16,6 +16,6 @@ class Spinach::Features::DashboardHelp < Spinach::FeatureSteps
|
||||
end
|
||||
|
||||
step 'Header "Rebuild project satellites" should have correct ids and links' do
|
||||
header_should_have_correct_id_and_link(2, '(Re-)Create satellite repositories', 're-create-satellite-repositories', '.documentation')
|
||||
header_should_have_correct_id_and_link(2, 'Check GitLab configuration', 'check-gitlab-configuration', '.documentation')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,7 +66,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
|
||||
|
||||
def authored_merge_request
|
||||
@authored_merge_request ||= create :merge_request,
|
||||
source_branch: 'simple_merge_request',
|
||||
source_branch: 'markdown',
|
||||
author: current_user,
|
||||
target_project: project,
|
||||
source_project: project
|
||||
@@ -74,14 +74,14 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
|
||||
|
||||
def other_merge_request
|
||||
@other_merge_request ||= create :merge_request,
|
||||
source_branch: '2_3_notes_fix',
|
||||
source_branch: 'fix',
|
||||
target_project: project,
|
||||
source_project: project
|
||||
end
|
||||
|
||||
def authored_merge_request_from_fork
|
||||
@authored_merge_request_from_fork ||= create :merge_request,
|
||||
source_branch: 'basic_page',
|
||||
source_branch: 'feature_conflict',
|
||||
author: current_user,
|
||||
target_project: public_project,
|
||||
source_project: forked_project
|
||||
@@ -89,7 +89,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
|
||||
|
||||
def assigned_merge_request_from_fork
|
||||
@assigned_merge_request_from_fork ||= create :merge_request,
|
||||
source_branch: 'basic_page_fix',
|
||||
source_branch: 'markdown',
|
||||
assignee: current_user,
|
||||
target_project: public_project,
|
||||
source_project: forked_project
|
||||
|
||||
@@ -9,7 +9,6 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
|
||||
@project = Project.find_by(name: "Shop")
|
||||
@project ||= create(:project, name: "Shop")
|
||||
@project.team << [@user, :reporter]
|
||||
@project.ensure_satellite_exists
|
||||
end
|
||||
|
||||
step 'I have a project forked off of "Shop" called "Forked Shop"' do
|
||||
|
||||
@@ -198,15 +198,10 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
|
||||
end
|
||||
|
||||
step 'merge request "Bug NS-05" is mergeable' do
|
||||
merge_request.project.satellite.create
|
||||
merge_request.mark_as_mergeable
|
||||
end
|
||||
|
||||
step 'I accept this merge request' do
|
||||
Gitlab::Satellite::MergeAction.any_instance.stub(
|
||||
merge!: true,
|
||||
)
|
||||
|
||||
page.within '.mr-state-widget' do
|
||||
click_button "Accept Merge Request"
|
||||
end
|
||||
|
||||
@@ -198,7 +198,11 @@ module API
|
||||
|
||||
if merge_request.open? && !merge_request.work_in_progress?
|
||||
if merge_request.can_be_merged?
|
||||
merge_request.automerge!(current_user, params[:merge_commit_message] || merge_request.merge_commit_message)
|
||||
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)
|
||||
|
||||
present merge_request, with: Entities::MergeRequest
|
||||
else
|
||||
render_api_error!('Branch cannot be merged', 405)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
require 'gitlab/git'
|
||||
|
||||
module Gitlab
|
||||
autoload :Satellite, 'gitlab/satellite/satellite'
|
||||
end
|
||||
|
||||
@@ -217,20 +217,6 @@ 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
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
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
|
||||
@@ -1,44 +0,0 @@
|
||||
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
|
||||
@@ -1,13 +0,0 @@
|
||||
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
|
||||
@@ -1,146 +0,0 @@
|
||||
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
|
||||
@@ -1,148 +0,0 @@
|
||||
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
|
||||
@@ -25,7 +25,6 @@ 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
|
||||
@@ -238,37 +237,6 @@ 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? ... "
|
||||
|
||||
@@ -339,7 +307,6 @@ 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
|
||||
|
||||
@@ -417,29 +384,6 @@ 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
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
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
|
||||
@@ -25,7 +25,7 @@ describe Gitlab::ReferenceExtractor do
|
||||
project.team << [@u_bar, :guest]
|
||||
|
||||
subject.analyze(%Q{
|
||||
Inline code: `@foo`
|
||||
Inline code: `@foo`
|
||||
|
||||
Code block:
|
||||
|
||||
@@ -33,7 +33,7 @@ describe Gitlab::ReferenceExtractor do
|
||||
@bar
|
||||
```
|
||||
|
||||
Quote:
|
||||
Quote:
|
||||
|
||||
> @offteam
|
||||
})
|
||||
@@ -49,8 +49,8 @@ describe Gitlab::ReferenceExtractor do
|
||||
end
|
||||
|
||||
it 'accesses valid merge requests' do
|
||||
@m0 = create(:merge_request, source_project: project, target_project: project, source_branch: 'aaa')
|
||||
@m1 = create(:merge_request, source_project: project, target_project: project, source_branch: 'bbb')
|
||||
@m0 = create(:merge_request, source_project: project, target_project: project, source_branch: 'markdown')
|
||||
@m1 = create(:merge_request, source_project: project, target_project: project, source_branch: 'feature_conflict')
|
||||
|
||||
subject.analyze("!999, !#{@m1.iid}, and !#{@m0.iid}.")
|
||||
expect(subject.merge_requests).to eq([@m1, @m0])
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Gitlab::Satellite::Action' do
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
describe '#prepare_satellite!' do
|
||||
it 'should be able to fetch timeout from conf' do
|
||||
expect(Gitlab::Satellite::Action::DEFAULT_OPTIONS[:git_timeout]).to eq(30.seconds)
|
||||
end
|
||||
|
||||
it 'create a repository with a parking branch and one remote: origin' do
|
||||
repo = project.satellite.repo
|
||||
|
||||
#now lets dirty it up
|
||||
|
||||
starting_remote_count = repo.git.list_remotes.size
|
||||
expect(starting_remote_count).to be >= 1
|
||||
#kind of hookey way to add a second remote
|
||||
origin_uri = repo.git.remote({ v: true }).split(" ")[1]
|
||||
|
||||
repo.git.remote({ raise: true }, 'add', 'another-remote', origin_uri)
|
||||
repo.git.branch({ raise: true }, 'a-new-branch')
|
||||
|
||||
expect(repo.heads.size).to be > (starting_remote_count)
|
||||
expect(repo.git.remote().split(" ").size).to be > (starting_remote_count)
|
||||
|
||||
repo.git.config({}, "user.name", "#{user.name} -- foo")
|
||||
repo.git.config({}, "user.email", "#{user.email} -- foo")
|
||||
expect(repo.config['user.name']).to eq("#{user.name} -- foo")
|
||||
expect(repo.config['user.email']).to eq("#{user.email} -- foo")
|
||||
|
||||
|
||||
#These must happen in the context of the satellite directory...
|
||||
satellite_action = Gitlab::Satellite::Action.new(user, project)
|
||||
project.satellite.lock do
|
||||
#Now clean it up, use send to get around prepare_satellite! being protected
|
||||
satellite_action.send(:prepare_satellite!, repo)
|
||||
end
|
||||
|
||||
#verify it's clean
|
||||
heads = repo.heads.map(&:name)
|
||||
expect(heads.size).to eq(1)
|
||||
expect(heads.include?(Gitlab::Satellite::Satellite::PARKING_BRANCH)).to eq(true)
|
||||
remotes = repo.git.remote().split(' ')
|
||||
expect(remotes.size).to eq(1)
|
||||
expect(remotes.include?('origin')).to eq(true)
|
||||
expect(repo.config['user.name']).to eq(user.name)
|
||||
expect(repo.config['user.email']).to eq(user.email)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#in_locked_and_timed_satellite' do
|
||||
|
||||
it 'should make use of a lockfile' do
|
||||
repo = project.satellite.repo
|
||||
called = false
|
||||
|
||||
#set assumptions
|
||||
FileUtils.rm_f(project.satellite.lock_file)
|
||||
|
||||
expect(File.exists?(project.satellite.lock_file)).to be_falsey
|
||||
|
||||
satellite_action = Gitlab::Satellite::Action.new(user, project)
|
||||
satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo|
|
||||
expect(repo).to eq(sat_repo)
|
||||
expect(File.exists? project.satellite.lock_file).to be_truthy
|
||||
called = true
|
||||
end
|
||||
|
||||
expect(called).to be_truthy
|
||||
|
||||
end
|
||||
|
||||
it 'should be able to use the satellite after locking' do
|
||||
repo = project.satellite.repo
|
||||
called = false
|
||||
|
||||
# Set base assumptions
|
||||
if File.exists? project.satellite.lock_file
|
||||
expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey
|
||||
end
|
||||
|
||||
satellite_action = Gitlab::Satellite::Action.new(user, project)
|
||||
satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo|
|
||||
called = true
|
||||
expect(repo).to eq(sat_repo)
|
||||
expect(File.exists? project.satellite.lock_file).to be_truthy
|
||||
expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_truthy
|
||||
end
|
||||
|
||||
expect(called).to be_truthy
|
||||
expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey
|
||||
|
||||
end
|
||||
|
||||
class FileLockStatusChecker < File
|
||||
def flocked?(&block)
|
||||
status = flock LOCK_EX|LOCK_NB
|
||||
case status
|
||||
when false
|
||||
return true
|
||||
when 0
|
||||
begin
|
||||
block ? block.call : false
|
||||
ensure
|
||||
flock LOCK_UN
|
||||
end
|
||||
else
|
||||
raise SystemCallError, status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,104 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Gitlab::Satellite::MergeAction' do
|
||||
include RepoHelpers
|
||||
|
||||
let(:project) { create(:project, namespace: create(:group)) }
|
||||
let(:fork_project) { create(:project, namespace: create(:group), forked_from_project: project) }
|
||||
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let(:merge_request_fork) { create(:merge_request, source_project: fork_project, target_project: project) }
|
||||
|
||||
let(:merge_request_with_conflict) { create(:merge_request, :conflict, source_project: project, target_project: project) }
|
||||
let(:merge_request_fork_with_conflict) { create(:merge_request, :conflict, source_project: project, target_project: project) }
|
||||
|
||||
describe '#commits_between' do
|
||||
def verify_commits(commits, first_commit_sha, last_commit_sha)
|
||||
commits.each { |commit| expect(commit.class).to eq(Gitlab::Git::Commit) }
|
||||
expect(commits.first.id).to eq(first_commit_sha)
|
||||
expect(commits.last.id).to eq(last_commit_sha)
|
||||
end
|
||||
|
||||
context 'on fork' do
|
||||
it 'should get proper commits between' do
|
||||
commits = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).commits_between
|
||||
verify_commits(commits, sample_compare.commits.first, sample_compare.commits.last)
|
||||
end
|
||||
end
|
||||
|
||||
context 'between branches' do
|
||||
it 'should raise exception -- not expected to be used by non forks' do
|
||||
expect { Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between }.to raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#format_patch' do
|
||||
def verify_content(patch)
|
||||
sample_compare.commits.each do |commit|
|
||||
expect(patch.include?(commit)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'on fork' do
|
||||
it 'should build a format patch' do
|
||||
patch = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).format_patch
|
||||
verify_content(patch)
|
||||
end
|
||||
end
|
||||
|
||||
context 'between branches' do
|
||||
it 'should build a format patch' do
|
||||
patch = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request).format_patch
|
||||
verify_content(patch)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#diffs_between_satellite tested against diff_in_satellite' do
|
||||
def is_a_matching_diff(diff, diffs)
|
||||
diff_count = diff.scan('diff --git').size
|
||||
expect(diff_count).to be >= 1
|
||||
expect(diffs.size).to eq(diff_count)
|
||||
diffs.each do |a_diff|
|
||||
expect(a_diff.class).to eq(Gitlab::Git::Diff)
|
||||
expect(diff.include? a_diff.diff).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'on fork' do
|
||||
it 'should get proper diffs' do
|
||||
diffs = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).diffs_between_satellite
|
||||
diff = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request_fork).diff_in_satellite
|
||||
is_a_matching_diff(diff, diffs)
|
||||
end
|
||||
end
|
||||
|
||||
context 'between branches' do
|
||||
it 'should get proper diffs' do
|
||||
expect{ Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite }.to raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#can_be_merged?' do
|
||||
context 'on fork' do
|
||||
it do
|
||||
expect(Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).can_be_merged?).to be_truthy
|
||||
end
|
||||
|
||||
it do
|
||||
expect(Gitlab::Satellite::MergeAction.new(merge_request_fork_with_conflict.author, merge_request_fork_with_conflict).can_be_merged?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'between branches' do
|
||||
it do
|
||||
expect(Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).can_be_merged?).to be_truthy
|
||||
end
|
||||
|
||||
it do
|
||||
expect(Gitlab::Satellite::MergeAction.new(merge_request_with_conflict.author, merge_request_with_conflict).can_be_merged?).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -165,7 +165,7 @@ describe MergeRequest do
|
||||
end
|
||||
|
||||
it_behaves_like 'an editable mentionable' do
|
||||
subject { create(:merge_request, source_project: project) }
|
||||
subject { create(:merge_request) }
|
||||
|
||||
let(:backref_text) { "merge request #{subject.to_reference}" }
|
||||
let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
|
||||
|
||||
@@ -67,7 +67,7 @@ describe SlackService do
|
||||
opts = {
|
||||
title: 'Awesome merge_request',
|
||||
description: 'please fix',
|
||||
source_branch: 'stable',
|
||||
source_branch: 'feature',
|
||||
target_branch: 'master'
|
||||
}
|
||||
merge_service = MergeRequests::CreateService.new(project,
|
||||
|
||||
@@ -91,7 +91,6 @@ describe Project do
|
||||
describe 'Respond to' do
|
||||
it { is_expected.to respond_to(:url_to_repo) }
|
||||
it { is_expected.to respond_to(:repo_exists?) }
|
||||
it { is_expected.to respond_to(:satellite) }
|
||||
it { is_expected.to respond_to(:update_merge_requests) }
|
||||
it { is_expected.to respond_to(:execute_hooks) }
|
||||
it { is_expected.to respond_to(:name_with_namespace) }
|
||||
|
||||
@@ -36,13 +36,13 @@ describe Repository do
|
||||
|
||||
describe :can_be_merged? do
|
||||
context 'mergeable branches' do
|
||||
subject { repository.can_be_merged?('feature', 'master') }
|
||||
subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'non-mergeable branches' do
|
||||
subject { repository.can_be_merged?('feature_conflict', 'feature') }
|
||||
subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
@@ -110,7 +110,7 @@ describe API::API, api: true do
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it "should return a 400 if satellite fails to create file" do
|
||||
it "should return a 400 if fails to create file" do
|
||||
allow_any_instance_of(Repository).to receive(:remove_file).and_return(false)
|
||||
|
||||
delete api("/projects/#{project.id}/repository/files", user), valid_params
|
||||
|
||||
@@ -148,7 +148,7 @@ describe API::API, api: true do
|
||||
it "should return merge_request" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: 'Test merge_request',
|
||||
source_branch: 'stable',
|
||||
source_branch: 'feature_conflict',
|
||||
target_branch: 'master',
|
||||
author: user,
|
||||
labels: 'label, label2'
|
||||
@@ -171,20 +171,20 @@ describe API::API, api: true do
|
||||
|
||||
it "should return 400 when target_branch is missing" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: "Test merge_request", source_branch: "stable", author: user
|
||||
title: "Test merge_request", source_branch: "markdown", author: user
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it "should return 400 when title is missing" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
target_branch: 'master', source_branch: 'stable'
|
||||
target_branch: 'master', source_branch: 'markdown'
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it 'should return 400 on invalid label names' do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: 'Test merge_request',
|
||||
source_branch: 'stable',
|
||||
source_branch: 'markdown',
|
||||
target_branch: 'master',
|
||||
author: user,
|
||||
labels: 'label, ?'
|
||||
@@ -198,7 +198,7 @@ describe API::API, api: true do
|
||||
before do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: 'Test merge_request',
|
||||
source_branch: 'stable',
|
||||
source_branch: 'feature_conflict',
|
||||
target_branch: 'master',
|
||||
author: user
|
||||
@mr = MergeRequest.all.last
|
||||
@@ -208,7 +208,7 @@ describe API::API, api: true do
|
||||
expect do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: 'New test merge_request',
|
||||
source_branch: 'stable',
|
||||
source_branch: 'feature_conflict',
|
||||
target_branch: 'master',
|
||||
author: user
|
||||
end.to change { MergeRequest.count }.by(0)
|
||||
@@ -228,7 +228,8 @@ describe API::API, api: true do
|
||||
|
||||
it "should return merge_request" do
|
||||
post api("/projects/#{fork_project.id}/merge_requests", user2),
|
||||
title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request'
|
||||
title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master",
|
||||
author: user2, target_project_id: project.id, description: 'Test description for Test merge_request'
|
||||
expect(response.status).to eq(201)
|
||||
expect(json_response['title']).to eq('Test merge_request')
|
||||
expect(json_response['description']).to eq('Test description for Test merge_request')
|
||||
@@ -258,7 +259,7 @@ describe API::API, api: true do
|
||||
|
||||
it "should return 400 when title is missing" do
|
||||
post api("/projects/#{fork_project.id}/merge_requests", user2),
|
||||
target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: project.id
|
||||
target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: project.id
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
@@ -267,7 +268,7 @@ describe API::API, api: true do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: 'Test merge_request',
|
||||
target_branch: 'master',
|
||||
source_branch: 'stable',
|
||||
source_branch: 'markdown',
|
||||
author: user,
|
||||
target_project_id: fork_project.id
|
||||
expect(response.status).to eq(422)
|
||||
@@ -277,7 +278,7 @@ describe API::API, api: true do
|
||||
post api("/projects/#{fork_project.id}/merge_requests", user2),
|
||||
title: 'Test merge_request',
|
||||
target_branch: 'master',
|
||||
source_branch: 'stable',
|
||||
source_branch: 'markdown',
|
||||
author: user2,
|
||||
target_project_id: unrelated_project.id
|
||||
expect(response.status).to eq(422)
|
||||
@@ -286,7 +287,7 @@ describe API::API, api: true do
|
||||
|
||||
it "should return 201 when target_branch is specified and for the same project" do
|
||||
post api("/projects/#{fork_project.id}/merge_requests", user2),
|
||||
title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: fork_project.id
|
||||
title: 'Test merge_request', target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: fork_project.id
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
end
|
||||
@@ -302,9 +303,6 @@ describe API::API, api: true do
|
||||
|
||||
describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do
|
||||
it "should return merge_request in case of success" do
|
||||
allow_any_instance_of(MergeRequest).
|
||||
to receive_messages(can_be_merged?: true, automerge!: true)
|
||||
|
||||
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
@@ -210,8 +210,8 @@ end
|
||||
|
||||
# diffs_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/diffs(.:format) projects/merge_requests#diffs
|
||||
# commits_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/commits(.:format) projects/merge_requests#commits
|
||||
# automerge_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/automerge(.:format) projects/merge_requests#automerge
|
||||
# automerge_check_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/automerge_check(.:format) projects/merge_requests#automerge_check
|
||||
# merge_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/merge(.:format) projects/merge_requests#merge
|
||||
# merge_check_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/merge_check(.:format) projects/merge_requests#merge_check
|
||||
# ci_status_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/ci_status(.:format) projects/merge_requests#ci_status
|
||||
# toggle_subscription_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/toggle_subscription(.:format) projects/merge_requests#toggle_subscription
|
||||
# branch_from_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/branch_from(.:format) projects/merge_requests#branch_from
|
||||
@@ -233,15 +233,15 @@ describe Projects::MergeRequestsController, 'routing' do
|
||||
expect(get('/gitlab/gitlabhq/merge_requests/1/commits')).to route_to('projects/merge_requests#commits', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
|
||||
end
|
||||
|
||||
it 'to #automerge' do
|
||||
expect(post('/gitlab/gitlabhq/merge_requests/1/automerge')).to route_to(
|
||||
'projects/merge_requests#automerge',
|
||||
it 'to #merge' do
|
||||
expect(post('/gitlab/gitlabhq/merge_requests/1/merge')).to route_to(
|
||||
'projects/merge_requests#merge',
|
||||
namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1'
|
||||
)
|
||||
end
|
||||
|
||||
it 'to #automerge_check' do
|
||||
expect(get('/gitlab/gitlabhq/merge_requests/1/automerge_check')).to route_to('projects/merge_requests#automerge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
|
||||
it 'to #merge_check' do
|
||||
expect(get('/gitlab/gitlabhq/merge_requests/1/merge_check')).to route_to('projects/merge_requests#merge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
|
||||
end
|
||||
|
||||
it 'to #branch_from' do
|
||||
|
||||
@@ -10,7 +10,7 @@ describe MergeRequests::CreateService do
|
||||
{
|
||||
title: 'Awesome merge_request',
|
||||
description: 'please fix',
|
||||
source_branch: 'stable',
|
||||
source_branch: 'feature',
|
||||
target_branch: 'master'
|
||||
}
|
||||
end
|
||||
|
||||
@@ -24,11 +24,6 @@ describe MergeRequests::MergeService do
|
||||
it { expect(merge_request).to be_valid }
|
||||
it { expect(merge_request).to be_merged }
|
||||
|
||||
it 'should execute hooks with merge action' do
|
||||
expect(service).to have_received(:execute_hooks).
|
||||
with(merge_request, 'merge')
|
||||
end
|
||||
|
||||
it 'should send email to user2 about merge of new merge_request' do
|
||||
email = ActionMailer::Base.deliveries.last
|
||||
expect(email.to.first).to eq(user2.email)
|
||||
|
||||
@@ -9,7 +9,7 @@ def common_mentionable_setup
|
||||
let(:author) { subject.author }
|
||||
|
||||
let(:mentioned_issue) { create(:issue, project: project) }
|
||||
let(:mentioned_mr) { create(:merge_request, :simple, source_project: project) }
|
||||
let!(:mentioned_mr) { create(:merge_request, :simple, source_project: project) }
|
||||
let(:mentioned_commit) { project.commit }
|
||||
|
||||
let(:ext_proj) { create(:project, :public) }
|
||||
|
||||
Reference in New Issue
Block a user