summaryrefslogtreecommitdiff
path: root/app/services
diff options
context:
space:
mode:
Diffstat (limited to 'app/services')
-rw-r--r--app/services/delete_user_service.rb16
-rw-r--r--app/services/destroy_group_service.rb17
-rw-r--r--app/services/files/base_service.rb7
-rw-r--r--app/services/files/create_service.rb22
-rw-r--r--app/services/files/delete_service.rb15
-rw-r--r--app/services/files/update_service.rb20
-rw-r--r--app/services/git_push_service.rb3
-rw-r--r--app/services/issuable_base_service.rb11
-rw-r--r--app/services/issues/update_service.rb4
-rw-r--r--app/services/merge_requests/update_service.rb18
-rw-r--r--app/services/notes/create_service.rb2
-rw-r--r--app/services/projects/destroy_service.rb67
-rw-r--r--app/services/system_note_service.rb98
13 files changed, 222 insertions, 78 deletions
diff --git a/app/services/delete_user_service.rb b/app/services/delete_user_service.rb
new file mode 100644
index 00000000000..9017a63af3b
--- /dev/null
+++ b/app/services/delete_user_service.rb
@@ -0,0 +1,16 @@
+class DeleteUserService
+ def execute(user)
+ if user.solo_owned_groups.present?
+ user.errors[:base] << 'You must transfer ownership or delete groups before you can remove user'
+ user
+ else
+ user.personal_projects.each do |project|
+ # Skip repository removal because we remove directory with namespace
+ # that contain all this repositories
+ ::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
+ end
+
+ user.destroy
+ end
+ end
+end
diff --git a/app/services/destroy_group_service.rb b/app/services/destroy_group_service.rb
new file mode 100644
index 00000000000..d929a676293
--- /dev/null
+++ b/app/services/destroy_group_service.rb
@@ -0,0 +1,17 @@
+class DestroyGroupService
+ attr_accessor :group, :current_user
+
+ def initialize(group, user)
+ @group, @current_user = group, user
+ end
+
+ def execute
+ @group.projects.each do |project|
+ # Skip repository removal because we remove directory with namespace
+ # that contain all this repositories
+ ::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
+ end
+
+ @group.destroy
+ end
+end
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index bd245100955..4d02752454e 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -13,5 +13,12 @@ module Files
def repository
project.repository
end
+
+ def after_commit(sha)
+ commit = repository.commit(sha)
+ full_ref = 'refs/heads/' + (params[:new_branch] || ref)
+ old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA
+ GitPushService.new.execute(project, current_user, old_sha, sha, full_ref)
+ end
end
end
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index 23833aa78ec..0a80455bc6b 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -1,7 +1,7 @@
require_relative "base_service"
module Files
- class CreateService < BaseService
+ class CreateService < Files::BaseService
def execute
allowed = Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
@@ -33,16 +33,24 @@ module Files
end
end
+ content =
+ if params[:encoding] == 'base64'
+ Base64.decode64(params[:content])
+ else
+ params[:content]
+ end
- new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
- created_successfully = new_file_action.commit!(
- params[:content],
+ sha = repository.commit_file(
+ current_user,
+ file_path,
+ content,
params[:commit_message],
- params[:encoding],
- params[:new_branch]
+ params[:new_branch] || ref
)
- if created_successfully
+
+ if sha
+ after_commit(sha)
success
else
error("Your changes could not be committed, because the file has been changed")
diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb
index 1497a0f883b..2281777604c 100644
--- a/app/services/files/delete_service.rb
+++ b/app/services/files/delete_service.rb
@@ -1,7 +1,7 @@
require_relative "base_service"
module Files
- class DeleteService < BaseService
+ class DeleteService < Files::BaseService
def execute
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
@@ -19,14 +19,15 @@ module Files
return error("You can only edit text files")
end
- delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path)
-
- deleted_successfully = delete_file_action.commit!(
- nil,
- params[:commit_message]
+ sha = repository.remove_file(
+ current_user,
+ path,
+ params[:commit_message],
+ ref
)
- if deleted_successfully
+ if sha
+ after_commit(sha)
success
else
error("Your changes could not be committed, because the file has been changed")
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index 0724d3ae634..013cc1ee322 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -1,7 +1,7 @@
require_relative "base_service"
module Files
- class UpdateService < BaseService
+ class UpdateService < Files::BaseService
def execute
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
@@ -19,14 +19,22 @@ module Files
return error("You can only edit text files")
end
- edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
- edit_file_action.commit!(
- params[:content],
+ content =
+ if params[:encoding] == 'base64'
+ Base64.decode64(params[:content])
+ else
+ params[:content]
+ end
+
+ sha = repository.commit_file(
+ current_user,
+ path,
+ content,
params[:commit_message],
- params[:encoding],
- params[:new_branch]
+ params[:new_branch] || ref
)
+ after_commit(sha)
success
rescue Gitlab::Satellite::CheckoutFailed => ex
error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400)
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index bdf36af02fd..cde65349d5c 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -127,7 +127,8 @@ class GitPushService
end
def is_default_branch?(ref)
- Gitlab::Git.branch_ref?(ref) && Gitlab::Git.ref_name(ref) == project.default_branch
+ Gitlab::Git.branch_ref?(ref) &&
+ (Gitlab::Git.ref_name(ref) == project.default_branch || project.default_branch.nil?)
end
def commit_user(commit)
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 8960235b093..1d99223cfe6 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -15,4 +15,15 @@ class IssuableBaseService < BaseService
SystemNoteService.change_label(
issuable, issuable.project, current_user, added_labels, removed_labels)
end
+
+ def create_title_change_note(issuable, old_title)
+ SystemNoteService.change_title(
+ issuable, issuable.project, current_user, old_title)
+ end
+
+ def create_branch_change_note(issuable, branch_type, old_branch, new_branch)
+ SystemNoteService.change_branch(
+ issuable, issuable.project, current_user, branch_type,
+ old_branch, new_branch)
+ end
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 8f04a69287a..6af942a5ca4 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -37,6 +37,10 @@ module Issues
notification_service.reassigned_issue(issue, current_user)
end
+ if issue.previous_changes.include?('title')
+ create_title_change_note(issue, issue.previous_changes['title'].first)
+ end
+
issue.notice_added_references(issue.project, current_user)
execute_hooks(issue, 'update')
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 23af2656c37..4f6c6cba9a9 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -5,10 +5,11 @@ require_relative 'close_service'
module MergeRequests
class UpdateService < MergeRequests::BaseService
def execute(merge_request)
- # We dont allow change of source/target projects
+ # We don't allow change of source/target projects and source branch
# after merge request was created
params.except!(:source_project_id)
params.except!(:target_project_id)
+ params.except!(:source_branch)
state = params[:state_event]
@@ -41,6 +42,12 @@ module MergeRequests
)
end
+ if merge_request.previous_changes.include?('target_branch')
+ create_branch_change_note(merge_request, 'target',
+ merge_request.previous_changes['target_branch'].first,
+ merge_request.target_branch)
+ end
+
if merge_request.previous_changes.include?('milestone_id')
create_milestone_note(merge_request)
end
@@ -50,6 +57,15 @@ module MergeRequests
notification_service.reassigned_merge_request(merge_request, current_user)
end
+ if merge_request.previous_changes.include?('title')
+ create_title_change_note(merge_request, merge_request.previous_changes['title'].first)
+ end
+
+ if merge_request.previous_changes.include?('target_branch') ||
+ merge_request.previous_changes.include?('source_branch')
+ merge_request.mark_as_unchecked
+ end
+
merge_request.notice_added_references(merge_request.project, current_user)
execute_hooks(merge_request, 'update')
end
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index d19a6c2eca3..0ff37c41743 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -31,7 +31,7 @@ module Notes
def execute_hooks(note)
note_data = hook_data(note)
- # TODO: Support Webhooks
+ note.project.execute_hooks(note_data, :note_hooks)
note.project.execute_services(note_data, :note_hooks)
end
end
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 7e1d753b021..403f419ec50 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -1,28 +1,69 @@
module Projects
class DestroyService < BaseService
+ include Gitlab::ShellAdapter
+
+ class DestroyError < StandardError; end
+
+ DELETED_FLAG = '+deleted'
+
def execute
return false unless can?(current_user, :remove_project, project)
project.team.truncate
project.repository.expire_cache unless project.empty_repo?
- if project.destroy
- GitlabShellWorker.perform_async(
- :remove_repository,
- project.path_with_namespace
- )
+ repo_path = project.path_with_namespace
+ wiki_path = repo_path + '.wiki'
- GitlabShellWorker.perform_async(
- :remove_repository,
- project.path_with_namespace + ".wiki"
- )
+ Project.transaction do
+ project.destroy!
- project.satellite.destroy
+ unless remove_repository(repo_path)
+ raise_error('Failed to remove project repository. Please try again or contact administrator')
+ end
- log_info("Project \"#{project.name}\" was removed")
- system_hook_service.execute_hooks_for(project, :destroy)
- true
+ unless remove_repository(wiki_path)
+ raise_error('Failed to remove wiki repository. Please try again or contact administrator')
+ end
end
+
+ project.satellite.destroy
+ log_info("Project \"#{project.name}\" was removed")
+ system_hook_service.execute_hooks_for(project, :destroy)
+ true
+ end
+
+ private
+
+ def remove_repository(path)
+ # Skip repository removal. We use this flag when remove user or group
+ return true if params[:skip_repo] == true
+
+ # There is a possibility project does not have repository or wiki
+ return true unless gitlab_shell.exists?(path + '.git')
+
+ new_path = removal_path(path)
+
+ if gitlab_shell.mv_repository(path, new_path)
+ log_info("Repository \"#{path}\" moved to \"#{new_path}\"")
+ GitlabShellWorker.perform_in(5.minutes, :remove_repository, new_path)
+ else
+ false
+ end
+ end
+
+ def raise_error(message)
+ raise DestroyError.new(message)
+ end
+
+ # Build a path for removing repositories
+ # We use `+` because its not allowed by GitLab so user can not create
+ # project with name cookies+119+deleted and capture someone stalled repository
+ #
+ # gitlab/cookies.git -> gitlab/cookies+119+deleted.git
+ #
+ def removal_path(path)
+ "#{path}+#{project.id}#{DELETED_FLAG}"
end
end
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 0614f8689a4..b6801a92330 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -10,7 +10,7 @@ class SystemNoteService
# author - User performing the change
# new_commits - Array of Commits added since last push
# existing_commits - Array of Commits added in a previous push
- # oldrev - TODO (rspeicher): I have no idea what this actually does
+ # oldrev - Optional String SHA of a previous Commit
#
# See new_commit_summary and existing_commit_summary.
#
@@ -130,6 +130,44 @@ class SystemNoteService
create_note(noteable: noteable, project: project, author: author, note: body)
end
+ # Called when the title of a Noteable is changed
+ #
+ # noteable - Noteable object that responds to `title`
+ # project - Project owning noteable
+ # author - User performing the change
+ # old_title - Previous String title
+ #
+ # Example Note text:
+ #
+ # "Title changed from **Old** to **New**"
+ #
+ # Returns the created Note object
+ def self.change_title(noteable, project, author, old_title)
+ return unless noteable.respond_to?(:title)
+
+ body = "Title changed from **#{old_title}** to **#{noteable.title}**"
+ create_note(noteable: noteable, project: project, author: author, note: body)
+ end
+
+ # Called when a branch in Noteable is changed
+ #
+ # noteable - Noteable object
+ # project - Project owning noteable
+ # author - User performing the change
+ # branch_type - 'source' or 'target'
+ # old_branch - old branch name
+ # new_branch - new branch nmae
+ #
+ # Example Note text:
+ #
+ # "Target branch changed from `Old` to `New`"
+ #
+ # Returns the created Note object
+ def self.change_branch(noteable, project, author, branch_type, old_branch, new_branch)
+ body = "#{branch_type} branch changed from `#{old_branch}` to `#{new_branch}`".capitalize
+ create_note(noteable: noteable, project: project, author: author, note: body)
+ end
+
# Called when a Mentionable references a Noteable
#
# noteable - Noteable object being referenced
@@ -138,11 +176,11 @@ class SystemNoteService
#
# Example Note text:
#
- # "Mentioned in #1"
+ # "mentioned in #1"
#
- # "Mentioned in !2"
+ # "mentioned in !2"
#
- # "Mentioned in 54f7727c"
+ # "mentioned in 54f7727c"
#
# See cross_reference_note_content.
#
@@ -150,7 +188,7 @@ class SystemNoteService
def self.cross_reference(noteable, mentioner, author)
return if cross_reference_disallowed?(noteable, mentioner)
- gfm_reference = mentioner_gfm_ref(noteable, mentioner)
+ gfm_reference = mentioner.gfm_reference(noteable.project)
note_options = {
project: noteable.project,
@@ -181,12 +219,21 @@ class SystemNoteService
#
# Returns Boolean
def self.cross_reference_disallowed?(noteable, mentioner)
- return false unless MergeRequest === mentioner
- return false unless Commit === noteable
+ return false unless mentioner.is_a?(MergeRequest)
+ return false unless noteable.is_a?(Commit)
mentioner.commits.include?(noteable)
end
+ # Check if a cross reference to a noteable from a mentioner already exists
+ #
+ # This method is used to prevent multiple notes being created for a mention
+ # when a issue is updated, for example.
+ #
+ # noteable - Noteable object being referenced
+ # mentioner - Mentionable object
+ #
+ # Returns Boolean
def self.cross_reference_exists?(noteable, mentioner)
# Initial scope should be system notes of this noteable type
notes = Note.system.where(noteable_type: noteable.class)
@@ -198,7 +245,7 @@ class SystemNoteService
notes = notes.where(noteable_id: noteable.id)
end
- gfm_reference = mentioner_gfm_ref(noteable, mentioner, true)
+ gfm_reference = mentioner.gfm_reference(noteable.project)
notes = notes.where(note: cross_reference_note_content(gfm_reference))
notes.count > 0
@@ -210,39 +257,6 @@ class SystemNoteService
Note.create(args.merge(system: true))
end
- # Prepend the mentioner's namespaced project path to the GFM reference for
- # cross-project references. For same-project references, return the
- # unmodified GFM reference.
- def self.mentioner_gfm_ref(noteable, mentioner, cross_reference = false)
- # FIXME (rspeicher): This was breaking things.
- # if mentioner.is_a?(Commit) && cross_reference
- # return mentioner.gfm_reference.sub('commit ', 'commit %')
- # end
-
- full_gfm_reference(mentioner.project, noteable.project, mentioner)
- end
-
- # Return the +mentioner+ GFM reference. If the mentioner and noteable
- # projects are not the same, add the mentioning project's path to the
- # returned value.
- def self.full_gfm_reference(mentioning_project, noteable_project, mentioner)
- if mentioning_project == noteable_project
- mentioner.gfm_reference
- else
- if mentioner.is_a?(Commit)
- mentioner.gfm_reference.sub(
- /(commit )/,
- "\\1#{mentioning_project.path_with_namespace}@"
- )
- else
- mentioner.gfm_reference.sub(
- /(issue |merge request )/,
- "\\1#{mentioning_project.path_with_namespace}"
- )
- end
- end
- end
-
def self.cross_reference_note_prefix
'mentioned in '
end
@@ -267,7 +281,7 @@ class SystemNoteService
#
# noteable - MergeRequest object
# existing_commits - Array of existing Commit objects
- # oldrev - Optional String SHA of ... TODO (rspeicher): I have no idea what this actually does.
+ # oldrev - Optional String SHA of a previous Commit
#
# Examples:
#