diff options
Diffstat (limited to 'app/services')
25 files changed, 178 insertions, 92 deletions
diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb index 15fed7d17c1..3ceab209f3f 100644 --- a/app/services/boards/issues/move_service.rb +++ b/app/services/boards/issues/move_service.rb @@ -42,7 +42,10 @@ module Boards ) end - attrs[:move_between_ids] = move_between_ids if move_between_ids + if move_between_ids + attrs[:move_between_ids] = move_between_ids + attrs[:board_group_id] = board.group&.id + end attrs end diff --git a/app/services/boards/list_service.rb b/app/services/boards/list_service.rb index 6d0dd0a9f99..9269b8d2620 100644 --- a/app/services/boards/list_service.rb +++ b/app/services/boards/list_service.rb @@ -2,11 +2,15 @@ module Boards class ListService < Boards::BaseService def execute create_board! if parent.boards.empty? - parent.boards + boards end private + def boards + parent.boards + end + def create_board! Boards::CreateService.new(parent, current_user).execute end diff --git a/app/services/boards/lists/create_service.rb b/app/services/boards/lists/create_service.rb index bebc90c7a8d..02f1c709374 100644 --- a/app/services/boards/lists/create_service.rb +++ b/app/services/boards/lists/create_service.rb @@ -12,11 +12,15 @@ module Boards private def available_labels_for(board) + options = { include_ancestor_groups: true } + if board.group_board? - parent.labels + options.merge!(group_id: parent.id, only_group_labels: true) else - LabelsFinder.new(current_user, project_id: parent.id).execute + options[:project_id] = parent.id end + + LabelsFinder.new(current_user, options).execute end def next_position(board) diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index ad27f320853..6ce86983287 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -7,6 +7,7 @@ module Ci Gitlab::Ci::Pipeline::Chain::Validate::Repository, Gitlab::Ci::Pipeline::Chain::Validate::Config, Gitlab::Ci::Pipeline::Chain::Skip, + Gitlab::Ci::Pipeline::Chain::Populate, Gitlab::Ci::Pipeline::Chain::Create].freeze def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, &block) diff --git a/app/services/ci/create_pipeline_stages_service.rb b/app/services/ci/create_pipeline_stages_service.rb deleted file mode 100644 index f2c175adee6..00000000000 --- a/app/services/ci/create_pipeline_stages_service.rb +++ /dev/null @@ -1,20 +0,0 @@ -module Ci - class CreatePipelineStagesService < BaseService - def execute(pipeline) - pipeline.stage_seeds.each do |seed| - seed.user = current_user - - seed.create! do |build| - ## - # Create the environment before the build starts. This sets its slug and - # makes it available as an environment variable - # - if build.has_environment? - environment_name = build.expanded_environment_name - project.environments.find_or_create_by(name: environment_name) - end - end - end - end - end -end diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb index a9813d774bb..85533a1cbdb 100644 --- a/app/services/ci/pipeline_trigger_service.rb +++ b/app/services/ci/pipeline_trigger_service.rb @@ -16,8 +16,8 @@ module Ci pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: params[:ref]) .execute(:trigger, ignore_skip_ci: true) do |pipeline| - pipeline.trigger_requests.create!(trigger: trigger) - create_pipeline_variables!(pipeline) + pipeline.trigger_requests.build(trigger: trigger) + pipeline.variables.build(variables) end if pipeline.persisted? @@ -33,14 +33,10 @@ module Ci end end - def create_pipeline_variables!(pipeline) - return unless params[:variables] - - variables = params[:variables].map do |key, value| + def variables + params[:variables].to_h.map do |key, value| { key: key, value: value } end - - pipeline.variables.create!(variables) end end end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 02fb48108fb..91ec702fbc6 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -106,7 +106,7 @@ class IssuableBaseService < BaseService end def available_labels - @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute + @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id, include_ancestor_groups: true).execute end def handle_quick_actions_on_create(issuable) diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb index 0c5cf2c62ad..fee5bc38f7b 100644 --- a/app/services/issues/close_service.rb +++ b/app/services/issues/close_service.rb @@ -23,6 +23,7 @@ module Issues end if project.issues_enabled? && issue.close + issue.update(closed_by: current_user) 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/update_service.rb b/app/services/issues/update_service.rb index d7aa7e2347e..4161932ad2a 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -55,9 +55,10 @@ module Issues return unless params[:move_between_ids] after_id, before_id = params.delete(:move_between_ids) + board_group_id = params.delete(:board_group_id) - issue_before = get_issue_if_allowed(issue.project, before_id) if before_id - issue_after = get_issue_if_allowed(issue.project, after_id) if after_id + issue_before = get_issue_if_allowed(before_id, board_group_id) + issue_after = get_issue_if_allowed(after_id, board_group_id) issue.move_between(issue_before, issue_after) end @@ -84,8 +85,16 @@ module Issues private - def get_issue_if_allowed(project, id) - issue = project.issues.find(id) + def get_issue_if_allowed(id, board_group_id = nil) + return unless id + + issue = + if board_group_id + IssuesFinder.new(current_user, group_id: board_group_id).find_by(id: id) + else + project.issues.find(id) + end + issue if can?(current_user, :update_issue, issue) end diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index 18c40ce8992..1fb1796b56c 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -21,7 +21,7 @@ module MergeRequests comment_mr_branch_presence_changed end - comment_mr_with_commits + notify_about_push mark_mr_as_wip_from_commits execute_mr_web_hooks @@ -141,8 +141,8 @@ module MergeRequests end end - # Add comment about pushing new commits to merge requests - def comment_mr_with_commits + # Add comment about pushing new commits to merge requests and send nofitication emails + def notify_about_push return unless @commits.present? merge_requests_for_source_branch.each do |merge_request| @@ -155,6 +155,8 @@ module MergeRequests SystemNoteService.add_commits(merge_request, merge_request.project, @current_user, new_commits, existing_commits, @oldrev) + + notification_service.push_to_merge_request(merge_request, @current_user, new_commits: new_commits, existing_commits: existing_commits) end end diff --git a/app/services/notes/post_process_service.rb b/app/services/notes/post_process_service.rb index ad3dcc5010b..199b8028dbc 100644 --- a/app/services/notes/post_process_service.rb +++ b/app/services/notes/post_process_service.rb @@ -11,7 +11,7 @@ module Notes unless @note.system? EventCreateService.new.leave_note(@note, @note.author) - return unless @note.for_project_noteable? + return if @note.for_personal_snippet? @note.create_cross_references! execute_note_hooks @@ -23,9 +23,13 @@ module Notes end def execute_note_hooks + return unless @note.project + note_data = hook_data - @note.project.execute_hooks(note_data, :note_hooks) - @note.project.execute_services(note_data, :note_hooks) + hooks_scope = @note.confidential? ? :confidential_note_hooks : :note_hooks + + @note.project.execute_hooks(note_data, hooks_scope) + @note.project.execute_services(note_data, hooks_scope) end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index d7d2cde1004..f94c76cf3ac 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -113,6 +113,16 @@ class NotificationService new_resource_email(merge_request, :new_merge_request_email) end + def push_to_merge_request(merge_request, current_user, new_commits: [], existing_commits: []) + new_commits = new_commits.map { |c| { short_id: c.short_id, title: c.title } } + existing_commits = existing_commits.map { |c| { short_id: c.short_id, title: c.title } } + recipients = NotificationRecipientService.build_recipients(merge_request, current_user, action: "push_to") + + recipients.each do |recipient| + mailer.send(:push_to_merge_request_email, recipient.user.id, merge_request.id, current_user.id, recipient.reason, new_commits: new_commits, existing_commits: existing_commits).deliver_later + end + end + # When merge request text is updated, we should send an email to: # # * newly mentioned project team members with notification level higher than Participating diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb index e61ecb696d0..346971138b1 100644 --- a/app/services/projects/autocomplete_service.rb +++ b/app/services/projects/autocomplete_service.rb @@ -21,7 +21,8 @@ module Projects end def labels(target = nil) - labels = LabelsFinder.new(current_user, project_id: project.id).execute.select([:color, :title]) + labels = LabelsFinder.new(current_user, project_id: project.id, include_ancestor_groups: true) + .execute.select([:color, :title]) return labels unless target&.respond_to?(:labels) diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 7fa1387084c..633e2c8236c 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -90,9 +90,6 @@ module Projects unless @project.gitlab_project_import? @project.write_repository_config @project.create_wiki unless skip_wiki? - create_services_from_active_templates(@project) - - @project.create_labels end event_service.create_project(@project, current_user) @@ -121,21 +118,29 @@ module Projects Project.transaction do @project.create_or_update_import_data(data: import_data[:data], credentials: import_data[:credentials]) if import_data - if @project.save && !@project.import? - raise 'Failed to create repository' unless @project.create_repository + if @project.save + unless @project.gitlab_project_import? + create_services_from_active_templates(@project) + @project.create_labels + end + + unless @project.import? + raise 'Failed to create repository' unless @project.create_repository + end end end end def fail(error:) message = "Unable to save project. Error: #{error}" - message << "Project ID: #{@project.id}" if @project && @project.id + log_message = message.dup - Rails.logger.error(message) + log_message << " Project ID: #{@project.id}" if @project&.id + Rails.logger.error(log_message) - if @project && @project.import? + if @project @project.errors.add(:base, message) - @project.mark_import_as_failed(message) + @project.mark_import_as_failed(message) if @project.import? end @project diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb index a68ecb4abe1..fb4afb85588 100644 --- a/app/services/projects/gitlab_projects_import_service.rb +++ b/app/services/projects/gitlab_projects_import_service.rb @@ -5,8 +5,8 @@ module Projects class GitlabProjectsImportService attr_reader :current_user, :params - def initialize(user, params) - @current_user, @params = user, params.dup + def initialize(user, import_params, override_params = nil) + @current_user, @params, @override_params = user, import_params.dup, override_params end def execute @@ -17,6 +17,7 @@ module Projects params[:import_type] = 'gitlab_project' params[:import_source] = import_upload_path + params[:import_data] = { data: { override_params: @override_params } } if @override_params ::Projects::CreateService.new(current_user, params).execute end diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb index d16aa3de639..7bf0b90b491 100644 --- a/app/services/projects/import_export/export_service.rb +++ b/app/services/projects/import_export/export_service.rb @@ -1,22 +1,36 @@ module Projects module ImportExport class ExportService < BaseService - def execute(_options = {}) + def execute(after_export_strategy = nil, options = {}) @shared = project.import_export_shared - save_all + + save_all! + execute_after_export_action(after_export_strategy) end private - def save_all - if [version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save) + def execute_after_export_action(after_export_strategy) + return unless after_export_strategy + + unless after_export_strategy.execute(current_user, project) + cleanup_and_notify_error + end + end + + def save_all! + if save_services Gitlab::ImportExport::Saver.save(project: project, shared: @shared) notify_success else - cleanup_and_notify + cleanup_and_notify_error! end end + def save_services + [version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver, lfs_saver].all?(&:save) + end + def version_saver Gitlab::ImportExport::VersionSaver.new(shared: @shared) end @@ -41,19 +55,26 @@ module Projects Gitlab::ImportExport::WikiRepoSaver.new(project: project, shared: @shared) end - def cleanup_and_notify + def lfs_saver + Gitlab::ImportExport::LfsSaver.new(project: project, shared: @shared) + end + + def cleanup_and_notify_error Rails.logger.error("Import/Export - Project #{project.name} with ID: #{project.id} export error - #{@shared.errors.join(', ')}") FileUtils.rm_rf(@shared.export_path) notify_error + end + + def cleanup_and_notify_error! + cleanup_and_notify_error + raise Gitlab::ImportExport::Error.new(@shared.errors.join(', ')) end def notify_success Rails.logger.info("Import/Export - Project #{project.name} with ID: #{project.id} successfully exported") - - notification_service.project_exported(@project, @current_user) end def notify_error diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index a34024f4f80..bdd9598f85a 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -28,7 +28,11 @@ module Projects def add_repository_to_project if project.external_import? && !unknown_url? - raise Error, 'Blocked import URL.' if Gitlab::UrlBlocker.blocked_url?(project.import_url, valid_ports: Project::VALID_IMPORT_PORTS) + begin + Gitlab::UrlBlocker.validate!(project.import_url, valid_ports: Project::VALID_IMPORT_PORTS) + rescue Gitlab::UrlBlocker::BlockedUrlError => e + raise Error, "Blocked import URL: #{e.message}" + end end # We should skip the repository for a GitHub import or GitLab project import, @@ -57,7 +61,7 @@ module Projects project.ensure_repository project.repository.fetch_as_mirror(project.import_url, refmap: refmap) else - gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, project.import_url) + gitlab_shell.import_repository(project.repository_storage, project.disk_path, project.import_url) end rescue Gitlab::Shell::Error, Gitlab::Git::RepositoryMirroring::RemoteError => e # Expire cache to prevent scenarios such as: diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index 00fdd047208..7e228d1833d 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -31,15 +31,17 @@ module Projects # Check if we did extract public directory archive_public_path = File.join(archive_path, 'public') - raise FailedToExtractError, 'pages miss the public folder' unless Dir.exist?(archive_public_path) + raise InvaildStateError, 'pages miss the public folder' unless Dir.exist?(archive_public_path) raise InvaildStateError, 'pages are outdated' unless latest? deploy_page!(archive_public_path) success end - rescue InvaildStateError, FailedToExtractError => e - register_failure + rescue InvaildStateError => e error(e.message) + rescue => e + error(e.message, false) + raise e end private @@ -50,12 +52,13 @@ module Projects super end - def error(message, http_status = nil) + def error(message, allow_delete_artifact = true) + register_failure log_error("Projects::UpdatePagesService: #{message}") @status.allow_failure = !latest? @status.description = message @status.drop(:script_failure) - delete_artifact! + delete_artifact! if allow_delete_artifact super end @@ -76,26 +79,28 @@ module Projects elsif artifacts.ends_with?('.zip') extract_zip_archive!(temp_path) else - raise FailedToExtractError, 'unsupported artifacts format' + raise InvaildStateError, 'unsupported artifacts format' end end def extract_tar_archive!(temp_path) - results = Open3.pipeline(%W(gunzip -c #{artifacts}), - %W(dd bs=#{BLOCK_SIZE} count=#{blocks}), - %W(tar -x -C #{temp_path} #{SITE_PATH}), - err: '/dev/null') - raise FailedToExtractError, 'pages failed to extract' unless results.compact.all?(&:success?) + build.artifacts_file.use_file do |artifacts_path| + results = Open3.pipeline(%W(gunzip -c #{artifacts_path}), + %W(dd bs=#{BLOCK_SIZE} count=#{blocks}), + %W(tar -x -C #{temp_path} #{SITE_PATH}), + err: '/dev/null') + raise FailedToExtractError, 'pages failed to extract' unless results.compact.all?(&:success?) + end end def extract_zip_archive!(temp_path) - raise FailedToExtractError, 'missing artifacts metadata' unless build.artifacts_metadata? + raise InvaildStateError, 'missing artifacts metadata' unless build.artifacts_metadata? # Calculate page size after extract public_entry = build.artifacts_metadata_entry(SITE_PATH, recursive: true) if public_entry.total_size > max_size - raise FailedToExtractError, "artifacts for pages are too large: #{public_entry.total_size}" + raise InvaildStateError, "artifacts for pages are too large: #{public_entry.total_size}" end # Requires UnZip at least 6.00 Info-ZIP. @@ -103,8 +108,10 @@ module Projects # -n never overwrite existing files # We add * to end of SITE_PATH, because we want to extract SITE_PATH and all subdirectories site_path = File.join(SITE_PATH, '*') - unless system(*%W(unzip -qq -n #{artifacts} #{site_path} -d #{temp_path})) - raise FailedToExtractError, 'pages failed to extract' + build.artifacts_file.use_file do |artifacts_path| + unless system(*%W(unzip -n #{artifacts_path} #{site_path} -d #{temp_path})) + raise FailedToExtractError, 'pages failed to extract' + end end end @@ -174,6 +181,9 @@ module Projects def latest_sha project.commit(build.ref).try(:sha).to_s + ensure + # Close any file descriptors that were opened and free libgit2 buffers + project.cleanup end def sha diff --git a/app/services/protected_branches/create_service.rb b/app/services/protected_branches/create_service.rb index 6212fd69077..9d947f73af1 100644 --- a/app/services/protected_branches/create_service.rb +++ b/app/services/protected_branches/create_service.rb @@ -1,11 +1,20 @@ module ProtectedBranches class CreateService < BaseService - attr_reader :protected_branch - def execute(skip_authorization: false) - raise Gitlab::Access::AccessDeniedError unless skip_authorization || can?(current_user, :admin_project, project) + raise Gitlab::Access::AccessDeniedError unless skip_authorization || authorized? + + protected_branch.save + protected_branch + end + + def authorized? + can?(current_user, :create_protected_branch, protected_branch) + end + + private - project.protected_branches.create(params) + def protected_branch + @protected_branch ||= project.protected_branches.new(params) end end end diff --git a/app/services/protected_branches/destroy_service.rb b/app/services/protected_branches/destroy_service.rb new file mode 100644 index 00000000000..8172c896e76 --- /dev/null +++ b/app/services/protected_branches/destroy_service.rb @@ -0,0 +1,9 @@ +module ProtectedBranches + class DestroyService < BaseService + def execute(protected_branch) + raise Gitlab::Access::AccessDeniedError unless can?(current_user, :destroy_protected_branch, protected_branch) + + protected_branch.destroy + end + end +end diff --git a/app/services/protected_branches/update_service.rb b/app/services/protected_branches/update_service.rb index 4b3337a5c9d..95e46645374 100644 --- a/app/services/protected_branches/update_service.rb +++ b/app/services/protected_branches/update_service.rb @@ -1,7 +1,7 @@ module ProtectedBranches class UpdateService < BaseService def execute(protected_branch) - raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_project, project) + raise Gitlab::Access::AccessDeniedError unless can?(current_user, :update_protected_branch, protected_branch) protected_branch.update(params) protected_branch diff --git a/app/services/protected_tags/destroy_service.rb b/app/services/protected_tags/destroy_service.rb new file mode 100644 index 00000000000..c868d7ad8e6 --- /dev/null +++ b/app/services/protected_tags/destroy_service.rb @@ -0,0 +1,7 @@ +module ProtectedTags + class DestroyService < BaseService + def execute(protected_tag) + protected_tag.destroy + end + end +end diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index cba49faac31..6cc51b6ee1b 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -200,7 +200,7 @@ module QuickActions end params '~label1 ~"label 2"' condition do - available_labels = LabelsFinder.new(current_user, project_id: project.id).execute + available_labels = LabelsFinder.new(current_user, project_id: project.id, include_ancestor_groups: true).execute current_user.can?(:"admin_#{issuable.to_ability_name}", project) && available_labels.any? @@ -562,7 +562,7 @@ module QuickActions def find_labels(labels_param) extract_references(labels_param, :label) | - LabelsFinder.new(current_user, project_id: project.id, name: labels_param.split).execute + LabelsFinder.new(current_user, project_id: project.id, name: labels_param.split, include_ancestor_groups: true).execute end def find_label_references(labels_param) @@ -593,6 +593,7 @@ module QuickActions def extract_references(arg, type) ext = Gitlab::ReferenceExtractor.new(project, current_user) + ext.analyze(arg, author: current_user) ext.references(type) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 2253d638e93..00bf5434b7f 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -429,7 +429,7 @@ module SystemNoteService def cross_reference(noteable, mentioner, author) return if cross_reference_disallowed?(noteable, mentioner) - gfm_reference = mentioner.gfm_reference(noteable.project) + gfm_reference = mentioner.gfm_reference(noteable.project || noteable.group) body = cross_reference_note_content(gfm_reference) if noteable.is_a?(ExternalIssue) @@ -582,7 +582,7 @@ module SystemNoteService text = "#{cross_reference_note_prefix}%#{mentioner.to_reference(nil)}" notes.where('(note LIKE ? OR note LIKE ?)', text, text.capitalize) else - gfm_reference = mentioner.gfm_reference(noteable.project) + gfm_reference = mentioner.gfm_reference(noteable.project || noteable.group) text = cross_reference_note_content(gfm_reference) notes.where(note: [text, text.capitalize]) end diff --git a/app/services/verify_pages_domain_service.rb b/app/services/verify_pages_domain_service.rb index 86166047302..13cb53dee01 100644 --- a/app/services/verify_pages_domain_service.rb +++ b/app/services/verify_pages_domain_service.rb @@ -34,7 +34,8 @@ class VerifyPagesDomainService < BaseService # Prevent any pre-existing grace period from being truncated reverify = [domain.enabled_until, VERIFICATION_PERIOD.from_now].compact.max - domain.update!(verified_at: Time.now, enabled_until: reverify) + domain.assign_attributes(verified_at: Time.now, enabled_until: reverify) + domain.save!(validate: false) if was_disabled notify(:enabled) @@ -47,7 +48,9 @@ class VerifyPagesDomainService < BaseService def unverify_domain! if domain.verified? - domain.update!(verified_at: nil) + domain.assign_attributes(verified_at: nil) + domain.save!(validate: false) + notify(:verification_failed) end @@ -55,7 +58,8 @@ class VerifyPagesDomainService < BaseService end def disable_domain! - domain.update!(verified_at: nil, enabled_until: nil) + domain.assign_attributes(verified_at: nil, enabled_until: nil) + domain.save!(validate: false) notify(:disabled) |