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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/services')
-rw-r--r--app/services/ci/create_pipeline_service.rb81
-rw-r--r--app/services/ci/create_trigger_request_service.rb13
-rw-r--r--app/services/ci/pipeline_trigger_service.rb44
-rw-r--r--app/services/delete_merged_branches_service.rb2
-rw-r--r--app/services/git_operation_service.rb2
-rw-r--r--app/services/git_push_service.rb39
-rw-r--r--app/services/groups/destroy_service.rb2
-rw-r--r--app/services/issuable_base_service.rb1
-rw-r--r--app/services/issues/base_service.rb8
-rw-r--r--app/services/issues/close_service.rb4
-rw-r--r--app/services/issues/duplicate_service.rb24
-rw-r--r--app/services/issues/reopen_service.rb6
-rw-r--r--app/services/issues/update_service.rb18
-rw-r--r--app/services/merge_requests/base_service.rb6
-rw-r--r--app/services/merge_requests/reopen_service.rb2
-rw-r--r--app/services/notification_service.rb10
-rw-r--r--app/services/projects/destroy_service.rb76
-rw-r--r--app/services/projects/import_export/export_service.rb2
-rw-r--r--app/services/projects/import_service.rb4
-rw-r--r--app/services/projects/transfer_service.rb4
-rw-r--r--app/services/projects/update_pages_service.rb6
-rw-r--r--app/services/projects/update_service.rb9
-rw-r--r--app/services/quick_actions/interpret_service.rb34
-rw-r--r--app/services/system_hooks_service.rb8
-rw-r--r--app/services/system_note_service.rb38
-rw-r--r--app/services/web_hook_service.rb10
-rw-r--r--app/services/wiki_pages/update_service.rb2
27 files changed, 369 insertions, 86 deletions
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 273386776fa..884b681ff81 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -15,12 +15,48 @@ module Ci
pipeline_schedule: schedule
)
+ result = validate(current_user || trigger_request.trigger.owner,
+ ignore_skip_ci: ignore_skip_ci,
+ save_on_errors: save_on_errors)
+
+ return result if result
+
+ begin
+ Ci::Pipeline.transaction do
+ pipeline.save!
+
+ yield(pipeline) if block_given?
+
+ Ci::CreatePipelineStagesService
+ .new(project, current_user)
+ .execute(pipeline)
+ end
+ rescue ActiveRecord::RecordInvalid => e
+ return error("Failed to persist the pipeline: #{e}")
+ end
+
+ update_merge_requests_head_pipeline
+
+ cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
+
+ pipeline_created_counter.increment(source: source)
+
+ pipeline.tap(&:process!)
+ end
+
+ private
+
+ def validate(triggering_user, ignore_skip_ci:, save_on_errors:)
unless project.builds_enabled?
return error('Pipeline is disabled')
end
- unless trigger_request || can?(current_user, :create_pipeline, project)
- return error('Insufficient permissions to create a new pipeline')
+ unless allowed_to_trigger_pipeline?(triggering_user)
+ if can?(triggering_user, :create_pipeline, project)
+ return error("Insufficient permissions for protected ref '#{ref}'")
+ else
+ return error('Insufficient permissions to create a new pipeline')
+ end
end
unless branch? || tag?
@@ -46,24 +82,29 @@ module Ci
unless pipeline.has_stage_seeds?
return error('No stages / jobs for this pipeline.')
end
+ end
- Ci::Pipeline.transaction do
- update_merge_requests_head_pipeline if pipeline.save
-
- Ci::CreatePipelineStagesService
- .new(project, current_user)
- .execute(pipeline)
+ def allowed_to_trigger_pipeline?(triggering_user)
+ if triggering_user
+ allowed_to_create?(triggering_user)
+ else # legacy triggers don't have a corresponding user
+ !project.protected_for?(ref)
end
+ end
- cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
+ def allowed_to_create?(triggering_user)
+ access = Gitlab::UserAccess.new(triggering_user, project: project)
- pipeline_created_counter.increment(source: source)
-
- pipeline.tap(&:process!)
+ can?(triggering_user, :create_pipeline, project) &&
+ if branch?
+ access.can_update_branch?(ref)
+ elsif tag?
+ access.can_create_tag?(ref)
+ else
+ true # Allow it for now and we'll reject when we check ref existence
+ end
end
- private
-
def update_merge_requests_head_pipeline
return unless pipeline.latest?
@@ -113,15 +154,21 @@ module Ci
end
def branch?
- project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref)
+ return @is_branch if defined?(@is_branch)
+
+ @is_branch =
+ project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref)
end
def tag?
- project.repository.ref_exists?(Gitlab::Git::TAG_REF_PREFIX + ref)
+ return @is_tag if defined?(@is_tag)
+
+ @is_tag =
+ project.repository.ref_exists?(Gitlab::Git::TAG_REF_PREFIX + ref)
end
def ref
- Gitlab::Git.ref_name(origin_ref)
+ @ref ||= Gitlab::Git.ref_name(origin_ref)
end
def valid_sha?
diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb
index cf3d4aee2bc..b2aa457bbd5 100644
--- a/app/services/ci/create_trigger_request_service.rb
+++ b/app/services/ci/create_trigger_request_service.rb
@@ -1,12 +1,19 @@
+# This class is deprecated because we're closing Ci::TriggerRequest.
+# New class is PipelineTriggerService (app/services/ci/pipeline_trigger_service.rb)
+# which is integrated with Ci::PipelineVariable instaed of Ci::TriggerRequest.
+# We remove this class after we removed v1 and v3 API. This class is still being
+# referred by such legacy code.
module Ci
- class CreateTriggerRequestService
- def execute(project, trigger, ref, variables = nil)
+ module CreateTriggerRequestService
+ Result = Struct.new(:trigger_request, :pipeline)
+
+ def self.execute(project, trigger, ref, variables = nil)
trigger_request = trigger.trigger_requests.create(variables: variables)
pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: ref)
.execute(:trigger, ignore_skip_ci: true, trigger_request: trigger_request)
- trigger_request if pipeline.persisted?
+ Result.new(trigger_request, pipeline)
end
end
end
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
new file mode 100644
index 00000000000..1e5ad28ba57
--- /dev/null
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -0,0 +1,44 @@
+module Ci
+ class PipelineTriggerService < BaseService
+ def execute
+ if trigger_from_token
+ create_pipeline_from_trigger(trigger_from_token)
+ end
+ end
+
+ private
+
+ def create_pipeline_from_trigger(trigger)
+ # this check is to not leak the presence of the project if user cannot read it
+ return unless trigger.project == project
+
+ pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: params[:ref])
+ .execute(:trigger, ignore_skip_ci: true) do |pipeline|
+ trigger.trigger_requests.create!(pipeline: pipeline)
+ create_pipeline_variables!(pipeline)
+ end
+
+ if pipeline.persisted?
+ success(pipeline: pipeline)
+ else
+ error(pipeline.errors.messages, 400)
+ end
+ end
+
+ def trigger_from_token
+ return @trigger if defined?(@trigger)
+
+ @trigger = Ci::Trigger.find_by_token(params[:token].to_s)
+ end
+
+ def create_pipeline_variables!(pipeline)
+ return unless params[:variables]
+
+ variables = params[:variables].map do |key, value|
+ { key: key, value: value }
+ end
+
+ pipeline.variables.create!(variables)
+ end
+ end
+end
diff --git a/app/services/delete_merged_branches_service.rb b/app/services/delete_merged_branches_service.rb
index 5c9e2a16c71..ff11bd59d29 100644
--- a/app/services/delete_merged_branches_service.rb
+++ b/app/services/delete_merged_branches_service.rb
@@ -11,7 +11,7 @@ class DeleteMergedBranchesService < BaseService
# Prevent deletion of branches relevant to open merge requests
branches -= merge_request_branch_names
# Prevent deletion of protected branches
- branches -= project.protected_branches.pluck(:name)
+ branches = branches.reject { |branch| project.protected_for?(branch) }
branches.each do |branch|
DeleteBranchService.new(project, current_user).execute(branch)
diff --git a/app/services/git_operation_service.rb b/app/services/git_operation_service.rb
index 32925e9c1f2..545ca0742e4 100644
--- a/app/services/git_operation_service.rb
+++ b/app/services/git_operation_service.rb
@@ -60,7 +60,7 @@ class GitOperationService
start_branch_name = nil if start_repository.empty_repo?
if start_branch_name && !start_repository.branch_exists?(start_branch_name)
- raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.path_with_namespace}"
+ raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.full_path}"
end
update_branch_with_hooks(branch_name) do
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 20d1fb29289..ada2b64a3a6 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -45,6 +45,7 @@ class GitPushService < BaseService
elsif push_to_existing_branch?
# Collect data for this git push
@push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev])
+
process_commit_messages
# Update the bare repositories info/attributes file using the contents of the default branches
@@ -56,6 +57,8 @@ class GitPushService < BaseService
perform_housekeeping
update_caches
+
+ update_signatures
end
def update_gitattributes
@@ -64,15 +67,21 @@ class GitPushService < BaseService
def update_caches
if is_default_branch?
- paths = Set.new
+ if push_to_new_branch?
+ # If this is the initial push into the default branch, the file type caches
+ # will already be reset as a result of `Project#change_head`.
+ types = []
+ else
+ paths = Set.new
- @push_commits.each do |commit|
- commit.raw_deltas.each do |diff|
- paths << diff.new_path
+ @push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
+ commit.raw_deltas.each do |diff|
+ paths << diff.new_path
+ end
end
- end
- types = Gitlab::FileDetector.types_in_paths(paths.to_a)
+ types = Gitlab::FileDetector.types_in_paths(paths.to_a)
+ end
else
types = []
end
@@ -80,11 +89,17 @@ class GitPushService < BaseService
ProjectCacheWorker.perform_async(@project.id, types, [:commit_count, :repository_size])
end
+ def update_signatures
+ @push_commits.each do |commit|
+ CreateGpgSignatureWorker.perform_async(commit.sha, @project.id)
+ end
+ end
+
# Schedules processing of commit messages.
def process_commit_messages
default = is_default_branch?
- push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
+ @push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
if commit.matches_cross_reference_regex?
ProcessCommitWorker
.perform_async(project.id, current_user.id, commit.to_hash, default)
@@ -103,7 +118,7 @@ class GitPushService < BaseService
EventCreateService.new.push(@project, current_user, build_push_data)
Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute(:push)
-
+
SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks)
@project.execute_hooks(build_push_data.dup, :push_hooks)
@project.execute_services(build_push_data.dup, :push_hooks)
@@ -123,7 +138,10 @@ class GitPushService < BaseService
end
def process_default_branch
- @push_commits = project.repository.commits(params[:newrev])
+ @push_commits_count = project.repository.commit_count_for_ref(params[:ref])
+
+ offset = [@push_commits_count - PROCESS_COMMIT_LIMIT, 0].max
+ @push_commits = project.repository.commits(params[:newrev], offset: offset, limit: PROCESS_COMMIT_LIMIT)
# Ensure HEAD points to the default branch in case it is not master
project.change_head(branch_name)
@@ -152,7 +170,8 @@ class GitPushService < BaseService
params[:oldrev],
params[:newrev],
params[:ref],
- push_commits)
+ @push_commits,
+ commits_count: @push_commits_count)
end
def push_to_existing_branch?
diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb
index 80c51cb5a72..f565612a89d 100644
--- a/app/services/groups/destroy_service.rb
+++ b/app/services/groups/destroy_service.rb
@@ -21,6 +21,8 @@ module Groups
DestroyService.new(group, current_user).execute
end
+ group.chat_team&.remove_mattermost_team(current_user)
+
group.really_destroy!
end
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 9078b1f0983..ea497729115 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -58,6 +58,7 @@ class IssuableBaseService < BaseService
params.delete(:assignee_ids)
params.delete(:assignee_id)
params.delete(:due_date)
+ params.delete(:canonical_issue_id)
end
filter_assignee(issuable)
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 34199eb5d13..4c198fc96ea 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -7,6 +7,14 @@ module Issues
issue_data
end
+ def reopen_service
+ Issues::ReopenService
+ end
+
+ def close_service
+ Issues::CloseService
+ end
+
private
def create_assignee_note(issue, old_assignees)
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index ddef5281498..74459c3342c 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -16,13 +16,13 @@ module Issues
# The code calling this method is responsible for ensuring that a user is
# allowed to close the given issue.
def close_issue(issue, commit: nil, notifications: true, system_note: true)
- if project.jira_tracker? && project.jira_service.active
+ if project.jira_tracker? && project.jira_service.active && issue.is_a?(ExternalIssue)
project.jira_service.close_issue(commit, issue)
todo_service.close_issue(issue, current_user)
return issue
end
- if project.default_issues_tracker? && issue.close
+ if project.issues_enabled? && issue.close
event_service.close_issue(issue, current_user)
create_note(issue, commit) if system_note
notification_service.close_issue(issue, current_user) if notifications
diff --git a/app/services/issues/duplicate_service.rb b/app/services/issues/duplicate_service.rb
new file mode 100644
index 00000000000..5c0854e664d
--- /dev/null
+++ b/app/services/issues/duplicate_service.rb
@@ -0,0 +1,24 @@
+module Issues
+ class DuplicateService < Issues::BaseService
+ def execute(duplicate_issue, canonical_issue)
+ return if canonical_issue == duplicate_issue
+ return unless can?(current_user, :update_issue, duplicate_issue)
+ return unless can?(current_user, :create_note, canonical_issue)
+
+ create_issue_duplicate_note(duplicate_issue, canonical_issue)
+ create_issue_canonical_note(canonical_issue, duplicate_issue)
+
+ close_service.new(project, current_user, {}).execute(duplicate_issue)
+ end
+
+ private
+
+ def create_issue_duplicate_note(duplicate_issue, canonical_issue)
+ SystemNoteService.mark_duplicate_issue(duplicate_issue, duplicate_issue.project, current_user, canonical_issue)
+ end
+
+ def create_issue_canonical_note(canonical_issue, duplicate_issue)
+ SystemNoteService.mark_canonical_issue_of_duplicate(canonical_issue, canonical_issue.project, current_user, duplicate_issue)
+ end
+ end
+end
diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb
index 73b2e85cba3..35de4337b15 100644
--- a/app/services/issues/reopen_service.rb
+++ b/app/services/issues/reopen_service.rb
@@ -5,7 +5,7 @@ module Issues
if issue.reopen
event_service.reopen_issue(issue, current_user)
- create_note(issue)
+ create_note(issue, 'reopened')
notification_service.reopen_issue(issue, current_user)
execute_hooks(issue, 'reopen')
invalidate_cache_counts(issue, users: issue.assignees)
@@ -16,8 +16,8 @@ module Issues
private
- def create_note(issue)
- SystemNoteService.change_status(issue, issue.project, current_user, issue.state, nil)
+ def create_note(issue, state = issue.state)
+ SystemNoteService.change_status(issue, issue.project, current_user, state, nil)
end
end
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index cd9f9a4a16e..8d918ccc635 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -5,6 +5,7 @@ module Issues
def execute(issue)
handle_move_between_iids(issue)
filter_spam_check_params
+ change_issue_duplicate(issue)
update(issue)
end
@@ -53,14 +54,6 @@ module Issues
end
end
- def reopen_service
- Issues::ReopenService
- end
-
- def close_service
- Issues::CloseService
- end
-
def handle_move_between_iids(issue)
return unless params[:move_between_iids]
@@ -72,6 +65,15 @@ module Issues
issue.move_between(issue_before, issue_after)
end
+ def change_issue_duplicate(issue)
+ canonical_issue_id = params.delete(:canonical_issue_id)
+ canonical_issue = IssuesFinder.new(current_user).find_by(id: canonical_issue_id)
+
+ if canonical_issue
+ Issues::DuplicateService.new(project, current_user).execute(issue, canonical_issue)
+ end
+ end
+
private
def get_issue_if_allowed(project, iid)
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 3542a41ac83..35ccff26262 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -1,7 +1,7 @@
module MergeRequests
class BaseService < ::IssuableBaseService
- def create_note(merge_request)
- SystemNoteService.change_status(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
+ def create_note(merge_request, state = merge_request.state)
+ SystemNoteService.change_status(merge_request, merge_request.target_project, current_user, state, nil)
end
def create_title_change_note(issuable, old_title)
@@ -44,7 +44,7 @@ module MergeRequests
end
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
- def merge_requests_for(source_branch, mr_states: [:opened, :reopened])
+ def merge_requests_for(source_branch, mr_states: [:opened])
MergeRequest
.with_state(mr_states)
.where(source_branch: source_branch, source_project_id: @project.id)
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index 52f6d511f98..b9c65be36ec 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -5,7 +5,7 @@ module MergeRequests
if merge_request.reopen
event_service.reopen_mr(merge_request, current_user)
- create_note(merge_request)
+ create_note(merge_request, 'reopened')
notification_service.reopen_mr(merge_request, current_user)
execute_hooks(merge_request, 'reopen')
merge_request.reload_diff(current_user)
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 3a98a5f6b64..b94921d2a08 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -17,6 +17,16 @@ class NotificationService
end
end
+ # Always notify the user about gpg key added
+ #
+ # This is a security email so it will be sent even if the user user disabled
+ # notifications
+ def new_gpg_key(gpg_key)
+ if gpg_key.user
+ mailer.new_gpg_key_email(gpg_key.id).deliver_later
+ end
+ end
+
# Always notify user about email added to profile
def new_email(email)
if email.user
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index e2b2660ea71..11ad4838471 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -9,46 +9,54 @@ module Projects
def async_execute
project.update_attribute(:pending_delete, true)
job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params)
- Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.path_with_namespace} with job ID #{job_id}")
+ Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}")
end
def execute
return false unless can?(current_user, :remove_project, project)
- repo_path = project.path_with_namespace
- wiki_path = repo_path + '.wiki'
-
# Flush the cache for both repositories. This has to be done _before_
# removing the physical repositories as some expiration code depends on
# Git data (e.g. a list of branch names).
- flush_caches(project, wiki_path)
+ flush_caches(project)
Projects::UnlinkForkService.new(project, current_user).execute
- Project.transaction do
- project.team.truncate
- project.destroy!
-
- unless remove_legacy_registry_tags
- raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
- end
-
- unless remove_repository(repo_path)
- raise_error('Failed to remove project repository. Please try again or contact administrator.')
- end
+ attempt_destroy_transaction(project)
- unless remove_repository(wiki_path)
- raise_error('Failed to remove wiki repository. Please try again or contact administrator.')
- end
- end
-
- log_info("Project \"#{project.path_with_namespace}\" was removed")
system_hook_service.execute_hooks_for(project, :destroy)
+ log_info("Project \"#{project.full_path}\" was removed")
+
true
+ rescue => error
+ attempt_rollback(project, error.message)
+ false
+ rescue Exception => error # rubocop:disable Lint/RescueException
+ # Project.transaction can raise Exception
+ attempt_rollback(project, error.message)
+ raise
end
private
+ def repo_path
+ project.disk_path
+ end
+
+ def wiki_path
+ repo_path + '.wiki'
+ end
+
+ def trash_repositories!
+ unless remove_repository(repo_path)
+ raise_error('Failed to remove project repository. Please try again or contact administrator.')
+ end
+
+ unless remove_repository(wiki_path)
+ raise_error('Failed to remove wiki repository. Please try again or contact administrator.')
+ end
+ end
+
def remove_repository(path)
# Skip repository removal. We use this flag when remove user or group
return true if params[:skip_repo] == true
@@ -70,6 +78,26 @@ module Projects
end
end
+ def attempt_rollback(project, message)
+ return unless project
+
+ project.update_attributes(delete_error: message, pending_delete: false)
+ log_error("Deletion failed on #{project.full_path} with the following message: #{message}")
+ end
+
+ def attempt_destroy_transaction(project)
+ Project.transaction do
+ unless remove_legacy_registry_tags
+ raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
+ end
+
+ trash_repositories!
+
+ project.team.truncate
+ project.destroy!
+ end
+ end
+
##
# This method makes sure that we correctly remove registry tags
# for legacy image repository (when repository path equals project path).
@@ -96,10 +124,10 @@ module Projects
"#{path}+#{project.id}#{DELETED_FLAG}"
end
- def flush_caches(project, wiki_path)
+ def flush_caches(project)
project.repository.before_delete
- Repository.new(wiki_path, project).before_delete
+ Repository.new(wiki_path, project, disk_path: repo_path).before_delete
end
end
end
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 535da706159..fe4e8ea10bf 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -2,7 +2,7 @@ module Projects
module ImportExport
class ExportService < BaseService
def execute(_options = {})
- @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.path_with_namespace, 'work'))
+ @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.disk_path, 'work'))
save_all
end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index eea17e24903..50ec3651515 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -11,7 +11,7 @@ module Projects
success
rescue => e
- error("Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}")
+ error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}")
end
private
@@ -51,7 +51,7 @@ module Projects
end
def clone_repository
- gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
+ gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, project.import_url)
end
def fetch_repository
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 4bb98e5cb4e..5957f612e84 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -34,7 +34,7 @@ module Projects
private
def transfer(project)
- @old_path = project.path_with_namespace
+ @old_path = project.full_path
@old_group = project.group
@new_path = File.join(@new_namespace.try(:full_path) || '', project.path)
@old_namespace = project.namespace
@@ -61,11 +61,13 @@ module Projects
project.send_move_instructions(@old_path)
# Move main repository
+ # TODO: check storage type and NOOP when not using Legacy
unless move_repo_folder(@old_path, @new_path)
raise TransferError.new('Cannot move project')
end
# Move wiki repo also if present
+ # TODO: check storage type and NOOP when not using Legacy
move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
# Move missing group labels to project
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index e60b854f916..749a1cc56d8 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -130,7 +130,11 @@ module Projects
end
def max_size
- current_application_settings.max_pages_size.megabytes || MAX_SIZE
+ max_pages_size = current_application_settings.max_pages_size.megabytes
+
+ return MAX_SIZE if max_pages_size.zero?
+
+ [max_pages_size, MAX_SIZE].min
end
def tmp_path
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index 30ca95eef7a..d81035e4eba 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -5,7 +5,7 @@ module Projects
return error('New visibility level not allowed!')
end
- if project.has_container_registry_tags?
+ if renaming_project_with_container_registry_tags?
return error('Cannot rename project because it contains container registry tags!')
end
@@ -44,6 +44,13 @@ module Projects
true
end
+ def renaming_project_with_container_registry_tags?
+ new_path = params[:path]
+
+ new_path && new_path != project.path &&
+ project.has_container_registry_tags?
+ end
+
def changing_default_branch?
new_branch = params[:default_branch]
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 6f82159e6c7..c22bf7498bb 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -4,6 +4,9 @@ module QuickActions
attr_reader :issuable
+ SHRUG = '¯\\_(ツ)_/¯'.freeze
+ TABLEFLIP = '(╯°□°)╯︵ ┻━┻'.freeze
+
# Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and hash of changes to be applied to a record.
def execute(content, issuable)
@@ -14,6 +17,7 @@ module QuickActions
content, commands = extractor.extract_commands(content, context)
extract_updates(commands, context)
+
[content, @updates]
end
@@ -423,6 +427,18 @@ module QuickActions
@updates[:spend_time] = { duration: :reset, user: current_user }
end
+ desc "Append the comment with #{SHRUG}"
+ params '<Comment>'
+ substitution :shrug do |comment|
+ "#{comment} #{SHRUG}"
+ end
+
+ desc "Append the comment with #{TABLEFLIP}"
+ params '<Comment>'
+ substitution :tableflip do |comment|
+ "#{comment} #{TABLEFLIP}"
+ end
+
# This is a dummy command, so that it appears in the autocomplete commands
desc 'CC'
params '@user'
@@ -471,6 +487,24 @@ module QuickActions
end
end
+ desc 'Mark this issue as a duplicate of another issue'
+ explanation do |duplicate_reference|
+ "Marks this issue as a duplicate of #{duplicate_reference}."
+ end
+ params '#issue'
+ condition do
+ issuable.is_a?(Issue) &&
+ issuable.persisted? &&
+ current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
+ end
+ command :duplicate do |duplicate_param|
+ canonical_issue = extract_references(duplicate_param, :issue).first
+
+ if canonical_issue.present?
+ @updates[:canonical_issue_id] = canonical_issue.id
+ end
+ end
+
def extract_users(params)
return [] if params.nil?
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index bd58a54592f..cbcd4478af6 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -24,7 +24,7 @@ class SystemHooksService
key: model.key,
id: model.id
)
-
+
if model.user
data[:username] = model.user.username
end
@@ -56,7 +56,7 @@ class SystemHooksService
when GroupMember
data.merge!(group_member_data(model))
end
-
+
data
end
@@ -79,7 +79,7 @@ class SystemHooksService
{
name: model.name,
path: model.path,
- path_with_namespace: model.path_with_namespace,
+ path_with_namespace: model.full_path,
project_id: model.id,
owner_name: owner.name,
owner_email: owner.respond_to?(:email) ? owner.email : "",
@@ -93,7 +93,7 @@ class SystemHooksService
{
project_name: project.name,
project_path: project.path,
- project_path_with_namespace: project.path_with_namespace,
+ project_path_with_namespace: project.full_path,
project_id: project.id,
user_username: model.user.username,
user_name: model.user.name,
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index da0f21d449a..2dbee9c246e 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -552,6 +552,44 @@ module SystemNoteService
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
end
+ # Called when a Noteable has been marked as a duplicate of another Issue
+ #
+ # noteable - Noteable object
+ # project - Project owning noteable
+ # author - User performing the change
+ # canonical_issue - Issue that this is a duplicate of
+ #
+ # Example Note text:
+ #
+ # "marked this issue as a duplicate of #1234"
+ #
+ # "marked this issue as a duplicate of other_project#5678"
+ #
+ # Returns the created Note object
+ def mark_duplicate_issue(noteable, project, author, canonical_issue)
+ body = "marked this issue as a duplicate of #{canonical_issue.to_reference(project)}"
+ create_note(NoteSummary.new(noteable, project, author, body, action: 'duplicate'))
+ end
+
+ # Called when a Noteable has been marked as the canonical Issue of a duplicate
+ #
+ # noteable - Noteable object
+ # project - Project owning noteable
+ # author - User performing the change
+ # duplicate_issue - Issue that was a duplicate of this
+ #
+ # Example Note text:
+ #
+ # "marked #1234 as a duplicate of this issue"
+ #
+ # "marked other_project#5678 as a duplicate of this issue"
+ #
+ # Returns the created Note object
+ def mark_canonical_issue_of_duplicate(noteable, project, author, duplicate_issue)
+ body = "marked #{duplicate_issue.to_reference(project)} as a duplicate of this issue"
+ create_note(NoteSummary.new(noteable, project, author, body, action: 'duplicate'))
+ end
+
private
def notes_for_mentioner(mentioner, noteable, notes)
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index a5110a23cad..2825478926a 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -44,7 +44,7 @@ class WebHookService
http_status: response.code,
message: response.to_s
}
- rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
+ rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout => e
log_execution(
trigger: hook_name,
url: hook.url,
@@ -101,7 +101,7 @@ class WebHookService
request_headers: build_headers(hook_name),
request_data: request_data,
response_headers: format_response_headers(response),
- response_body: response.body,
+ response_body: safe_response_body(response),
response_status: response.code,
internal_error_message: error_message
)
@@ -124,4 +124,10 @@ class WebHookService
def format_response_headers(response)
response.headers.each_capitalized.to_h
end
+
+ def safe_response_body(response)
+ return '' unless response.body
+
+ response.body.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
+ end
end
diff --git a/app/services/wiki_pages/update_service.rb b/app/services/wiki_pages/update_service.rb
index 8f6a50da838..c628e6781af 100644
--- a/app/services/wiki_pages/update_service.rb
+++ b/app/services/wiki_pages/update_service.rb
@@ -1,7 +1,7 @@
module WikiPages
class UpdateService < WikiPages::BaseService
def execute(page)
- if page.update(@params[:content], @params[:format], @params[:message])
+ if page.update(@params[:content], format: @params[:format], message: @params[:message], last_commit_sha: @params[:last_commit_sha])
execute_hooks(page, 'update')
end