From 3b5e9f0608fac06b6c5f39a036efa3c1d82cc6f5 Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Sun, 29 Apr 2018 14:01:00 +0300 Subject: Use gitaly repository_service.CreateFromBundle to restore --- lib/backup/repository.rb | 55 ++---------------------------------------------- 1 file changed, 2 insertions(+), 53 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 65e06fd78c0..42d7fe570ec 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -91,65 +91,14 @@ module Backup prepare_directories Project.find_each(batch_size: 1000) do |project| progress.print " * #{display_repo_path(project)} ... " - path_to_project_repo = path_to_repo(project) path_to_project_bundle = path_to_bundle(project) - project.ensure_storage_path_exists - - cmd = if File.exist?(path_to_project_bundle) - %W(#{Gitlab.config.git.bin_path} clone --bare --mirror #{path_to_project_bundle} #{path_to_project_repo}) - else - %W(#{Gitlab.config.git.bin_path} init --bare #{path_to_project_repo}) - end - - output, status = Gitlab::Popen.popen(cmd) - if status.zero? - progress.puts "[DONE]".color(:green) - else - progress_warn(project, cmd.join(' '), output) - end - - in_path(path_to_tars(project)) do |dir| - cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) - - output, status = Gitlab::Popen.popen(cmd) - unless status.zero? - progress_warn(project, cmd.join(' '), output) - end - end + project.repository.create_from_bundle path_to_project_bundle unless project.repository_exists? wiki = ProjectWiki.new(project) - path_to_wiki_repo = path_to_repo(wiki) path_to_wiki_bundle = path_to_bundle(wiki) - if File.exist?(path_to_wiki_bundle) - progress.print " * #{display_repo_path(wiki)} ... " - - # If a wiki bundle exists, first remove the empty repo - # that was initialized with ProjectWiki.new() and then - # try to restore with 'git clone --bare'. - FileUtils.rm_rf(path_to_wiki_repo) - cmd = %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_wiki_bundle} #{path_to_wiki_repo}) - - output, status = Gitlab::Popen.popen(cmd) - if status.zero? - progress.puts " [DONE]".color(:green) - else - progress_warn(project, cmd.join(' '), output) - end - end - end - - progress.print 'Put GitLab hooks in repositories dirs'.color(:yellow) - cmd = %W(#{Gitlab.config.gitlab_shell.path}/bin/create-hooks) + repository_storage_paths_args - - output, status = Gitlab::Popen.popen(cmd) - if status.zero? - progress.puts " [DONE]".color(:green) - else - puts " [FAILED]".color(:red) - puts "failed: #{cmd}" - puts output + project.repository.create_from_bundle(path_to_wiki_bundle) if File.exists?(path_to_wiki_bundle) end end # rubocop:enable Metrics/AbcSize -- cgit v1.2.3 From fb54eb76ee79121f2aeb480944e6d0552100743b Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Fri, 4 May 2018 16:02:01 +0200 Subject: Remove useless print line --- lib/backup/repository.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 42d7fe570ec..d068d1f66c4 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -90,7 +90,6 @@ module Backup def restore prepare_directories Project.find_each(batch_size: 1000) do |project| - progress.print " * #{display_repo_path(project)} ... " path_to_project_bundle = path_to_bundle(project) project.repository.create_from_bundle path_to_project_bundle unless project.repository_exists? -- cgit v1.2.3 From 845726212584fd9413e1fa70fb3896342f824434 Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Sun, 6 May 2018 21:32:25 +0200 Subject: Better repo restore progress logging --- lib/backup/repository.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index d068d1f66c4..4fc1dded344 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -93,11 +93,15 @@ module Backup path_to_project_bundle = path_to_bundle(project) project.repository.create_from_bundle path_to_project_bundle unless project.repository_exists? + progress.puts "[DONE] restoring #{project.name} repository".color(:green) wiki = ProjectWiki.new(project) path_to_wiki_bundle = path_to_bundle(wiki) - project.repository.create_from_bundle(path_to_wiki_bundle) if File.exists?(path_to_wiki_bundle) + if File.exists?(path_to_wiki_bundle) + project.repository.create_from_bundle(path_to_wiki_bundle) + progress.puts "[DONE] restoring #{project.name} wiki".color(:green) + end end end # rubocop:enable Metrics/AbcSize -- cgit v1.2.3 From 0734da16f631cc9f842bd5c0de08cc2b306738e5 Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Sun, 6 May 2018 21:42:39 +0200 Subject: Wrap create_from_bundle in a begin rescue block --- lib/backup/repository.rb | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 4fc1dded344..67e317c35bc 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -92,15 +92,29 @@ module Backup Project.find_each(batch_size: 1000) do |project| path_to_project_bundle = path_to_bundle(project) - project.repository.create_from_bundle path_to_project_bundle unless project.repository_exists? - progress.puts "[DONE] restoring #{project.name} repository".color(:green) + if File.exist?(path_to_project_bundle) + begin + project.repository.create_from_bundle path_to_project_bundle unless project.repository_exists? + progress.puts "[DONE] restoring #{project.name} repository".color(:green) + rescue StandardError => e + progress.puts "[Failed] restoring #{project.name} repository".color(:red) + progress.puts "Error: #{e}".color(:red) + end + else + progress.puts "[Failed] bundle file #{path_to_project_bundle} does not exist" + end wiki = ProjectWiki.new(project) path_to_wiki_bundle = path_to_bundle(wiki) - if File.exists?(path_to_wiki_bundle) - project.repository.create_from_bundle(path_to_wiki_bundle) - progress.puts "[DONE] restoring #{project.name} wiki".color(:green) + if File.exist?(path_to_wiki_bundle) + begin + project.repository.create_from_bundle(path_to_wiki_bundle) + progress.puts "[DONE] restoring #{project.name} wiki".color(:green) + rescue StandardError => e + progress.puts "[Failed] restoring #{project.name} wiki".color(:red) + progress.puts "Error #{e}".color(:red) + end end end end -- cgit v1.2.3 From 2bcc324f268942d28c427cd3208e43df03af957d Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Fri, 11 May 2018 11:33:29 -0700 Subject: Add back some of the non-gitaly restore functionality behind gates --- lib/backup/repository.rb | 86 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 20 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 67e317c35bc..74b9e0205a4 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -68,40 +68,70 @@ module Backup def prepare_directories Gitlab.config.repositories.storages.each do |name, repository_storage| - path = repository_storage.legacy_disk_path - next unless File.exist?(path) - - # Move all files in the existing repos directory except . and .. to - # repositories.old. directory - bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s) - FileUtils.mkdir_p(bk_repos_path, mode: 0700) - files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")] - - begin - FileUtils.mv(files, bk_repos_path) - rescue Errno::EACCES - access_denied_error(path) - rescue Errno::EBUSY - resource_busy_error(path) + gitaly_migrate(:remove_repositories) do |is_enabled| + # TODO: Need to find a way to do this for gitaly + unless is_enabled + path = repository_storage.legacy_disk_path + next unless File.exist?(path) + + # Move all files in the existing repos directory except . and .. to + # repositories.old. directory + bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s) + FileUtils.mkdir_p(bk_repos_path, mode: 0700) + files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")] + + begin + FileUtils.mv(files, bk_repos_path) + rescue Errno::EACCES + access_denied_error(path) + rescue Errno::EBUSY + resource_busy_error(path) + end + end end end end def restore prepare_directories + gitlab_shell = Gitlab::Shell.new Project.find_each(batch_size: 1000) do |project| path_to_project_bundle = path_to_bundle(project) + path_to_project_repo = path_to_repo(project) + project.ensure_storage_path_exists + restore_repo_status = nil if File.exist?(path_to_project_bundle) begin - project.repository.create_from_bundle path_to_project_bundle unless project.repository_exists? - progress.puts "[DONE] restoring #{project.name} repository".color(:green) - rescue StandardError => e - progress.puts "[Failed] restoring #{project.name} repository".color(:red) + gitlab_shell.remove_repository(project.repository_storage, project.disk_path) if project.repository_exists? + project.repository.create_from_bundle path_to_project_bundle + restore_repo_status = true + rescue => e + restore_repo_status = false progress.puts "Error: #{e}".color(:red) end else - progress.puts "[Failed] bundle file #{path_to_project_bundle} does not exist" + restore_repo_status = gitlab_shell.create_repository(project.repository_storage, project.disk_path) + end + + if restore_repo_status + progress.puts "[DONE] restoring #{project.name} repository".color(:green) + else + progress.puts "[Failed] restoring #{project.name} repository".color(:red) + end + + gitaly_migrate(:restore_custom_hooks) do |is_enabled| + # TODO: Need to find a way to do this for gitaly + unless is_enabled + in_path(path_to_tars(project)) do |dir| + cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) + + output, status = Gitlab::Popen.popen(cmd) + unless status.zero? + progress_warn(project, cmd.join(' '), output) + end + end + end end wiki = ProjectWiki.new(project) @@ -109,6 +139,7 @@ module Backup if File.exist?(path_to_wiki_bundle) begin + gitlab_shell.remove_repository(project.wiki.repository_storage, project.wiki.disk_path) if project.wiki_repository_exists? project.repository.create_from_bundle(path_to_wiki_bundle) progress.puts "[DONE] restoring #{project.name} wiki".color(:green) rescue StandardError => e @@ -116,6 +147,13 @@ module Backup progress.puts "Error #{e}".color(:red) end end + + gitaly_migrate(:create_hooks) do |is_enabled| + # TODO: Need to find a way to do this for gitaly + unless is_enabled + Gitlab::Git::Repository.create_hooks(path_to_project_repo, Gitlab.config.gitlab_shell.hooks_path) + end + end end end # rubocop:enable Metrics/AbcSize @@ -190,5 +228,13 @@ module Backup def display_repo_path(project) project.hashed_storage?(:repository) ? "#{project.full_path} (#{project.disk_path})" : project.full_path end + + def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block) + Gitlab::GitalyClient.migrate(method, status: status, &block) + rescue GRPC::NotFound, GRPC::BadStatus => e + # Old Popen code returns [Error, output] to the caller, so we + # need to do the same here... + raise Error, e + end end end -- cgit v1.2.3 From 4ea9458bbeb28d0a9ba70b45810bda9433467ddd Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Fri, 11 May 2018 12:46:42 -0700 Subject: Remove hooks restore that is no longer necessary --- lib/backup/repository.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 74b9e0205a4..02897a11fac 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -147,13 +147,6 @@ module Backup progress.puts "Error #{e}".color(:red) end end - - gitaly_migrate(:create_hooks) do |is_enabled| - # TODO: Need to find a way to do this for gitaly - unless is_enabled - Gitlab::Git::Repository.create_hooks(path_to_project_repo, Gitlab.config.gitlab_shell.hooks_path) - end - end end end # rubocop:enable Metrics/AbcSize -- cgit v1.2.3 From d5bd61e82f425288397b53ba077eb59782fb12ca Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Fri, 11 May 2018 14:07:56 -0700 Subject: Output project name before restoring each project repo --- lib/backup/repository.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 02897a11fac..0e12f70a066 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -96,6 +96,7 @@ module Backup prepare_directories gitlab_shell = Gitlab::Shell.new Project.find_each(batch_size: 1000) do |project| + progress.print " * #{project.name} ... " path_to_project_bundle = path_to_bundle(project) path_to_project_repo = path_to_repo(project) project.ensure_storage_path_exists -- cgit v1.2.3 From b8b82aa50e677d6e38b32080a65b48211350e9af Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Fri, 11 May 2018 15:09:26 -0700 Subject: Fix the paths for wiki restore --- lib/backup/repository.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 0e12f70a066..38751a1040e 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -96,7 +96,7 @@ module Backup prepare_directories gitlab_shell = Gitlab::Shell.new Project.find_each(batch_size: 1000) do |project| - progress.print " * #{project.name} ... " + progress.print " * #{project.full_path} ... " path_to_project_bundle = path_to_bundle(project) path_to_project_repo = path_to_repo(project) project.ensure_storage_path_exists @@ -116,9 +116,9 @@ module Backup end if restore_repo_status - progress.puts "[DONE] restoring #{project.name} repository".color(:green) + progress.puts "[DONE]".color(:green) else - progress.puts "[Failed] restoring #{project.name} repository".color(:red) + progress.puts "[Failed] restoring #{project.full_path} repository".color(:red) end gitaly_migrate(:restore_custom_hooks) do |is_enabled| @@ -139,12 +139,13 @@ module Backup path_to_wiki_bundle = path_to_bundle(wiki) if File.exist?(path_to_wiki_bundle) + progress.print " * #{wiki.full_path} ... " begin - gitlab_shell.remove_repository(project.wiki.repository_storage, project.wiki.disk_path) if project.wiki_repository_exists? - project.repository.create_from_bundle(path_to_wiki_bundle) - progress.puts "[DONE] restoring #{project.name} wiki".color(:green) + gitlab_shell.remove_repository(wiki.repository_storage, wiki.disk_path) if wiki.repository_exists? + wiki.repository.create_from_bundle(path_to_wiki_bundle) + progress.puts "[DONE]".color(:green) rescue StandardError => e - progress.puts "[Failed] restoring #{project.name} wiki".color(:red) + progress.puts "[Failed] restoring #{wiki.full_path} wiki".color(:red) progress.puts "Error #{e}".color(:red) end end -- cgit v1.2.3 From 889c62c1e3a2c11e726d1160d92ff83cb3e0fd7d Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Mon, 14 May 2018 13:49:04 -0700 Subject: Link to upstream gitaly issues for the missing restore paths --- lib/backup/repository.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 38751a1040e..ba464368042 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -70,6 +70,7 @@ module Backup Gitlab.config.repositories.storages.each do |name, repository_storage| gitaly_migrate(:remove_repositories) do |is_enabled| # TODO: Need to find a way to do this for gitaly + # Gitaly discussion issue: https://gitlab.com/gitlab-org/gitaly/issues/1194 unless is_enabled path = repository_storage.legacy_disk_path next unless File.exist?(path) @@ -101,21 +102,21 @@ module Backup path_to_project_repo = path_to_repo(project) project.ensure_storage_path_exists - restore_repo_status = nil + restore_repo_success = nil if File.exist?(path_to_project_bundle) begin gitlab_shell.remove_repository(project.repository_storage, project.disk_path) if project.repository_exists? project.repository.create_from_bundle path_to_project_bundle - restore_repo_status = true + restore_repo_success = true rescue => e - restore_repo_status = false + restore_repo_success = false progress.puts "Error: #{e}".color(:red) end else - restore_repo_status = gitlab_shell.create_repository(project.repository_storage, project.disk_path) + restore_repo_success = gitlab_shell.create_repository(project.repository_storage, project.disk_path) end - if restore_repo_status + if restore_repo_success progress.puts "[DONE]".color(:green) else progress.puts "[Failed] restoring #{project.full_path} repository".color(:red) @@ -123,6 +124,7 @@ module Backup gitaly_migrate(:restore_custom_hooks) do |is_enabled| # TODO: Need to find a way to do this for gitaly + # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195 unless is_enabled in_path(path_to_tars(project)) do |dir| cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) @@ -144,7 +146,7 @@ module Backup gitlab_shell.remove_repository(wiki.repository_storage, wiki.disk_path) if wiki.repository_exists? wiki.repository.create_from_bundle(path_to_wiki_bundle) progress.puts "[DONE]".color(:green) - rescue StandardError => e + rescue => e progress.puts "[Failed] restoring #{wiki.full_path} wiki".color(:red) progress.puts "Error #{e}".color(:red) end -- cgit v1.2.3 From 7ea4a42395eb09f2c2bac2ed74297fca6188fd3b Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Wed, 16 May 2018 17:17:05 +0300 Subject: Fix failing specs --- spec/lib/backup/repository_spec.rb | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb index b1ea9c0b622..3a568506c11 100644 --- a/spec/lib/backup/repository_spec.rb +++ b/spec/lib/backup/repository_spec.rb @@ -49,14 +49,14 @@ describe Backup::Repository do describe 'command failure' do before do - allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1]) + allow_any_instance_of(Gitlab::Shell).to receive(:create_repository).and_return(false) end context 'hashed storage' do it 'shows the appropriate error' do subject.restore - expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} (#{project.disk_path}) - error") + expect(progress).to have_received(:puts).with("[Failed] restoring #{project.full_path} repository") end end @@ -66,33 +66,10 @@ describe Backup::Repository do it 'shows the appropriate error' do subject.restore - expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} - error") + expect(progress).to have_received(:puts).with("[Failed] restoring #{project.full_path} repository") end end end - - describe 'folders without permissions' do - before do - allow(FileUtils).to receive(:mv).and_raise(Errno::EACCES) - end - - it 'shows error message' do - expect(subject).to receive(:access_denied_error) - subject.restore - end - end - - describe 'folder that is a mountpoint' do - before do - allow(FileUtils).to receive(:mv).and_raise(Errno::EBUSY) - end - - it 'shows error message' do - expect(subject).to receive(:resource_busy_error).and_call_original - - expect { subject.restore }.to raise_error(/is a mountpoint/) - end - end end describe '#empty_repo?' do -- cgit v1.2.3 From 51ee6f0b8ba1995f60299f7f399bed1420de5729 Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Wed, 16 May 2018 20:16:42 +0300 Subject: Changelog entry --- changelogs/unreleased/migrate-restore-repo-to-gitaly.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/migrate-restore-repo-to-gitaly.yml diff --git a/changelogs/unreleased/migrate-restore-repo-to-gitaly.yml b/changelogs/unreleased/migrate-restore-repo-to-gitaly.yml new file mode 100644 index 00000000000..59f375de20e --- /dev/null +++ b/changelogs/unreleased/migrate-restore-repo-to-gitaly.yml @@ -0,0 +1,5 @@ +--- +title: Support restoring repositories into gitaly +merge_request: +author: +type: changed -- cgit v1.2.3 From 19c63911340f81f3f94659f702abc5d2f1527870 Mon Sep 17 00:00:00 2001 From: lulalala Date: Tue, 17 Apr 2018 11:13:40 +0800 Subject: Update doc redirect link --- doc/workflow/merge_requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/workflow/merge_requests.md b/doc/workflow/merge_requests.md index a68bb8b27ca..dc6da1938f3 100644 --- a/doc/workflow/merge_requests.md +++ b/doc/workflow/merge_requests.md @@ -1 +1 @@ -This document was moved to [user/project/merge_requests](../user/project/merge_requests.md). +This document was moved to [user/project/merge_requests/index.md](../user/project/merge_requests/index.md). -- cgit v1.2.3 From bf669717a86e9bbe87ba1f97d4433b79081d2b97 Mon Sep 17 00:00:00 2001 From: lulalala Date: Wed, 28 Mar 2018 10:49:13 +0800 Subject: Update doc --- app/services/notification_service.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 55a1735e54b..8b3ddab5e19 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -129,6 +129,7 @@ class NotificationService # When create a merge request we should send an email to: # + # * mr author # * mr assignee if their notification level is not Disabled # * project team members with notification level higher then Participating # * watchers of the mr's labels -- cgit v1.2.3 From 7517105303c264484d8677c81268f9f43ecc5593 Mon Sep 17 00:00:00 2001 From: lulalala Date: Wed, 28 Mar 2018 11:20:12 +0800 Subject: Add cannot_be_merged_recheck merge_status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First, transitions between can_be_merged & cannot_be_merged are removed, as they are currently blocked in `check_if_can_be_merged`. `can_be_merge` always returns to `unchecked` first, before it can transition to `cannot_be_merged` (and vice versa). We want to avoid repeated notification triggered by repeated transition between `cannot_be_merged` & `unchecked`. So we added `cannot_be_merged_recheck` state, similar to `unchecked`, but as a mean to remember it’s from cannot_be_merged. See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18042/#note_65945407 Since `unchecked` and `cannot_be_merged_recheck` both mean “we are in the middle of checking if it is mergeable”, quite often we need to see if merge_status is in either one of them, so `check_state?` is added to achieve this. --- app/models/merge_request.rb | 14 +++++-- app/models/project_services/drone_ci_service.rb | 2 +- .../merge_requests/conflicts_controller_spec.rb | 2 +- .../projects/merge_requests_controller_spec.rb | 2 +- .../merge_request/user_resolves_conflicts_spec.rb | 2 +- spec/models/merge_request_spec.rb | 47 ++++++++++++++++++++++ .../merge_requests/conflicts/list_service_spec.rb | 2 +- 7 files changed, 62 insertions(+), 9 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 628c61d5d69..ea5057c2a78 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -104,24 +104,30 @@ class MergeRequest < ActiveRecord::Base state_machine :merge_status, initial: :unchecked do event :mark_as_unchecked do - transition [:can_be_merged, :cannot_be_merged] => :unchecked + transition [:can_be_merged] => :unchecked + transition [:cannot_be_merged] => :cannot_be_merged_recheck end event :mark_as_mergeable do - transition [:unchecked, :cannot_be_merged] => :can_be_merged + transition [:unchecked, :cannot_be_merged_recheck] => :can_be_merged end event :mark_as_unmergeable do - transition [:unchecked, :can_be_merged] => :cannot_be_merged + transition [:unchecked, :cannot_be_merged_recheck] => :cannot_be_merged end state :unchecked + state :cannot_be_merged_recheck state :can_be_merged state :cannot_be_merged around_transition do |merge_request, transition, block| Gitlab::Timeless.timeless(merge_request, &block) end + + def check_state?(merge_status) + [:unchecked, :cannot_be_merged_recheck].include?(merge_status.to_sym) + end end validates :source_project, presence: true, unless: [:allow_broken, :importing?, :closed_without_fork?] @@ -602,7 +608,7 @@ class MergeRequest < ActiveRecord::Base end def check_if_can_be_merged - return unless unchecked? && Gitlab::Database.read_write? + return unless self.class.state_machines[:merge_status].check_state?(merge_status) && Gitlab::Database.read_write? can_be_merged = !broken? && project.repository.can_be_merged?(diff_head_sha, target_branch) diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 71b10fc6bc1..a4bf427ac0b 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -115,6 +115,6 @@ class DroneCiService < CiService def merge_request_valid?(data) data[:object_attributes][:state] == 'opened' && - data[:object_attributes][:merge_status] == 'unchecked' + MergeRequest.state_machines[:merge_status].check_state?(data[:object_attributes][:merge_status]) end end diff --git a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb index 2d7647a6e12..397cc79bde4 100644 --- a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb @@ -5,7 +5,7 @@ describe Projects::MergeRequests::ConflictsController do let(:user) { project.owner } let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } let(:merge_request_with_conflicts) do - create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project) do |mr| + create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project, merge_status: :unchecked) do |mr| mr.mark_as_unmergeable end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index c8cc6b374f6..d3042be9e8b 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -7,7 +7,7 @@ describe Projects::MergeRequestsController do let(:user) { project.owner } let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } let(:merge_request_with_conflicts) do - create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project) do |mr| + create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project, merge_status: :unchecked) do |mr| mr.mark_as_unmergeable end end diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb index 19995559fae..ac3c734599c 100644 --- a/spec/features/merge_request/user_resolves_conflicts_spec.rb +++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb @@ -10,7 +10,7 @@ describe 'Merge request > User resolves conflicts', :js do end def create_merge_request(source_branch) - create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', source_project: project) do |mr| + create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', source_project: project, merge_status: :unchecked) do |mr| mr.mark_as_unmergeable end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 04379e7d2c3..bcc836acefa 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -2064,6 +2064,53 @@ describe MergeRequest do expect(subject.merge_jid).to be_nil end end + + describe 'transition to cannot_be_merged' do + let(:notification_service) { double(:notification_service) } + let(:todo_service) { double(:todo_service) } + + subject { create(:merge_request, merge_status: :unchecked) } + + before do + allow(NotificationService).to receive(:new).and_return(notification_service) + allow(TodoService).to receive(:new).and_return(todo_service) + end + + it 'notifies, but does not notify again if rechecking still results in cannot_be_merged' do + expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once + expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once + + subject.mark_as_unmergeable + subject.mark_as_unchecked + subject.mark_as_unmergeable + end + + it 'notifies whenever merge request is newly unmergeable' do + expect(notification_service).to receive(:merge_request_unmergeable).with(subject).twice + expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice + + subject.mark_as_unmergeable + subject.mark_as_unchecked + subject.mark_as_mergeable + subject.mark_as_unchecked + subject.mark_as_unmergeable + end + end + + describe 'check_state?' do + it 'indicates whether MR is still checking for mergeability' do + state_machine = described_class.state_machines[:merge_status] + check_states = [:unchecked, :cannot_be_merged_recheck] + + check_states.each do |merge_status| + expect(state_machine.check_state?(merge_status)).to be true + end + + (state_machine.states.map(&:name) - check_states).each do |merge_status| + expect(state_machine.check_state?(merge_status)).to be false + end + end + end end describe '#should_be_rebased?' do diff --git a/spec/services/merge_requests/conflicts/list_service_spec.rb b/spec/services/merge_requests/conflicts/list_service_spec.rb index 837b8a56d12..d57852615d9 100644 --- a/spec/services/merge_requests/conflicts/list_service_spec.rb +++ b/spec/services/merge_requests/conflicts/list_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe MergeRequests::Conflicts::ListService do describe '#can_be_resolved_in_ui?' do def create_merge_request(source_branch) - create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start') do |mr| + create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', merge_status: :unchecked) do |mr| mr.mark_as_unmergeable end end -- cgit v1.2.3 From 179a1ee7c669e758f8dc9fb5e9c8f2563012a6e1 Mon Sep 17 00:00:00 2001 From: lulalala Date: Wed, 9 May 2018 17:43:47 +0800 Subject: Add MergeRequest#merge_participants For notifying via todo or email. --- app/models/merge_request.rb | 10 ++++++++++ spec/models/merge_request_spec.rb | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index ea5057c2a78..dd4daf3711b 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -333,6 +333,16 @@ class MergeRequest < ActiveRecord::Base update_column(:merge_jid, jid) end + def merge_participants + participants = [author] + + if merge_when_pipeline_succeeds? && !participants.include?(merge_user) + participants << merge_user + end + + participants + end + def first_commit merge_request_diff ? merge_request_diff.first_commit : compare_commits.first end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index bcc836acefa..d1cc57fd103 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -2242,4 +2242,39 @@ describe MergeRequest do expect(merge_request.can_allow_maintainer_to_push?(user)).to be_truthy end end + + describe '#merge_participants' do + it 'contains author' do + expect(subject.merge_participants).to eq([subject.author]) + end + + describe 'when merge_when_pipeline_succeeds? is true' do + describe 'when merge user is author' do + let(:user) { create(:user) } + subject do + create(:merge_request, + merge_when_pipeline_succeeds: true, + merge_user: user, + author: user) + end + + it 'contains author only' do + expect(subject.merge_participants).to eq([subject.author]) + end + end + + describe 'when merge user and author are different users' do + let(:merge_user) { create(:user) } + subject do + create(:merge_request, + merge_when_pipeline_succeeds: true, + merge_user: merge_user) + end + + it 'contains author and merge user' do + expect(subject.merge_participants).to eq([subject.author, merge_user]) + end + end + end + end end -- cgit v1.2.3 From dc174e9655267e89e1b7c63f8c9f4dac069069c7 Mon Sep 17 00:00:00 2001 From: lulalala Date: Wed, 9 May 2018 17:49:33 +0800 Subject: Notify with email when merge request became unmergeable Display MR unmergeable reasons --- app/mailers/emails/merge_requests.rb | 8 +++++ app/models/merge_request.rb | 4 +++ app/presenters/merge_request_presenter.rb | 11 ++++++ app/services/notification_recipient_service.rb | 25 ++++++++++++++ app/services/notification_service.rb | 17 ++++++++++ .../merge_request_unmergeable_email.html.haml | 6 ++++ .../merge_request_unmergeable_email.text.haml | 11 ++++++ changelogs/unreleased/mr-conflict-notification.yml | 5 +++ doc/workflow/notifications.md | 4 +++ spec/mailers/notify_spec.rb | 39 ++++++++++++++++++++++ spec/presenters/merge_request_presenter_spec.rb | 35 +++++++++++++++++++ spec/services/notification_service_spec.rb | 26 +++++++++++++++ 12 files changed, 191 insertions(+) create mode 100644 app/views/notify/merge_request_unmergeable_email.html.haml create mode 100644 app/views/notify/merge_request_unmergeable_email.text.haml create mode 100644 changelogs/unreleased/mr-conflict-notification.yml diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index b3f2aeb08ca..c2333180663 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -56,6 +56,14 @@ module Emails mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason)) end + def merge_request_unmergeable_email(recipient_id, merge_request_id, reason = nil) + setup_merge_request_mail(merge_request_id, recipient_id) + + @merge_request_presenter = MergeRequestPresenter.new(@merge_request, current_user: current_user) + + mail_answer_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason)) + end + def resolved_all_discussions_email(recipient_id, merge_request_id, resolved_by_user_id, reason = nil) setup_merge_request_mail(merge_request_id, recipient_id) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index dd4daf3711b..99687d305e7 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -125,6 +125,10 @@ class MergeRequest < ActiveRecord::Base Gitlab::Timeless.timeless(merge_request, &block) end + after_transition unchecked: :cannot_be_merged do |merge_request, transition| + NotificationService.new.merge_request_unmergeable(merge_request) + end + def check_state?(merge_status) [:unchecked, :cannot_be_merged_recheck].include?(merge_status.to_sym) end diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index 4b4132af2d0..ad839d9840a 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -20,6 +20,17 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated end end + def unmergeable_reasons + strong_memoize(:unmergeable_reasons) do + reasons = [] + reasons << "no commits" if merge_request.has_no_commits? + reasons << "source branch is missing" unless merge_request.source_branch_exists? + reasons << "target branch is missing" unless merge_request.target_branch_exists? + reasons << "has merge conflicts" unless merge_request.project.repository.can_be_merged?(merge_request.diff_head_sha, merge_request.target_branch) + reasons + end + end + def cancel_merge_when_pipeline_succeeds_path if can_cancel_merge_when_pipeline_succeeds?(current_user) cancel_merge_when_pipeline_succeeds_project_merge_request_path(project, merge_request) diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb index 5658699664d..4fa38665abc 100644 --- a/app/services/notification_recipient_service.rb +++ b/app/services/notification_recipient_service.rb @@ -18,6 +18,10 @@ module NotificationRecipientService Builder::NewNote.new(*a).notification_recipients end + def self.build_merge_request_unmergeable_recipients(*a) + Builder::MergeRequestUnmergeable.new(*a).notification_recipients + end + module Builder class Base def initialize(*) @@ -330,5 +334,26 @@ module NotificationRecipientService note.author end end + + class MergeRequestUnmergeable < Base + attr_reader :target + def initialize(merge_request) + @target = merge_request + end + + def build! + target.merge_participants.each do |user| + add_recipients(user, :participating, nil) + end + end + + def custom_action + :unmergeable_merge_request + end + + def acting_user + nil + end + end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 8b3ddab5e19..636cfbf5b45 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -149,6 +149,15 @@ class NotificationService end end + # When a merge request is found to be unmergeable, we should send an email to: + # + # * mr author + # * mr merge user if set + # + def merge_request_unmergeable(merge_request) + merge_request_unmergeable_email(merge_request) + end + # When merge request text is updated, we should send an email to: # # * newly mentioned project team members with notification level higher than Participating @@ -485,6 +494,14 @@ class NotificationService end end + def merge_request_unmergeable_email(merge_request) + recipients = NotificationRecipientService.build_merge_request_unmergeable_recipients(merge_request) + + recipients.each do |recipient| + mailer.merge_request_unmergeable_email(recipient.user.id, merge_request.id).deliver_later + end + end + def mailer Notify end diff --git a/app/views/notify/merge_request_unmergeable_email.html.haml b/app/views/notify/merge_request_unmergeable_email.html.haml new file mode 100644 index 00000000000..f51a31c0eda --- /dev/null +++ b/app/views/notify/merge_request_unmergeable_email.html.haml @@ -0,0 +1,6 @@ +%p + Merge Request #{link_to @merge_request.to_reference, project_merge_request_url(@merge_request.target_project, @merge_request)} can no longer be merged due to the following reason(s): + + %ul + - @merge_request_presenter.unmergeable_reasons.each do |reason| + %li= reason diff --git a/app/views/notify/merge_request_unmergeable_email.text.haml b/app/views/notify/merge_request_unmergeable_email.text.haml new file mode 100644 index 00000000000..2acf0de34fc --- /dev/null +++ b/app/views/notify/merge_request_unmergeable_email.text.haml @@ -0,0 +1,11 @@ +Merge Request #{@merge_request.to_reference} can no longer be merged due to the following reason(s): + +- @merge_request_presenter.unmergeable_reasons.each do |reason| + * #{reason} + +Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} + += merge_path_description(@merge_request, 'to') + +Author: #{@merge_request.author_name} +Assignee: #{@merge_request.assignee_name} diff --git a/changelogs/unreleased/mr-conflict-notification.yml b/changelogs/unreleased/mr-conflict-notification.yml new file mode 100644 index 00000000000..d3d5f1fc373 --- /dev/null +++ b/changelogs/unreleased/mr-conflict-notification.yml @@ -0,0 +1,5 @@ +--- +title: When MR becomes unmergeable, notify and create todo for author and merge user +merge_request: 18042 +author: +type: added diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md index f1501c81b27..fc592b99860 100644 --- a/doc/workflow/notifications.md +++ b/doc/workflow/notifications.md @@ -106,6 +106,10 @@ by yourself (except when an issue is due). You will only receive automatic notifications when somebody else comments or adds changes to the ones that you've created or mentions you. +If a merge request becomes unmergeable, its author will be notified about the cause. +If a user has also set the merge request to automatically merge once pipeline succeeds, +then that user will also be notified. + ### Email Headers Notification emails include headers that provide extra content about the notification received: diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 84ddbbbf2ee..bed7675db4f 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -390,6 +390,45 @@ describe Notify do end end + describe 'that are unmergeable' do + set(:merge_request) do + create(:merge_request, :conflict, + source_project: project, + target_project: project, + author: current_user, + assignee: assignee, + description: 'Awesome description') + end + + subject { described_class.merge_request_unmergeable_email(recipient.id, merge_request.id) } + + it_behaves_like 'a multiple recipients email' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { merge_request } + end + it_behaves_like 'it should show Gmail Actions View Merge request link' + it_behaves_like 'an unsubscribeable thread' + + it 'is sent as the merge request author' do + sender = subject.header[:from].addrs[0] + expect(sender.display_name).to eq(merge_request.author.name) + expect(sender.address).to eq(gitlab_sender) + end + + it 'has the correct subject and body' do + reasons = %w[foo bar] + + allow_any_instance_of(MergeRequestPresenter).to receive(:unmergeable_reasons).and_return(reasons) + aggregate_failures do + is_expected.to have_referable_subject(merge_request, reply: true) + is_expected.to have_body_text(project_merge_request_path(project, merge_request)) + reasons.each do |reason| + is_expected.to have_body_text(reason) + end + end + end + end + shared_examples 'a push to an existing merge request' do let(:push_user) { create(:user) } diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb index e3b37739e8e..d5fb4a7742c 100644 --- a/spec/presenters/merge_request_presenter_spec.rb +++ b/spec/presenters/merge_request_presenter_spec.rb @@ -70,6 +70,41 @@ describe MergeRequestPresenter do end end + describe "#unmergeable_reasons" do + let(:presenter) { described_class.new(resource, current_user: user) } + + before do + # Mergeable base state + allow(resource).to receive(:has_no_commits?).and_return(false) + allow(resource).to receive(:source_branch_exists?).and_return(true) + allow(resource).to receive(:target_branch_exists?).and_return(true) + allow(resource.project.repository).to receive(:can_be_merged?).and_return(true) + end + + it "handles mergeable request" do + expect(presenter.unmergeable_reasons).to eq([]) + end + + it "handles no commit" do + allow(resource).to receive(:has_no_commits?).and_return(true) + + expect(presenter.unmergeable_reasons).to eq(["no commits"]) + end + + it "handles branches missing" do + allow(resource).to receive(:source_branch_exists?).and_return(false) + allow(resource).to receive(:target_branch_exists?).and_return(false) + + expect(presenter.unmergeable_reasons).to eq(["source branch is missing", "target branch is missing"]) + end + + it "handles merge conflict" do + allow(resource.project.repository).to receive(:can_be_merged?).and_return(false) + + expect(presenter.unmergeable_reasons).to eq(["has merge conflicts"]) + end + end + describe '#conflict_resolution_path' do let(:project) { create :project } let(:user) { create :user } diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 5f28bc123f3..831678b949d 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -1224,6 +1224,32 @@ describe NotificationService, :mailer do end end + describe '#merge_request_unmergeable' do + it "sends email to merge request author" do + notification.merge_request_unmergeable(merge_request) + + should_email(merge_request.author) + expect(email_recipients.size).to eq(1) + end + + describe 'when merge_when_pipeline_succeeds is true' do + before do + merge_request.update_attributes( + merge_when_pipeline_succeeds: true, + merge_user: create(:user) + ) + end + + it "sends email to merge request author and merge_user" do + notification.merge_request_unmergeable(merge_request) + + should_email(merge_request.author) + should_email(merge_request.merge_user) + expect(email_recipients.size).to eq(2) + end + end + end + describe '#closed_merge_request' do before do update_custom_notification(:close_merge_request, @u_guest_custom, resource: project) -- cgit v1.2.3 From 953bb41f25270c07ab12c17472ef0fe8ab848301 Mon Sep 17 00:00:00 2001 From: lulalala Date: Wed, 9 May 2018 17:55:00 +0800 Subject: Create TODO when MR became unmergeable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Old behavior of creating TODO when “Merge When Pipeline Succeeds” service fails, is generalized to: Create a TODO whenever MR became unmergeable (and similar to notification, MR author and merge_user are both applicable) --- app/models/merge_request.rb | 1 + .../merge_when_pipeline_succeeds_service.rb | 6 +---- app/services/todo_service.rb | 30 ++++++++++++---------- doc/workflow/todos.md | 3 +++ .../merge_when_pipeline_succeeds_service_spec.rb | 26 ------------------- spec/services/todo_service_spec.rb | 25 ++++++++++++------ 6 files changed, 38 insertions(+), 53 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 99687d305e7..a0ad7f3c609 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -127,6 +127,7 @@ class MergeRequest < ActiveRecord::Base after_transition unchecked: :cannot_be_merged do |merge_request, transition| NotificationService.new.merge_request_unmergeable(merge_request) + TodoService.new.merge_request_became_unmergeable(merge_request) end def check_state?(merge_status) diff --git a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb index 850deb0ac7a..9a4e6eb2e88 100644 --- a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb +++ b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb @@ -24,11 +24,7 @@ module MergeRequests pipeline_merge_requests(pipeline) do |merge_request| next unless merge_request.merge_when_pipeline_succeeds? - - unless merge_request.mergeable? - todo_service.merge_request_became_unmergeable(merge_request) - next - end + next unless merge_request.mergeable? merge_request.merge_async(merge_request.merge_user_id, merge_request.merge_params) end diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index ffd48e842c2..f91cd03bf5c 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -98,12 +98,12 @@ class TodoService # When a build fails on the HEAD of a merge request we should: # - # * create a todo for author of MR to fix it - # * create a todo for merge_user to keep an eye on it + # * create a todo for each merge participant # def merge_request_build_failed(merge_request) - create_build_failed_todo(merge_request, merge_request.author) - create_build_failed_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds? + merge_request.merge_participants.each do |user| + create_build_failed_todo(merge_request, user) + end end # When a new commit is pushed to a merge request we should: @@ -116,20 +116,22 @@ class TodoService # When a build is retried to a merge request we should: # - # * mark all pending todos related to the merge request for the author as done - # * mark all pending todos related to the merge request for the merge_user as done + # * mark all pending todos related to the merge request as done for each merge participant # def merge_request_build_retried(merge_request) - mark_pending_todos_as_done(merge_request, merge_request.author) - mark_pending_todos_as_done(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds? + merge_request.merge_participants.each do |user| + mark_pending_todos_as_done(merge_request, user) + end end - # When a merge request could not be automatically merged due to its unmergeable state we should: + # When a merge request could not be merged due to its unmergeable state we should: # - # * create a todo for a merge_user + # * create a todo for each merge participant # def merge_request_became_unmergeable(merge_request) - create_unmergeable_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds? + merge_request.merge_participants.each do |user| + create_unmergeable_todo(merge_request, user) + end end # When create a note we should: @@ -275,9 +277,9 @@ class TodoService create_todos(todo_author, attributes) end - def create_unmergeable_todo(merge_request, merge_user) - attributes = attributes_for_todo(merge_request.project, merge_request, merge_user, Todo::UNMERGEABLE) - create_todos(merge_user, attributes) + def create_unmergeable_todo(merge_request, todo_author) + attributes = attributes_for_todo(merge_request.project, merge_request, todo_author, Todo::UNMERGEABLE) + create_todos(todo_author, attributes) end def attributes_for_target(target) diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md index f13d29884d4..762bf616268 100644 --- a/doc/workflow/todos.md +++ b/doc/workflow/todos.md @@ -31,6 +31,9 @@ A Todo appears in your Todos dashboard when: - you are `@mentioned` in a comment on a commit, - a job in the CI pipeline running for your merge request failed, but this job is not allowed to fail. +- a merge request becomes unmergeable, and you are either: + - the author, or + - have set it to automatically merge once pipeline succeeds. ### Directly addressed Todos diff --git a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb index 240aa638f79..8838742a637 100644 --- a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb +++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb @@ -112,32 +112,6 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do service.trigger(unrelated_pipeline) end end - - context 'when the merge request is not mergeable' do - let(:mr_conflict) do - create(:merge_request, merge_when_pipeline_succeeds: true, merge_user: user, - source_branch: 'master', target_branch: 'feature-conflict', - source_project: project, target_project: project) - end - - let(:conflict_pipeline) do - create(:ci_pipeline, project: project, ref: mr_conflict.source_branch, - sha: mr_conflict.diff_head_sha, status: 'success', - head_pipeline_of: mr_conflict) - end - - it 'does not merge the merge request' do - expect(MergeWorker).not_to receive(:perform_async) - - service.trigger(conflict_pipeline) - end - - it 'creates todos for unmergeability' do - expect_any_instance_of(TodoService).to receive(:merge_request_became_unmergeable).with(mr_conflict) - - service.trigger(conflict_pipeline) - end - end end describe "#cancel" do diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index 562b89e6767..9a51c873b30 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -721,17 +721,18 @@ describe TodoService do end describe '#merge_request_build_failed' do - it 'creates a pending todo for the merge request author' do - service.merge_request_build_failed(mr_unassigned) + let(:merge_participants) { [mr_unassigned.author, admin] } - should_create_todo(user: author, target: mr_unassigned, action: Todo::BUILD_FAILED) + before do + allow(mr_unassigned).to receive(:merge_participants).and_return(merge_participants) end - it 'creates a pending todo for merge_user' do - mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin) + it 'creates a pending todo for each merge_participant' do service.merge_request_build_failed(mr_unassigned) - should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::BUILD_FAILED) + merge_participants.each do |participant| + should_create_todo(user: participant, author: participant, target: mr_unassigned, action: Todo::BUILD_FAILED) + end end end @@ -747,11 +748,19 @@ describe TodoService do end describe '#merge_request_became_unmergeable' do - it 'creates a pending todo for a merge_user' do + let(:merge_participants) { [admin, create(:user)] } + + before do + allow(mr_unassigned).to receive(:merge_participants).and_return(merge_participants) + end + + it 'creates a pending todo for each merge_participant' do mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin) service.merge_request_became_unmergeable(mr_unassigned) - should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::UNMERGEABLE) + merge_participants.each do |participant| + should_create_todo(user: participant, author: participant, target: mr_unassigned, action: Todo::UNMERGEABLE) + end end end -- cgit v1.2.3 From 42cabcb1c45dc8f0d38c36c409bf0efbc9111a67 Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Thu, 17 May 2018 15:34:31 +0300 Subject: Call path_to_repo only when using gitaly --- lib/backup/repository.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index ba464368042..bc517191939 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -99,7 +99,6 @@ module Backup Project.find_each(batch_size: 1000) do |project| progress.print " * #{project.full_path} ... " path_to_project_bundle = path_to_bundle(project) - path_to_project_repo = path_to_repo(project) project.ensure_storage_path_exists restore_repo_success = nil @@ -127,6 +126,7 @@ module Backup # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195 unless is_enabled in_path(path_to_tars(project)) do |dir| + path_to_project_repo = path_to_repo(project) cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) output, status = Gitlab::Popen.popen(cmd) -- cgit v1.2.3 From 62f29887136ba39eb2c0607eba250d488ced2611 Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Thu, 17 May 2018 15:36:58 +0300 Subject: Return from prepare directory if gitaly skip is enabled --- lib/backup/repository.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index bc517191939..2209a305d91 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -67,6 +67,8 @@ module Backup end def prepare_directories + return if GitalyClient.feature_enabled?(:backup_skip_prepare_directories) + Gitlab.config.repositories.storages.each do |name, repository_storage| gitaly_migrate(:remove_repositories) do |is_enabled| # TODO: Need to find a way to do this for gitaly -- cgit v1.2.3 From a660f548966bc8232cb56c4e344b0da3c91b3701 Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Fri, 18 May 2018 09:50:01 +0800 Subject: Plural reason(s) --- app/mailers/emails/merge_requests.rb | 2 +- app/views/notify/merge_request_unmergeable_email.html.haml | 4 ++-- app/views/notify/merge_request_unmergeable_email.text.haml | 4 ++-- spec/mailers/notify_spec.rb | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index c2333180663..5ba3a4a322c 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -59,7 +59,7 @@ module Emails def merge_request_unmergeable_email(recipient_id, merge_request_id, reason = nil) setup_merge_request_mail(merge_request_id, recipient_id) - @merge_request_presenter = MergeRequestPresenter.new(@merge_request, current_user: current_user) + @reasons = MergeRequestPresenter.new(@merge_request, current_user: current_user).unmergeable_reasons mail_answer_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason)) end diff --git a/app/views/notify/merge_request_unmergeable_email.html.haml b/app/views/notify/merge_request_unmergeable_email.html.haml index f51a31c0eda..e84905a5fad 100644 --- a/app/views/notify/merge_request_unmergeable_email.html.haml +++ b/app/views/notify/merge_request_unmergeable_email.html.haml @@ -1,6 +1,6 @@ %p - Merge Request #{link_to @merge_request.to_reference, project_merge_request_url(@merge_request.target_project, @merge_request)} can no longer be merged due to the following reason(s): + Merge Request #{link_to @merge_request.to_reference, project_merge_request_url(@merge_request.target_project, @merge_request)} can no longer be merged due to the following #{pluralize(@reasons, 'reason')}: %ul - - @merge_request_presenter.unmergeable_reasons.each do |reason| + - @reasons.each do |reason| %li= reason diff --git a/app/views/notify/merge_request_unmergeable_email.text.haml b/app/views/notify/merge_request_unmergeable_email.text.haml index 2acf0de34fc..4f0901c761a 100644 --- a/app/views/notify/merge_request_unmergeable_email.text.haml +++ b/app/views/notify/merge_request_unmergeable_email.text.haml @@ -1,6 +1,6 @@ -Merge Request #{@merge_request.to_reference} can no longer be merged due to the following reason(s): +Merge Request #{@merge_request.to_reference} can no longer be merged due to the following #{pluralize(@reasons, 'reason')}: -- @merge_request_presenter.unmergeable_reasons.each do |reason| +- @reasons.each do |reason| * #{reason} Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index bed7675db4f..e9b3ccf31fb 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -422,6 +422,7 @@ describe Notify do aggregate_failures do is_expected.to have_referable_subject(merge_request, reply: true) is_expected.to have_body_text(project_merge_request_path(project, merge_request)) + is_expected.to have_body_text('reasons:') reasons.each do |reason| is_expected.to have_body_text(reason) end -- cgit v1.2.3 From 125be1865b224c0a34ab8a1bbd62ac33b10e1d9b Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Thu, 17 May 2018 15:39:59 +0300 Subject: Move restore_custom_hooks to its seperate function --- lib/backup/repository.rb | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 2209a305d91..3e7590e55e3 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -67,7 +67,7 @@ module Backup end def prepare_directories - return if GitalyClient.feature_enabled?(:backup_skip_prepare_directories) + return if Gitlab::GitalyClient.feature_enabled?(:backup_skip_prepare_directories) Gitlab.config.repositories.storages.each do |name, repository_storage| gitaly_migrate(:remove_repositories) do |is_enabled| @@ -95,6 +95,24 @@ module Backup end end + def restore_custom_hooks + gitaly_migrate(:restore_custom_hooks) do |is_enabled| + # TODO: Need to find a way to do this for gitaly + # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195 + unless is_enabled + in_path(path_to_tars(project)) do |dir| + path_to_project_repo = path_to_repo(project) + cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) + + output, status = Gitlab::Popen.popen(cmd) + unless status.zero? + progress_warn(project, cmd.join(' '), output) + end + end + end + end + end + def restore prepare_directories gitlab_shell = Gitlab::Shell.new @@ -123,21 +141,7 @@ module Backup progress.puts "[Failed] restoring #{project.full_path} repository".color(:red) end - gitaly_migrate(:restore_custom_hooks) do |is_enabled| - # TODO: Need to find a way to do this for gitaly - # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195 - unless is_enabled - in_path(path_to_tars(project)) do |dir| - path_to_project_repo = path_to_repo(project) - cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) - - output, status = Gitlab::Popen.popen(cmd) - unless status.zero? - progress_warn(project, cmd.join(' '), output) - end - end - end - end + restore_custom_hooks unless Gitlab::GitalyClient.feature_enabled?(:backup_skip_restore_custom_hooks) wiki = ProjectWiki.new(project) path_to_wiki_bundle = path_to_bundle(wiki) -- cgit v1.2.3 From b1ab1609b66f6cb47bb7d7b3babdd29cd0aae3c4 Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Fri, 18 May 2018 17:02:35 +0800 Subject: Avoid race condition of re-triggering mark_as_unchecked --- app/models/merge_request.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index a0ad7f3c609..9c4384a6e42 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -104,8 +104,8 @@ class MergeRequest < ActiveRecord::Base state_machine :merge_status, initial: :unchecked do event :mark_as_unchecked do - transition [:can_be_merged] => :unchecked - transition [:cannot_be_merged] => :cannot_be_merged_recheck + transition [:can_be_merged, :unchecked] => :unchecked + transition [:cannot_be_merged, :cannot_be_merged_recheck] => :cannot_be_merged_recheck end event :mark_as_mergeable do -- cgit v1.2.3 From 80197bdc6212a610f96062b8b4c49771f00c87f3 Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Mon, 21 May 2018 11:45:41 +0200 Subject: Remove gitaly_migrate blocks and replace by early return --- lib/backup/repository.rb | 59 +++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 3e7590e55e3..a6e9450796f 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -67,48 +67,41 @@ module Backup end def prepare_directories + # TODO: Need to find a way to do this for gitaly + # Gitaly discussion issue: https://gitlab.com/gitlab-org/gitaly/issues/1194 + return if Gitlab::GitalyClient.feature_enabled?(:backup_skip_prepare_directories) Gitlab.config.repositories.storages.each do |name, repository_storage| - gitaly_migrate(:remove_repositories) do |is_enabled| - # TODO: Need to find a way to do this for gitaly - # Gitaly discussion issue: https://gitlab.com/gitlab-org/gitaly/issues/1194 - unless is_enabled - path = repository_storage.legacy_disk_path - next unless File.exist?(path) - - # Move all files in the existing repos directory except . and .. to - # repositories.old. directory - bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s) - FileUtils.mkdir_p(bk_repos_path, mode: 0700) - files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")] - - begin - FileUtils.mv(files, bk_repos_path) - rescue Errno::EACCES - access_denied_error(path) - rescue Errno::EBUSY - resource_busy_error(path) - end - end + path = repository_storage.legacy_disk_path + next unless File.exist?(path) + + # Move all files in the existing repos directory except . and .. to + # repositories.old. directory + bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s) + FileUtils.mkdir_p(bk_repos_path, mode: 0700) + files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")] + + begin + FileUtils.mv(files, bk_repos_path) + rescue Errno::EACCES + access_denied_error(path) + rescue Errno::EBUSY + resource_busy_error(path) end end end def restore_custom_hooks - gitaly_migrate(:restore_custom_hooks) do |is_enabled| - # TODO: Need to find a way to do this for gitaly - # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195 - unless is_enabled - in_path(path_to_tars(project)) do |dir| - path_to_project_repo = path_to_repo(project) - cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) + # TODO: Need to find a way to do this for gitaly + # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195 + in_path(path_to_tars(project)) do |dir| + path_to_project_repo = path_to_repo(project) + cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) - output, status = Gitlab::Popen.popen(cmd) - unless status.zero? - progress_warn(project, cmd.join(' '), output) - end - end + output, status = Gitlab::Popen.popen(cmd) + unless status.zero? + progress_warn(project, cmd.join(' '), output) end end end -- cgit v1.2.3 From b98a88cfe9033b5d694ac237a50ad4fe923ad95d Mon Sep 17 00:00:00 2001 From: James Edwards-Jones Date: Mon, 23 Apr 2018 00:17:49 +0100 Subject: Backport helpers from GroupSAML failure messages --- app/controllers/omniauth_callbacks_controller.rb | 4 ++-- spec/support/helpers/login_helpers.rb | 8 ++++++++ spec/support/helpers/routes_helpers.rb | 7 +++++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 spec/support/helpers/routes_helpers.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index ed89bed029b..27fd5f7ba37 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -26,11 +26,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController # Extend the standard message generation to accept our custom exception def failure_message - exception = env["omniauth.error"] + exception = request.env["omniauth.error"] error = exception.error_reason if exception.respond_to?(:error_reason) error ||= exception.error if exception.respond_to?(:error) error ||= exception.message if exception.respond_to?(:message) - error ||= env["omniauth.error.type"].to_s + error ||= request.env["omniauth.error.type"].to_s error.to_s.humanize if error end diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index 72e5c2d66dd..f7b71bf42e3 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -132,6 +132,14 @@ module LoginHelpers env['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym] end + def stub_omniauth_failure(strategy, message_key, exception = nil) + env = @request.env + + env['omniauth.error'] = exception + env['omniauth.error.type'] = message_key.to_sym + env['omniauth.error.strategy'] = strategy + end + def stub_omniauth_saml_config(messages) set_devise_mapping(context: Rails.application) Rails.application.routes.disable_clear_and_finalize = true diff --git a/spec/support/helpers/routes_helpers.rb b/spec/support/helpers/routes_helpers.rb new file mode 100644 index 00000000000..c4129606418 --- /dev/null +++ b/spec/support/helpers/routes_helpers.rb @@ -0,0 +1,7 @@ +module RoutesHelpers + def fake_routes(&block) + @routes = @routes.dup + @routes.formatter.clear + ActionDispatch::Routing::Mapper.new(@routes).instance_exec(&block) + end +end -- cgit v1.2.3 From 7af283c74d6bc36d2d3a963ddc4b831e9e1e8a98 Mon Sep 17 00:00:00 2001 From: Jacopo Date: Fri, 18 May 2018 17:10:26 +0200 Subject: Closes MR check out branch modal with escape --- .../merge_requests/_how_to_merge.html.haml | 2 +- .../45850-close-mr-checkout-modal-on-escape.yml | 5 +++++ .../user_sees_check_out_branch_modal_spec.rb | 24 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/45850-close-mr-checkout-modal-on-escape.yml create mode 100644 spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml index 54a661040ea..779034fa173 100644 --- a/app/views/projects/merge_requests/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/_how_to_merge.html.haml @@ -1,4 +1,4 @@ -#modal_merge_info.modal +#modal_merge_info.modal{ tabindex: '-1' } .modal-dialog .modal-content .modal-header diff --git a/changelogs/unreleased/45850-close-mr-checkout-modal-on-escape.yml b/changelogs/unreleased/45850-close-mr-checkout-modal-on-escape.yml new file mode 100644 index 00000000000..c3955c9d8b3 --- /dev/null +++ b/changelogs/unreleased/45850-close-mr-checkout-modal-on-escape.yml @@ -0,0 +1,5 @@ +--- +title: Closes MR check out branch modal with escape +merge_request: Jacopo Beschi @jacopo-beschi +author: 19050 +type: added diff --git a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb new file mode 100644 index 00000000000..c40c720d168 --- /dev/null +++ b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +describe 'Merge request > User sees Check out branch modal', :js do + let(:project) { create(:project, :public, :repository) } + let(:user) { project.creator } + let(:merge_request) { create(:merge_request, source_project: project) } + + before do + sign_in(user) + visit project_merge_request_path(project, merge_request) + wait_for_requests + click_button('Check out branch') + end + + it 'shows the check out branch modal' do + expect(page).to have_content('Check out, review, and merge locally') + end + + it 'closes the check out branch model with Escape keypress' do + find('#modal_merge_info').send_keys(:escape) + + expect(page).not_to have_content('Check out, review, and merge locally') + end +end -- cgit v1.2.3 From 9c4f967785d05b45ce33ece043c6bacb9d798ca1 Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Wed, 23 May 2018 12:31:53 +0200 Subject: Take restoring custom hooks and the skip flag out of scope --- lib/backup/repository.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index a6e9450796f..847d14d01c3 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -70,8 +70,6 @@ module Backup # TODO: Need to find a way to do this for gitaly # Gitaly discussion issue: https://gitlab.com/gitlab-org/gitaly/issues/1194 - return if Gitlab::GitalyClient.feature_enabled?(:backup_skip_prepare_directories) - Gitlab.config.repositories.storages.each do |name, repository_storage| path = repository_storage.legacy_disk_path next unless File.exist?(path) @@ -92,7 +90,7 @@ module Backup end end - def restore_custom_hooks + def restore_custom_hooks(project) # TODO: Need to find a way to do this for gitaly # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195 in_path(path_to_tars(project)) do |dir| @@ -134,7 +132,7 @@ module Backup progress.puts "[Failed] restoring #{project.full_path} repository".color(:red) end - restore_custom_hooks unless Gitlab::GitalyClient.feature_enabled?(:backup_skip_restore_custom_hooks) + restore_custom_hooks(project) wiki = ProjectWiki.new(project) path_to_wiki_bundle = path_to_bundle(wiki) -- cgit v1.2.3 From ec31fa260b9f438fe7e369c6f61d55c4f10441cc Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 23 May 2018 18:06:06 +0800 Subject: Avoid using expect_any_instance_of We can't use it for prepended module, so use `and_wrap_original` to wrap around the instance. --- spec/workers/update_merge_requests_worker_spec.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/workers/update_merge_requests_worker_spec.rb b/spec/workers/update_merge_requests_worker_spec.rb index 0fa19ac84bb..80137815d2b 100644 --- a/spec/workers/update_merge_requests_worker_spec.rb +++ b/spec/workers/update_merge_requests_worker_spec.rb @@ -18,8 +18,13 @@ describe UpdateMergeRequestsWorker do end it 'executes MergeRequests::RefreshService with expected values' do - expect(MergeRequests::RefreshService).to receive(:new).with(project, user).and_call_original - expect_any_instance_of(MergeRequests::RefreshService).to receive(:execute).with(oldrev, newrev, ref) + expect(MergeRequests::RefreshService).to receive(:new) + .with(project, user).and_wrap_original do |method, *args| + method.call(*args).tap do |refresh_service| + expect(refresh_service) + .to receive(:execute).with(oldrev, newrev, ref) + end + end perform end -- cgit v1.2.3 From 0230ca839a2ec8debcbe4476c3deed8b422bf451 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Tue, 22 May 2018 12:24:14 +0200 Subject: Check if note's noteable is not nil when checking resolvability This can occur when a note is added to a commit and then this commit is deleted. --- app/models/concerns/resolvable_note.rb | 2 +- changelogs/unreleased/jprovazn-fix-resolvable.yml | 5 +++++ spec/models/concerns/resolvable_note_spec.rb | 8 ++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/jprovazn-fix-resolvable.yml diff --git a/app/models/concerns/resolvable_note.rb b/app/models/concerns/resolvable_note.rb index 668c5a079e3..4a0f8b92b3a 100644 --- a/app/models/concerns/resolvable_note.rb +++ b/app/models/concerns/resolvable_note.rb @@ -32,7 +32,7 @@ module ResolvableNote # Keep this method in sync with the `potentially_resolvable` scope def potentially_resolvable? - RESOLVABLE_TYPES.include?(self.class.name) && noteable.supports_resolvable_notes? + RESOLVABLE_TYPES.include?(self.class.name) && noteable&.supports_resolvable_notes? end # Keep this method in sync with the `resolvable` scope diff --git a/changelogs/unreleased/jprovazn-fix-resolvable.yml b/changelogs/unreleased/jprovazn-fix-resolvable.yml new file mode 100644 index 00000000000..e17c409e290 --- /dev/null +++ b/changelogs/unreleased/jprovazn-fix-resolvable.yml @@ -0,0 +1,5 @@ +--- +title: Fix resolvable check if note's commit could not be found. +merge_request: +author: +type: fixed diff --git a/spec/models/concerns/resolvable_note_spec.rb b/spec/models/concerns/resolvable_note_spec.rb index 91591017587..fcb5250278e 100644 --- a/spec/models/concerns/resolvable_note_spec.rb +++ b/spec/models/concerns/resolvable_note_spec.rb @@ -326,4 +326,12 @@ describe Note, ResolvableNote do end end end + + describe "#potentially_resolvable?" do + it 'returns false if noteable could not be found' do + allow(subject).to receive(:noteable).and_return(nil) + + expect(subject.potentially_resolvable?).to be_falsey + end + end end -- cgit v1.2.3 From 25aec20fab63946dfe1c5b9ae544e15cfcff2b81 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 23 May 2018 17:01:00 +0100 Subject: Show loading icon only when making the request --- .../javascripts/pipelines/components/pipelines_table.vue | 3 +++ .../javascripts/pipelines/components/pipelines_table_row.vue | 12 +++++++++--- spec/javascripts/pipelines/pipelines_table_row_spec.js | 11 ++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue index 41986b827cd..4318abe97e0 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue @@ -37,6 +37,7 @@ return { pipelineId: '', endpoint: '', + cancelingPipeline: null, }; }, computed: { @@ -64,6 +65,7 @@ }, onSubmit() { eventHub.$emit('postAction', this.endpoint); + this.cancelingPipeline = this.pipelineId; }, }, }; @@ -106,6 +108,7 @@ :update-graph-dropdown="updateGraphDropdown" :auto-devops-help-path="autoDevopsHelpPath" :view-type="viewType" + :canceling-pipeline="cancelingPipeline" /> { }); component.$el.querySelector('.js-pipelines-cancel-button').click(); - expect(component.isCancelling).toEqual(true); + }); + + it('renders a loading icon when `cancelingPipeline` matches pipeline id', done => { + component.cancelingPipeline = pipeline.id; + component.$nextTick() + .then(() => { + expect(component.isCancelling).toEqual(true); + }) + .then(done) + .catch(done.fail); }); }); }); -- cgit v1.2.3 From dd639aad8d0d24386c6b7bba0ec4a120dbd2027b Mon Sep 17 00:00:00 2001 From: Olivier Gonzalez Date: Fri, 18 May 2018 12:47:51 -0400 Subject: Rename code quality job and artifact. Refs gitlab-org/gitlab-ee#5779 --- .gitlab-ci.yml | 4 ++-- doc/ci/examples/code_climate.md | 12 ++++++------ doc/topics/autodevops/index.md | 4 ++-- doc/topics/autodevops/quick_start_guide.md | 4 ++-- spec/javascripts/pipelines/mock_data.js | 2 +- vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml | 8 ++++---- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ef263a3f106..83699b9d829 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -690,7 +690,7 @@ karma: - chrome_debug.log - coverage-javascript/ -codequality: +code_quality: <<: *dedicated-no-docs-no-db-pull-cache-job image: docker:stable allow_failure: true @@ -710,7 +710,7 @@ codequality: - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - docker run --env SOURCE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code artifacts: - paths: [codeclimate.json] + paths: [gl-code-quality-report.json] expire_in: 1 week sast: diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md index d1aa783cc9c..de49c34130f 100644 --- a/doc/ci/examples/code_climate.md +++ b/doc/ci/examples/code_climate.md @@ -5,10 +5,10 @@ GitLab CI and Docker. First, you need GitLab Runner with [docker-in-docker executor][dind]. -Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `codequality`: +Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `code_quality`: ```yaml -codequality: +code_quality: image: docker:stable variables: DOCKER_DRIVER: overlay2 @@ -23,18 +23,18 @@ codequality: --volume /var/run/docker.sock:/var/run/docker.sock "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code artifacts: - paths: [codeclimate.json] + paths: [gl-code-quality-report.json] ``` -The above example will create a `codequality` job in your CI/CD pipeline which +The above example will create a `code_quality` job in your CI/CD pipeline which will scan your source code for code quality issues. The report will be saved as an artifact that you can later download and analyze. TIP: **Tip:** Starting with [GitLab Starter][ee] 9.3, this information will be automatically extracted and shown right in the merge request widget. To do -so, the CI/CD job must be named `codequality` and the artifact path must be -`codeclimate.json`. +so, the CI/CD job must be named `code_quality` and the artifact path must be +`gl-code-quality-report.json`. [Learn more on code quality diffs in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html). [cli]: https://github.com/codeclimate/codeclimate diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index f80c82acfa3..efec365042a 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -220,8 +220,8 @@ tests, it's up to you to add them. ### Auto Code Quality -Auto Code Quality uses the open source -[`codeclimate` image](https://hub.docker.com/r/codeclimate/codeclimate/) to run +Auto Code Quality uses the +[Code Quality image](https://gitlab.com/gitlab-org/security-products/codequality) to run static analysis and other code checks on the current code. The report is created, and is uploaded as an artifact which you can later download and check out. diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md index 8989a4c8956..61c04f3d9bb 100644 --- a/doc/topics/autodevops/quick_start_guide.md +++ b/doc/topics/autodevops/quick_start_guide.md @@ -126,10 +126,10 @@ Next, a pipeline needs to be triggered. Since the test project doesn't have a manually visit `https://gitlab.com//minimal-ruby-app/pipelines/new`, where `` is your username. -This will create a new pipeline with several jobs: `build`, `test`, `codequality`, +This will create a new pipeline with several jobs: `build`, `test`, `code_quality`, and `production`. The `build` job will create a Docker image with your new change and push it to the Container Registry. The `test` job will test your -changes, whereas the `codequality` job will run static analysis on your changes. +changes, whereas the `code_quality` job will run static analysis on your changes. Finally, the `production` job will deploy your changes to a production application. Once the deploy job succeeds you should be able to see your application by diff --git a/spec/javascripts/pipelines/mock_data.js b/spec/javascripts/pipelines/mock_data.js index a5a200973d7..03ead6cd8ba 100644 --- a/spec/javascripts/pipelines/mock_data.js +++ b/spec/javascripts/pipelines/mock_data.js @@ -217,7 +217,7 @@ export const pipelineWithStages = { browse_path: '/gitlab-org/gitlab-ee/-/jobs/62411442/artifacts/browse', }, { - name: 'codequality', + name: 'code_quality', expired: false, expire_at: '2018-04-18T14:16:24.484Z', path: '/gitlab-org/gitlab-ee/-/jobs/62411441/artifacts/download', diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml index a00c6e89a1d..d1dbd877150 100644 --- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml @@ -77,7 +77,7 @@ test: only: - branches -codequality: +code_quality: image: docker:stable variables: DOCKER_DRIVER: overlay2 @@ -86,9 +86,9 @@ codequality: - docker:stable-dind script: - setup_docker - - codeclimate + - code_quality artifacts: - paths: [codeclimate.json] + paths: [gl-code-quality-report.json] performance: stage: performance @@ -409,7 +409,7 @@ rollout 100%: ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true } - function codeclimate() { + function code_quality() { docker run --env SOURCE_CODE="$PWD" \ --volume "$PWD":/code \ --volume /var/run/docker.sock:/var/run/docker.sock \ -- cgit v1.2.3 From 34fc5730c7e91c936afbc026243d0d4725ae66d7 Mon Sep 17 00:00:00 2001 From: Olivier Gonzalez Date: Fri, 18 May 2018 13:51:22 -0400 Subject: Do not update project's .gitlab-ci.yml right now --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 83699b9d829..ef263a3f106 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -690,7 +690,7 @@ karma: - chrome_debug.log - coverage-javascript/ -code_quality: +codequality: <<: *dedicated-no-docs-no-db-pull-cache-job image: docker:stable allow_failure: true @@ -710,7 +710,7 @@ code_quality: - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - docker run --env SOURCE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code artifacts: - paths: [gl-code-quality-report.json] + paths: [codeclimate.json] expire_in: 1 week sast: -- cgit v1.2.3 From 8c8a794fc89211e6d8a5df24cc00e521889e202c Mon Sep 17 00:00:00 2001 From: Olivier Gonzalez Date: Wed, 23 May 2018 12:04:35 -0400 Subject: Add deprecation notice in documentation --- doc/ci/examples/code_climate.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md index de49c34130f..cc19e090964 100644 --- a/doc/ci/examples/code_climate.md +++ b/doc/ci/examples/code_climate.md @@ -37,6 +37,13 @@ so, the CI/CD job must be named `code_quality` and the artifact path must be `gl-code-quality-report.json`. [Learn more on code quality diffs in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html). +CAUTION: **Caution:** +Code Quality was previously using `codeclimate` and `codequality` for job name and +`codeclimate.json` for the artifact name. While these old names +are still maintained they have been deprecated with GitLab 11.0 and may be removed +in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml` +configuration to reflect that change. + [cli]: https://github.com/codeclimate/codeclimate [dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor [ee]: https://about.gitlab.com/products/ -- cgit v1.2.3 From 13aa6f6740118743e47f24f01aa54fed30079f4d Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Thu, 24 May 2018 09:51:25 +0800 Subject: Add spec --- spec/models/merge_request_spec.rb | 56 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index d1cc57fd103..92e33a64d26 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1245,38 +1245,50 @@ describe MergeRequest do describe '#check_if_can_be_merged' do let(:project) { create(:project, only_allow_merge_if_pipeline_succeeds: true) } - subject { create(:merge_request, source_project: project, merge_status: :unchecked) } + shared_examples 'checking if can be merged' do + context 'when it is not broken and has no conflicts' do + before do + allow(subject).to receive(:broken?) { false } + allow(project.repository).to receive(:can_be_merged?).and_return(true) + end - context 'when it is not broken and has no conflicts' do - before do - allow(subject).to receive(:broken?) { false } - allow(project.repository).to receive(:can_be_merged?).and_return(true) + it 'is marked as mergeable' do + expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('can_be_merged') + end end - it 'is marked as mergeable' do - expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('can_be_merged') - end - end + context 'when broken' do + before do + allow(subject).to receive(:broken?) { true } + end - context 'when broken' do - before do - allow(subject).to receive(:broken?) { true } + it 'becomes unmergeable' do + expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged') + end end - it 'becomes unmergeable' do - expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged') + context 'when it has conflicts' do + before do + allow(subject).to receive(:broken?) { false } + allow(project.repository).to receive(:can_be_merged?).and_return(false) + end + + it 'becomes unmergeable' do + expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged') + end end end - context 'when it has conflicts' do - before do - allow(subject).to receive(:broken?) { false } - allow(project.repository).to receive(:can_be_merged?).and_return(false) - end + context 'when merge_status is unchecked' do + subject { create(:merge_request, source_project: project, merge_status: :unchecked) } - it 'becomes unmergeable' do - expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged') - end + it_behaves_like 'checking if can be merged' + end + + context 'when merge_status is unchecked' do + subject { create(:merge_request, source_project: project, merge_status: :cannot_be_merged_recheck) } + + it_behaves_like 'checking if can be merged' end end -- cgit v1.2.3 From be9a7e67747b7280816b1f8be24d911ea7d24621 Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Thu, 24 May 2018 10:11:02 +0100 Subject: Clarify milestone_id scope for API operations on Issuables --- doc/api/issues.md | 4 ++-- doc/api/merge_requests.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/issues.md b/doc/api/issues.md index d0063e0b8a2..5613cb6d915 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -467,7 +467,7 @@ POST /projects/:id/issues | `description` | string | no | The description of an issue | | `confidential` | boolean | no | Set an issue to be confidential. Default is `false`. | | `assignee_ids` | Array[integer] | no | The ID of the users to assign issue | -| `milestone_id` | integer | no | The ID of a milestone to assign issue | +| `milestone_id` | integer | no | The global ID of a milestone to assign issue | | `labels` | string | no | Comma-separated label names for an issue | | `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) | | `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` | @@ -548,7 +548,7 @@ PUT /projects/:id/issues/:issue_iid | `description` | string | no | The description of an issue | | `confidential` | boolean | no | Updates an issue to be confidential | | `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. | -| `milestone_id` | integer | no | The ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.| +| `milestone_id` | integer | no | The global ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.| | `labels` | string | no | Comma-separated label names for an issue. Set to an empty string to unassign all labels. | | `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it | | `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) | diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index cbd51c9870c..4e34831422a 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -539,7 +539,7 @@ POST /projects/:id/merge_requests | `description` | string | no | Description of MR | | `target_project_id` | integer | no | The target project (numeric id) | | `labels` | string | no | Labels for MR as a comma-separated list | -| `milestone_id` | integer | no | The ID of a milestone | +| `milestone_id` | integer | no | The global ID of a milestone | | `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging | | `allow_maintainer_to_push` | boolean | no | Whether or not a maintainer of the target project can push to the source branch | @@ -622,7 +622,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid | `target_branch` | string | no | The target branch | | `title` | string | no | Title of MR | | `assignee_id` | integer | no | The ID of the user to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. | -| `milestone_id` | integer | no | The ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.| +| `milestone_id` | integer | no | The global ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.| | `labels` | string | no | Comma-separated label names for a merge request. Set to an empty string to unassign all labels. | | `description` | string | no | Description of MR | | `state_event` | string | no | New state (close/reopen) | -- cgit v1.2.3 From cf996685bebd9ce33980b3f7b060609bb9583649 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 May 2018 15:49:39 +0300 Subject: Refactor code around scheduling cluster installations Signed-off-by: Dmitriy Zaporozhets --- .../projects/clusters/applications_controller.rb | 7 +++-- .../applications/schedule_installation_service.rb | 17 ++--------- .../schedule_installation_service_spec.rb | 33 ++++++++++------------ 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/app/controllers/projects/clusters/applications_controller.rb b/app/controllers/projects/clusters/applications_controller.rb index 90c7fa62216..35885543622 100644 --- a/app/controllers/projects/clusters/applications_controller.rb +++ b/app/controllers/projects/clusters/applications_controller.rb @@ -5,9 +5,10 @@ class Projects::Clusters::ApplicationsController < Projects::ApplicationControll before_action :authorize_create_cluster!, only: [:create] def create - Clusters::Applications::ScheduleInstallationService.new(project, current_user, - application_class: @application_class, - cluster: @cluster).execute + application = @application_class.find_or_create_by!(cluster: @cluster) + + Clusters::Applications::ScheduleInstallationService.new(project, current_user).execute(application) + head :no_content rescue StandardError head :bad_request diff --git a/app/services/clusters/applications/schedule_installation_service.rb b/app/services/clusters/applications/schedule_installation_service.rb index eb8caa68ef7..9c5461e85e1 100644 --- a/app/services/clusters/applications/schedule_installation_service.rb +++ b/app/services/clusters/applications/schedule_installation_service.rb @@ -1,21 +1,10 @@ module Clusters module Applications class ScheduleInstallationService < ::BaseService - def execute - application_class.find_or_create_by!(cluster: cluster).try do |application| - application.make_scheduled! - ClusterInstallAppWorker.perform_async(application.name, application.id) - end - end - - private - - def application_class - params[:application_class] - end + def execute(application) + application.make_scheduled! - def cluster - params[:cluster] + ClusterInstallAppWorker.perform_async(application.name, application.id) end end end diff --git a/spec/services/clusters/applications/schedule_installation_service_spec.rb b/spec/services/clusters/applications/schedule_installation_service_spec.rb index 473dbcbb692..bca1e71bef2 100644 --- a/spec/services/clusters/applications/schedule_installation_service_spec.rb +++ b/spec/services/clusters/applications/schedule_installation_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Clusters::Applications::ScheduleInstallationService do def count_scheduled - application_class&.with_status(:scheduled)&.count || 0 + application&.class&.with_status(:scheduled)&.count || 0 end shared_examples 'a failing service' do @@ -10,45 +10,42 @@ describe Clusters::Applications::ScheduleInstallationService do expect(ClusterInstallAppWorker).not_to receive(:perform_async) count_before = count_scheduled - expect { service.execute }.to raise_error(StandardError) + expect { service.execute(application) }.to raise_error(StandardError) expect(count_scheduled).to eq(count_before) end end describe '#execute' do - let(:application_class) { Clusters::Applications::Helm } - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - let(:service) { described_class.new(project, nil, cluster: cluster, application_class: application_class) } + let(:project) { double(:project) } + let(:service) { described_class.new(project, nil) } - it 'creates a new application' do - allow(ClusterInstallAppWorker).to receive(:perform_async) + context 'when application is installable' do + let(:application) { create(:clusters_applications_helm, :installable) } - expect { service.execute }.to change { application_class.count }.by(1) - end - - it 'make the application scheduled' do - expect(ClusterInstallAppWorker).to receive(:perform_async).with(application_class.application_name, kind_of(Numeric)).once + it 'make the application scheduled' do + expect(ClusterInstallAppWorker).to receive(:perform_async).with(application.name, kind_of(Numeric)).once - expect { service.execute }.to change { application_class.with_status(:scheduled).count }.by(1) + expect { service.execute(application) }.to change { application.class.with_status(:scheduled).count }.by(1) + end end context 'when installation is already in progress' do let(:application) { create(:clusters_applications_helm, :installing) } - let(:cluster) { application.cluster } it_behaves_like 'a failing service' end - context 'when application_class is nil' do - let(:application_class) { nil } + context 'when application is nil' do + let(:application) { nil } it_behaves_like 'a failing service' end context 'when application cannot be persisted' do + let(:application) { create(:clusters_applications_helm) } + before do - expect_any_instance_of(application_class).to receive(:make_scheduled!).once.and_raise(ActiveRecord::RecordInvalid) + expect(application).to receive(:make_scheduled!).once.and_raise(ActiveRecord::RecordInvalid) end it_behaves_like 'a failing service' -- cgit v1.2.3 From 84e6ddb41f581064c9bb66c34341ec7248f1b333 Mon Sep 17 00:00:00 2001 From: Ahmad Hassan Date: Thu, 24 May 2018 14:16:31 +0200 Subject: Remove unneeded gitaly_migrate function --- lib/backup/repository.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 847d14d01c3..aa3c3a35764 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -222,13 +222,5 @@ module Backup def display_repo_path(project) project.hashed_storage?(:repository) ? "#{project.full_path} (#{project.disk_path})" : project.full_path end - - def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block) - Gitlab::GitalyClient.migrate(method, status: status, &block) - rescue GRPC::NotFound, GRPC::BadStatus => e - # Old Popen code returns [Error, output] to the caller, so we - # need to do the same here... - raise Error, e - end end end -- cgit v1.2.3 From e78b2190f82e75aae763492fdf86a3df31719ae4 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 24 May 2018 18:41:59 +0530 Subject: Add `Access-Control-Allow-Headers` to Webpack Dev Server config --- config/webpack.config.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 27050e7069d..d6ab32972fb 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -306,7 +306,10 @@ module.exports = { host: DEV_SERVER_HOST, port: DEV_SERVER_PORT, disableHostCheck: true, - headers: { 'Access-Control-Allow-Origin': '*' }, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': '*', + }, stats: 'errors-only', hot: DEV_SERVER_LIVERELOAD, inline: DEV_SERVER_LIVERELOAD, -- cgit v1.2.3 From 317ce9175a2bf9898e685bdf8cbf2555135312d6 Mon Sep 17 00:00:00 2001 From: Alexander Tanayno Date: Thu, 24 May 2018 14:21:38 +0000 Subject: add info about the current format of the backup name and refresh examples of commands --- doc/raketasks/backup_restore.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 77139c50d07..5faae7ca2d6 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -31,8 +31,8 @@ sudo apt-get install -y rsync >**Note:** In GitLab 9.2 the timestamp format was changed from `EPOCH_YYYY_MM_DD` to -`EPOCH_YYYY_MM_DD_GitLab version`, for example `1493107454_2017_04_25` -would become `1493107454_2017_04_25_9.1.0`. +`EPOCH_YYYY_MM_DD_GitLab_version`, for example `1493107454_2018_04_25` +would become `1493107454_2018_04_25_10.6.4-ce`. The backup archive will be saved in `backup_path`, which is specified in the `config/gitlab.yml` file. @@ -41,8 +41,8 @@ identifies the time at which each backup was created, plus the GitLab version. The timestamp is needed if you need to restore GitLab and multiple backups are available. -For example, if the backup name is `1493107454_2017_04_25_9.1.0_gitlab_backup.tar`, -then the timestamp is `1493107454_2017_04_25_9.1.0`. +For example, if the backup name is `1493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar`, +then the timestamp is `1493107454_2018_04_25_10.6.4-ce`. ### Creating a backup of the GitLab system @@ -574,7 +574,7 @@ First make sure your backup tar file is in the backup directory described in the `/var/opt/gitlab/backups`. ```shell -sudo cp 1493107454_2017_04_25_9.1.0_gitlab_backup.tar /var/opt/gitlab/backups/ +sudo cp 11493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar /var/opt/gitlab/backups/ ``` Stop the processes that are connected to the database. Leave the rest of GitLab @@ -592,7 +592,7 @@ restore: ```shell # This command will overwrite the contents of your GitLab database! -sudo gitlab-rake gitlab:backup:restore BACKUP=1493107454_2017_04_25_9.1.0 +sudo gitlab-rake gitlab:backup:restore BACKUP=1493107454_2018_04_25_10.6.4-ce ``` Next, restore `/etc/gitlab/gitlab-secrets.json` if necessary as mentioned above. -- cgit v1.2.3 From 5ad7ac34dd029520fd91e8ec5d95cf3516b9df40 Mon Sep 17 00:00:00 2001 From: Olivier Gonzalez Date: Thu, 24 May 2018 14:22:51 +0000 Subject: Rename container scanning job and artifact --- doc/ci/examples/container_scanning.md | 21 ++++++++++++++------- vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml | 10 +++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/doc/ci/examples/container_scanning.md b/doc/ci/examples/container_scanning.md index a9501f6c577..92ff90507ee 100644 --- a/doc/ci/examples/container_scanning.md +++ b/doc/ci/examples/container_scanning.md @@ -7,10 +7,10 @@ for Vulnerability Static Analysis for containers. All you need is a GitLab Runner with the Docker executor (the shared Runners on GitLab.com will work fine). You can then add a new job to `.gitlab-ci.yml`, -called `sast:container`: +called `container_scanning`: ```yaml -sast:container: +container_scanning: image: docker:stable variables: DOCKER_DRIVER: overlay2 @@ -34,12 +34,12 @@ sast:container: - retries=0 - echo "Waiting for clair daemon to start" - while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done - - ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true + - ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true artifacts: - paths: [gl-sast-container-report.json] + paths: [gl-container-scanning-report.json] ``` -The above example will create a `sast:container` job in your CI/CD pipeline, pull +The above example will create a `container_scanning` job in your CI/CD pipeline, pull the image from the [Container Registry](../../user/project/container_registry.md) (whose name is defined from the two `CI_APPLICATION_` variables) and scan it for possible vulnerabilities. The report will be saved as an artifact that you @@ -52,8 +52,15 @@ in our case its named `clair-whitelist.yml`. TIP: **Tip:** Starting with [GitLab Ultimate][ee] 10.4, this information will be automatically extracted and shown right in the merge request widget. To do -so, the CI/CD job must be named `sast:container` and the artifact path must be -`gl-sast-container-report.json`. +so, the CI/CD job must be named `container_scanning` and the artifact path must be +`gl-container-scanning-report.json`. [Learn more on container scanning results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/container_scanning.html). +CAUTION: **Caution:** +Container Scanning was previously using `sast:container` for job name and +`gl-sast-container-report.json` for the artifact name. While these old names +are still maintained they have been deprecated with GitLab 11.0 and may be removed +in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml` +configuration to reflect that change. + [ee]: https://about.gitlab.com/products/ diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml index a00c6e89a1d..45cb43ae8ef 100644 --- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml @@ -136,7 +136,7 @@ dependency_scanning: artifacts: paths: [gl-dependency-scanning-report.json] -sast:container: +container_scanning: image: docker:stable variables: DOCKER_DRIVER: overlay2 @@ -145,9 +145,9 @@ sast:container: - docker:stable-dind script: - setup_docker - - sast_container + - container_scanning artifacts: - paths: [gl-sast-container-report.json] + paths: [gl-container-scanning-report.json] dast: stage: dast @@ -388,7 +388,7 @@ rollout 100%: # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - function sast_container() { + function container_scanning() { if [[ -n "$CI_REGISTRY_USER" ]]; then echo "Logging to GitLab Container Registry with CI credentials..." docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" @@ -406,7 +406,7 @@ rollout 100%: retries=0 echo "Waiting for clair daemon to start" while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done - ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true + ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true } function codeclimate() { -- cgit v1.2.3 From a7225111b16eb5f8f6f00a6ea2a844ea94e50b5e Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 24 May 2018 22:38:48 +0800 Subject: Extract a do_execute method to be overridden --- app/services/merge_requests/refresh_service.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index 1fb1796b56c..0127d781686 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -3,6 +3,12 @@ module MergeRequests def execute(oldrev, newrev, ref) return true unless Gitlab::Git.branch_ref?(ref) + do_execute(oldrev, newrev, ref) + end + + private + + def do_execute(oldrev, newrev, ref) @oldrev, @newrev = oldrev, newrev @branch_name = Gitlab::Git.ref_name(ref) @@ -28,8 +34,6 @@ module MergeRequests true end - private - def close_upon_missing_source_branch_ref # MergeRequest#reload_diff ignores not opened MRs. This means it won't # create an `empty` diff for `closed` MRs without a source branch, keeping -- cgit v1.2.3 From aee2b235499f9ea179aed82d6beb8ea955986aa3 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 17 May 2018 16:39:48 -0300 Subject: Fix sidebar issue count --- app/models/project.rb | 4 +- app/services/projects/open_issues_count_service.rb | 38 +++++++++++++--- app/views/layouts/nav/sidebar/_project.html.haml | 4 +- changelogs/unreleased/issue_38418.yml | 5 +++ .../projects/open_issues_count_service_spec.rb | 51 ++++++++++++++++++---- 5 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 changelogs/unreleased/issue_38418.yml diff --git a/app/models/project.rb b/app/models/project.rb index 534a0e630af..f227ee11cb0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1417,8 +1417,8 @@ class Project < ActiveRecord::Base self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token) end - def open_issues_count - Projects::OpenIssuesCountService.new(self).count + def open_issues_count(current_user = nil) + Projects::OpenIssuesCountService.new(self, current_user).count end def open_merge_requests_count diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb index a975a06a05c..0a004677417 100644 --- a/app/services/projects/open_issues_count_service.rb +++ b/app/services/projects/open_issues_count_service.rb @@ -2,14 +2,42 @@ module Projects # Service class for counting and caching the number of open issues of a # project. class OpenIssuesCountService < Projects::CountService + include Gitlab::Utils::StrongMemoize + + def initialize(project, user = nil) + @user = user + + super(project) + end + def cache_key_name - 'open_issues_count' + public_only? ? 'public_open_issues_count' : 'total_open_issues_count' + end + + def public_only? + !user_is_at_least_reporter? + end + + def relation_for_count + self.class.query(@project, public_only: public_only?) + end + + def user_is_at_least_reporter? + strong_memoize(:user_is_at_least_reporter) do + @user && @project.team.member?(@user, Gitlab::Access::REPORTER) + end end - def self.query(project_ids) - # We don't include confidential issues in this number since this would - # expose the number of confidential issues to non project members. - Issue.opened.public_only.where(project: project_ids) + # We only show total issues count for reporters + # which are allowed to view confidential issues + # This will still show a discrepancy on issues number but should be less than before. + # Check https://gitlab.com/gitlab-org/gitlab-ce/issues/38418 description. + def self.query(projects, public_only: true) + if public_only + Issue.opened.public_only.where(project: projects) + else + Issue.opened.where(project: projects) + end end end end diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index c3ea592a6b5..3223dc903f5 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -89,7 +89,7 @@ = _('Issues') - if @project.issues_enabled? %span.badge.count.issue_counter - = number_with_delimiter(@project.open_issues_count) + = number_with_delimiter(@project.open_issues_count(current_user)) %ul.sidebar-sub-level-items = nav_link(controller: :issues, html_options: { class: "fly-out-top-item" } ) do @@ -98,7 +98,7 @@ = _('Issues') - if @project.issues_enabled? %span.badge.count.issue_counter.fly-out-badge - = number_with_delimiter(@project.open_issues_count) + = number_with_delimiter(@project.open_issues_count(current_user)) %li.divider.fly-out-top-item = nav_link(controller: :issues, action: :index) do = link_to project_issues_path(@project), title: 'Issues' do diff --git a/changelogs/unreleased/issue_38418.yml b/changelogs/unreleased/issue_38418.yml new file mode 100644 index 00000000000..79452b27e4b --- /dev/null +++ b/changelogs/unreleased/issue_38418.yml @@ -0,0 +1,5 @@ +--- +title: Fix issue count on sidebar +merge_request: +author: +type: other diff --git a/spec/services/projects/open_issues_count_service_spec.rb b/spec/services/projects/open_issues_count_service_spec.rb index f964f9972cd..06b470849b3 100644 --- a/spec/services/projects/open_issues_count_service_spec.rb +++ b/spec/services/projects/open_issues_count_service_spec.rb @@ -2,20 +2,53 @@ require 'spec_helper' describe Projects::OpenIssuesCountService do describe '#count' do - it 'returns the number of open issues' do - project = create(:project) - create(:issue, :opened, project: project) + let(:project) { create(:project) } - expect(described_class.new(project).count).to eq(1) + context 'when user is nil' do + it 'does not include confidential issues in the issue count' do + create(:issue, :opened, project: project) + create(:issue, :opened, confidential: true, project: project) + + expect(described_class.new(project).count).to eq(1) + end end - it 'does not include confidential issues in the issue count' do - project = create(:project) + context 'when user is provided' do + let(:user) { create(:user) } + + context 'when user can read confidential issues' do + before do + project.add_reporter(user) + end + + it 'returns the right count with confidential issues' do + create(:issue, :opened, project: project) + create(:issue, :opened, confidential: true, project: project) + + expect(described_class.new(project, user).count).to eq(2) + end + + it 'uses total_open_issues_count cache key' do + expect(described_class.new(project, user).cache_key_name).to eq('total_open_issues_count') + end + end + + context 'when user cannot read confidential issues' do + before do + project.add_guest(user) + end + + it 'does not include confidential issues' do + create(:issue, :opened, project: project) + create(:issue, :opened, confidential: true, project: project) - create(:issue, :opened, project: project) - create(:issue, :opened, confidential: true, project: project) + expect(described_class.new(project, user).count).to eq(1) + end - expect(described_class.new(project).count).to eq(1) + it 'uses public_open_issues_count cache key' do + expect(described_class.new(project, user).cache_key_name).to eq('public_open_issues_count') + end + end end end end -- cgit v1.2.3 From 03d5f122f7386e49a7761111bc4d315723de1591 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Thu, 24 May 2018 12:55:45 +0000 Subject: Merge branch 'patch-7' into 'master' Updated documentation as per latest missing features on about.gitlab.com See merge request gitlab-org/gitlab-ee!5794 --- doc/install/requirements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 1f2b4d9d3d9..1f399a8a3f7 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -27,8 +27,8 @@ Please see the [installation from source guide](installation.md) and the [instal ### Non-Unix operating systems such as Windows -GitLab is developed for Unix operating systems. -GitLab does **not** run on Windows and we have no plans of supporting it in the near future. +GitLab is developed for Unix operating systems. +It does **not** run on Windows, and we have no plans to support it in the near future. For the latest development status view this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46567). Please consider using a virtual machine to run GitLab. ## Ruby versions -- cgit v1.2.3 From e6fca03bbdf5b9a26d720e9dc2ec065ecda5f711 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 24 May 2018 14:58:25 +0000 Subject: Fix backup creation and restore for specific Rake tasks --- .../sh-fix-backup-specific-rake-task.yml | 5 + lib/backup/artifacts.rb | 6 +- lib/backup/builds.rb | 6 +- lib/backup/database.rb | 16 +-- lib/backup/lfs.rb | 6 +- lib/backup/manager.rb | 55 +++++---- lib/backup/pages.rb | 6 +- lib/backup/registry.rb | 6 +- lib/backup/repository.rb | 10 +- lib/backup/uploads.rb | 6 +- lib/tasks/gitlab/backup.rake | 132 ++++++++++----------- spec/lib/backup/manager_spec.rb | 2 + spec/lib/backup/repository_spec.rb | 11 +- spec/tasks/gitlab/backup_rake_spec.rb | 10 ++ 14 files changed, 163 insertions(+), 114 deletions(-) create mode 100644 changelogs/unreleased/sh-fix-backup-specific-rake-task.yml diff --git a/changelogs/unreleased/sh-fix-backup-specific-rake-task.yml b/changelogs/unreleased/sh-fix-backup-specific-rake-task.yml new file mode 100644 index 00000000000..71b121710ee --- /dev/null +++ b/changelogs/unreleased/sh-fix-backup-specific-rake-task.yml @@ -0,0 +1,5 @@ +--- +title: Fix backup creation and restore for specific Rake tasks +merge_request: +author: +type: fixed diff --git a/lib/backup/artifacts.rb b/lib/backup/artifacts.rb index 6a5a223a614..45a935ab352 100644 --- a/lib/backup/artifacts.rb +++ b/lib/backup/artifacts.rb @@ -2,7 +2,11 @@ require 'backup/files' module Backup class Artifacts < Files - def initialize + attr_reader :progress + + def initialize(progress) + @progress = progress + super('artifacts', JobArtifactUploader.root) end end diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb index f869916e199..adf85ca4719 100644 --- a/lib/backup/builds.rb +++ b/lib/backup/builds.rb @@ -2,7 +2,11 @@ require 'backup/files' module Backup class Builds < Files - def initialize + attr_reader :progress + + def initialize(progress) + @progress = progress + super('builds', Settings.gitlab_ci.builds_path) end end diff --git a/lib/backup/database.rb b/lib/backup/database.rb index 5e6828de597..1608f7ad02d 100644 --- a/lib/backup/database.rb +++ b/lib/backup/database.rb @@ -2,9 +2,11 @@ require 'yaml' module Backup class Database + attr_reader :progress attr_reader :config, :db_file_name - def initialize + def initialize(progress) + @progress = progress @config = YAML.load_file(File.join(Rails.root, 'config', 'database.yml'))[Rails.env] @db_file_name = File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz') end @@ -19,12 +21,12 @@ module Backup dump_pid = case config["adapter"] when /^mysql/ then - $progress.print "Dumping MySQL database #{config['database']} ... " + progress.print "Dumping MySQL database #{config['database']} ... " # Workaround warnings from MySQL 5.6 about passwords on cmd line ENV['MYSQL_PWD'] = config["password"].to_s if config["password"] spawn('mysqldump', *mysql_args, config['database'], out: compress_wr) when "postgresql" then - $progress.print "Dumping PostgreSQL database #{config['database']} ... " + progress.print "Dumping PostgreSQL database #{config['database']} ... " pg_env pgsql_args = ["--clean"] # Pass '--clean' to include 'DROP TABLE' statements in the DB dump. if Gitlab.config.backup.pg_schema @@ -53,12 +55,12 @@ module Backup restore_pid = case config["adapter"] when /^mysql/ then - $progress.print "Restoring MySQL database #{config['database']} ... " + progress.print "Restoring MySQL database #{config['database']} ... " # Workaround warnings from MySQL 5.6 about passwords on cmd line ENV['MYSQL_PWD'] = config["password"].to_s if config["password"] spawn('mysql', *mysql_args, config['database'], in: decompress_rd) when "postgresql" then - $progress.print "Restoring PostgreSQL database #{config['database']} ... " + progress.print "Restoring PostgreSQL database #{config['database']} ... " pg_env spawn('psql', config['database'], in: decompress_rd) end @@ -111,9 +113,9 @@ module Backup def report_success(success) if success - $progress.puts '[DONE]'.color(:green) + progress.puts '[DONE]'.color(:green) else - $progress.puts '[FAILED]'.color(:red) + progress.puts '[FAILED]'.color(:red) end end end diff --git a/lib/backup/lfs.rb b/lib/backup/lfs.rb index 4e234e50a7a..185ff8ae6bd 100644 --- a/lib/backup/lfs.rb +++ b/lib/backup/lfs.rb @@ -2,7 +2,11 @@ require 'backup/files' module Backup class Lfs < Files - def initialize + attr_reader :progress + + def initialize(progress) + @progress = progress + super('lfs', Settings.lfs.storage_path) end end diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index f27ce4d2b2b..a8da0c7edef 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -4,6 +4,12 @@ module Backup FOLDERS_TO_BACKUP = %w[repositories db].freeze FILE_NAME_SUFFIX = '_gitlab_backup.tar'.freeze + attr_reader :progress + + def initialize(progress) + @progress = progress + end + def pack # Make sure there is a connection ActiveRecord::Base.connection.reconnect! @@ -14,11 +20,11 @@ module Backup end # create archive - $progress.print "Creating backup archive: #{tar_file} ... " + progress.print "Creating backup archive: #{tar_file} ... " # Set file permissions on open to prevent chmod races. tar_system_options = { out: [tar_file, 'w', Gitlab.config.backup.archive_permissions] } if Kernel.system('tar', '-cf', '-', *backup_contents, tar_system_options) - $progress.puts "done".color(:green) + progress.puts "done".color(:green) else puts "creating archive #{tar_file} failed".color(:red) abort 'Backup failed' @@ -29,11 +35,11 @@ module Backup end def upload - $progress.print "Uploading backup archive to remote storage #{remote_directory} ... " + progress.print "Uploading backup archive to remote storage #{remote_directory} ... " connection_settings = Gitlab.config.backup.upload.connection if connection_settings.blank? - $progress.puts "skipped".color(:yellow) + progress.puts "skipped".color(:yellow) return end @@ -43,7 +49,7 @@ module Backup multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size, encryption: Gitlab.config.backup.upload.encryption, storage_class: Gitlab.config.backup.upload.storage_class) - $progress.puts "done".color(:green) + progress.puts "done".color(:green) else puts "uploading backup to #{remote_directory} failed".color(:red) abort 'Backup failed' @@ -51,13 +57,13 @@ module Backup end def cleanup - $progress.print "Deleting tmp directories ... " + progress.print "Deleting tmp directories ... " backup_contents.each do |dir| next unless File.exist?(File.join(backup_path, dir)) if FileUtils.rm_rf(File.join(backup_path, dir)) - $progress.puts "done".color(:green) + progress.puts "done".color(:green) else puts "deleting tmp directory '#{dir}' failed".color(:red) abort 'Backup failed' @@ -67,7 +73,7 @@ module Backup def remove_old # delete backups - $progress.print "Deleting old backups ... " + progress.print "Deleting old backups ... " keep_time = Gitlab.config.backup.keep_time.to_i if keep_time > 0 @@ -88,31 +94,32 @@ module Backup FileUtils.rm(file) removed += 1 rescue => e - $progress.puts "Deleting #{file} failed: #{e.message}".color(:red) + progress.puts "Deleting #{file} failed: #{e.message}".color(:red) end end end end - $progress.puts "done. (#{removed} removed)".color(:green) + progress.puts "done. (#{removed} removed)".color(:green) else - $progress.puts "skipping".color(:yellow) + progress.puts "skipping".color(:yellow) end end + # rubocop: disable Metrics/AbcSize def unpack Dir.chdir(backup_path) do # check for existing backups in the backup dir if backup_file_list.empty? - $progress.puts "No backups found in #{backup_path}" - $progress.puts "Please make sure that file name ends with #{FILE_NAME_SUFFIX}" + progress.puts "No backups found in #{backup_path}" + progress.puts "Please make sure that file name ends with #{FILE_NAME_SUFFIX}" exit 1 elsif backup_file_list.many? && ENV["BACKUP"].nil? - $progress.puts 'Found more than one backup:' + progress.puts 'Found more than one backup:' # print list of available backups - $progress.puts " " + available_timestamps.join("\n ") - $progress.puts 'Please specify which one you want to restore:' - $progress.puts 'rake gitlab:backup:restore BACKUP=timestamp_of_backup' + progress.puts " " + available_timestamps.join("\n ") + progress.puts 'Please specify which one you want to restore:' + progress.puts 'rake gitlab:backup:restore BACKUP=timestamp_of_backup' exit 1 end @@ -123,31 +130,31 @@ module Backup end unless File.exist?(tar_file) - $progress.puts "The backup file #{tar_file} does not exist!" + progress.puts "The backup file #{tar_file} does not exist!" exit 1 end - $progress.print 'Unpacking backup ... ' + progress.print 'Unpacking backup ... ' unless Kernel.system(*%W(tar -xf #{tar_file})) - $progress.puts 'unpacking backup failed'.color(:red) + progress.puts 'unpacking backup failed'.color(:red) exit 1 else - $progress.puts 'done'.color(:green) + progress.puts 'done'.color(:green) end ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0 # restoring mismatching backups can lead to unexpected problems if settings[:gitlab_version] != Gitlab::VERSION - $progress.puts(<<~HEREDOC.color(:red)) + progress.puts(<<~HEREDOC.color(:red)) GitLab version mismatch: Your current GitLab version (#{Gitlab::VERSION}) differs from the GitLab version in the backup! Please switch to the following version and try again: version: #{settings[:gitlab_version]} HEREDOC - $progress.puts - $progress.puts "Hint: git checkout v#{settings[:gitlab_version]}" + progress.puts + progress.puts "Hint: git checkout v#{settings[:gitlab_version]}" exit 1 end end diff --git a/lib/backup/pages.rb b/lib/backup/pages.rb index 5830b209d6e..542e35a7c7c 100644 --- a/lib/backup/pages.rb +++ b/lib/backup/pages.rb @@ -2,7 +2,11 @@ require 'backup/files' module Backup class Pages < Files - def initialize + attr_reader :progress + + def initialize(progress) + @progress = progress + super('pages', Gitlab.config.pages.path) end end diff --git a/lib/backup/registry.rb b/lib/backup/registry.rb index 91698669402..35821805797 100644 --- a/lib/backup/registry.rb +++ b/lib/backup/registry.rb @@ -2,7 +2,11 @@ require 'backup/files' module Backup class Registry < Files - def initialize + attr_reader :progress + + def initialize(progress) + @progress = progress + super('registry', Settings.registry.path) end end diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 65e06fd78c0..44ad991c72a 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -6,6 +6,12 @@ module Backup include Backup::Helper # rubocop:disable Metrics/AbcSize + attr_reader :progress + + def initialize(progress) + @progress = progress + end + def dump prepare @@ -217,10 +223,6 @@ module Backup Gitlab.config.repositories.storages.values.map { |rs| rs.legacy_disk_path } end - def progress - $progress - end - def display_repo_path(project) project.hashed_storage?(:repository) ? "#{project.full_path} (#{project.disk_path})" : project.full_path end diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb index d46e2cd869d..49b117a7ee3 100644 --- a/lib/backup/uploads.rb +++ b/lib/backup/uploads.rb @@ -2,7 +2,11 @@ require 'backup/files' module Backup class Uploads < Files - def initialize + attr_reader :progress + + def initialize(progress) + @progress = progress + super('uploads', Rails.root.join('public/uploads')) end end diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 24e37f6c6cc..e96fbb64372 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -6,7 +6,6 @@ namespace :gitlab do desc "GitLab | Create a backup of the GitLab system" task create: :gitlab_environment do warn_user_is_not_gitlab - configure_cron_mode Rake::Task["gitlab:backup:db:create"].invoke Rake::Task["gitlab:backup:repo:create"].invoke @@ -17,7 +16,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:lfs:create"].invoke Rake::Task["gitlab:backup:registry:create"].invoke - backup = Backup::Manager.new + backup = Backup::Manager.new(progress) backup.pack backup.cleanup backup.remove_old @@ -27,9 +26,8 @@ namespace :gitlab do desc 'GitLab | Restore a previously created backup' task restore: :gitlab_environment do warn_user_is_not_gitlab - configure_cron_mode - backup = Backup::Manager.new + backup = Backup::Manager.new(progress) backup.unpack unless backup.skipped?('db') @@ -49,9 +47,9 @@ namespace :gitlab do # Drop all tables Load the schema to ensure we don't have any newer tables # hanging out from a failed upgrade - $progress.puts 'Cleaning the database ... '.color(:blue) + progress.puts 'Cleaning the database ... '.color(:blue) Rake::Task['gitlab:db:drop_tables'].invoke - $progress.puts 'done'.color(:green) + progress.puts 'done'.color(:green) Rake::Task['gitlab:backup:db:restore'].invoke rescue Gitlab::TaskAbortedByUserError puts "Quitting...".color(:red) @@ -74,173 +72,173 @@ namespace :gitlab do namespace :repo do task create: :gitlab_environment do - $progress.puts "Dumping repositories ...".color(:blue) + progress.puts "Dumping repositories ...".color(:blue) if ENV["SKIP"] && ENV["SKIP"].include?("repositories") - $progress.puts "[SKIPPED]".color(:cyan) + progress.puts "[SKIPPED]".color(:cyan) else - Backup::Repository.new.dump - $progress.puts "done".color(:green) + Backup::Repository.new(progress).dump + progress.puts "done".color(:green) end end task restore: :gitlab_environment do - $progress.puts "Restoring repositories ...".color(:blue) - Backup::Repository.new.restore - $progress.puts "done".color(:green) + progress.puts "Restoring repositories ...".color(:blue) + Backup::Repository.new(progress).restore + progress.puts "done".color(:green) end end namespace :db do task create: :gitlab_environment do - $progress.puts "Dumping database ... ".color(:blue) + progress.puts "Dumping database ... ".color(:blue) if ENV["SKIP"] && ENV["SKIP"].include?("db") - $progress.puts "[SKIPPED]".color(:cyan) + progress.puts "[SKIPPED]".color(:cyan) else - Backup::Database.new.dump - $progress.puts "done".color(:green) + Backup::Database.new(progress).dump + progress.puts "done".color(:green) end end task restore: :gitlab_environment do - $progress.puts "Restoring database ... ".color(:blue) - Backup::Database.new.restore - $progress.puts "done".color(:green) + progress.puts "Restoring database ... ".color(:blue) + Backup::Database.new(progress).restore + progress.puts "done".color(:green) end end namespace :builds do task create: :gitlab_environment do - $progress.puts "Dumping builds ... ".color(:blue) + progress.puts "Dumping builds ... ".color(:blue) if ENV["SKIP"] && ENV["SKIP"].include?("builds") - $progress.puts "[SKIPPED]".color(:cyan) + progress.puts "[SKIPPED]".color(:cyan) else - Backup::Builds.new.dump - $progress.puts "done".color(:green) + Backup::Builds.new(progress).dump + progress.puts "done".color(:green) end end task restore: :gitlab_environment do - $progress.puts "Restoring builds ... ".color(:blue) - Backup::Builds.new.restore - $progress.puts "done".color(:green) + progress.puts "Restoring builds ... ".color(:blue) + Backup::Builds.new(progress).restore + progress.puts "done".color(:green) end end namespace :uploads do task create: :gitlab_environment do - $progress.puts "Dumping uploads ... ".color(:blue) + progress.puts "Dumping uploads ... ".color(:blue) if ENV["SKIP"] && ENV["SKIP"].include?("uploads") - $progress.puts "[SKIPPED]".color(:cyan) + progress.puts "[SKIPPED]".color(:cyan) else - Backup::Uploads.new.dump - $progress.puts "done".color(:green) + Backup::Uploads.new(progress).dump + progress.puts "done".color(:green) end end task restore: :gitlab_environment do - $progress.puts "Restoring uploads ... ".color(:blue) - Backup::Uploads.new.restore - $progress.puts "done".color(:green) + progress.puts "Restoring uploads ... ".color(:blue) + Backup::Uploads.new(progress).restore + progress.puts "done".color(:green) end end namespace :artifacts do task create: :gitlab_environment do - $progress.puts "Dumping artifacts ... ".color(:blue) + progress.puts "Dumping artifacts ... ".color(:blue) if ENV["SKIP"] && ENV["SKIP"].include?("artifacts") - $progress.puts "[SKIPPED]".color(:cyan) + progress.puts "[SKIPPED]".color(:cyan) else - Backup::Artifacts.new.dump - $progress.puts "done".color(:green) + Backup::Artifacts.new(progress).dump + progress.puts "done".color(:green) end end task restore: :gitlab_environment do - $progress.puts "Restoring artifacts ... ".color(:blue) - Backup::Artifacts.new.restore - $progress.puts "done".color(:green) + progress.puts "Restoring artifacts ... ".color(:blue) + Backup::Artifacts.new(progress).restore + progress.puts "done".color(:green) end end namespace :pages do task create: :gitlab_environment do - $progress.puts "Dumping pages ... ".color(:blue) + progress.puts "Dumping pages ... ".color(:blue) if ENV["SKIP"] && ENV["SKIP"].include?("pages") - $progress.puts "[SKIPPED]".color(:cyan) + progress.puts "[SKIPPED]".color(:cyan) else - Backup::Pages.new.dump - $progress.puts "done".color(:green) + Backup::Pages.new(progress).dump + progress.puts "done".color(:green) end end task restore: :gitlab_environment do - $progress.puts "Restoring pages ... ".color(:blue) - Backup::Pages.new.restore - $progress.puts "done".color(:green) + progress.puts "Restoring pages ... ".color(:blue) + Backup::Pages.new(progress).restore + progress.puts "done".color(:green) end end namespace :lfs do task create: :gitlab_environment do - $progress.puts "Dumping lfs objects ... ".color(:blue) + progress.puts "Dumping lfs objects ... ".color(:blue) if ENV["SKIP"] && ENV["SKIP"].include?("lfs") - $progress.puts "[SKIPPED]".color(:cyan) + progress.puts "[SKIPPED]".color(:cyan) else - Backup::Lfs.new.dump - $progress.puts "done".color(:green) + Backup::Lfs.new(progress).dump + progress.puts "done".color(:green) end end task restore: :gitlab_environment do - $progress.puts "Restoring lfs objects ... ".color(:blue) - Backup::Lfs.new.restore - $progress.puts "done".color(:green) + progress.puts "Restoring lfs objects ... ".color(:blue) + Backup::Lfs.new(progress).restore + progress.puts "done".color(:green) end end namespace :registry do task create: :gitlab_environment do - $progress.puts "Dumping container registry images ... ".color(:blue) + progress.puts "Dumping container registry images ... ".color(:blue) if Gitlab.config.registry.enabled if ENV["SKIP"] && ENV["SKIP"].include?("registry") - $progress.puts "[SKIPPED]".color(:cyan) + progress.puts "[SKIPPED]".color(:cyan) else - Backup::Registry.new.dump - $progress.puts "done".color(:green) + Backup::Registry.new(progress).dump + progress.puts "done".color(:green) end else - $progress.puts "[DISABLED]".color(:cyan) + progress.puts "[DISABLED]".color(:cyan) end end task restore: :gitlab_environment do - $progress.puts "Restoring container registry images ... ".color(:blue) + progress.puts "Restoring container registry images ... ".color(:blue) if Gitlab.config.registry.enabled - Backup::Registry.new.restore - $progress.puts "done".color(:green) + Backup::Registry.new(progress).restore + progress.puts "done".color(:green) else - $progress.puts "[DISABLED]".color(:cyan) + progress.puts "[DISABLED]".color(:cyan) end end end - def configure_cron_mode + def progress if ENV['CRON'] # We need an object we can say 'puts' and 'print' to; let's use a # StringIO. require 'stringio' - $progress = StringIO.new + StringIO.new else - $progress = $stdout + $stdout end end end # namespace end: backup diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb index 84688845fa5..23c04a1a101 100644 --- a/spec/lib/backup/manager_spec.rb +++ b/spec/lib/backup/manager_spec.rb @@ -5,6 +5,8 @@ describe Backup::Manager do let(:progress) { StringIO.new } + subject { described_class.new(progress) } + before do allow(progress).to receive(:puts) allow(progress).to receive(:print) diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb index b1ea9c0b622..20ea981ec47 100644 --- a/spec/lib/backup/repository_spec.rb +++ b/spec/lib/backup/repository_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe Backup::Repository do let(:progress) { StringIO.new } let!(:project) { create(:project, :wiki_repo) } + subject { described_class.new(progress) } before do allow(progress).to receive(:puts) @@ -24,14 +25,12 @@ describe Backup::Repository do end it 'does not raise error' do - expect { described_class.new.dump }.not_to raise_error + expect { subject.dump }.not_to raise_error end end end describe '#restore' do - subject { described_class.new } - let(:timestamp) { Time.utc(2017, 3, 22) } let(:temp_dirs) do Gitlab.config.repositories.storages.map do |name, storage| @@ -102,20 +101,20 @@ describe Backup::Repository do it 'invalidates the emptiness cache' do expect(wiki.repository).to receive(:expire_emptiness_caches).once - described_class.new.send(:empty_repo?, wiki) + subject.send(:empty_repo?, wiki) end context 'wiki repo has content' do let!(:wiki_page) { create(:wiki_page, wiki: wiki) } it 'returns true, regardless of bad cache value' do - expect(described_class.new.send(:empty_repo?, wiki)).to be(false) + expect(subject.send(:empty_repo?, wiki)).to be(false) end end context 'wiki repo does not have content' do it 'returns true, regardless of bad cache value' do - expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy + expect(subject.send(:empty_repo?, wiki)).to be_truthy end end end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index a2e5642a72c..c9252bebb2e 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -125,6 +125,16 @@ describe 'gitlab:app namespace rake task' do expect(Dir.entries(File.join(project.repository.path, 'custom_hooks'))).to include("dummy.txt") end end + + context 'specific backup tasks' do + let(:task_list) { %w(db repo uploads builds artifacts pages lfs registry) } + + it 'prints a progress message to stdout' do + task_list.each do |task| + expect { run_rake_task("gitlab:backup:#{task}:create") }.to output(/Dumping /).to_stdout + end + end + end end context 'tar creation' do -- cgit v1.2.3 From 68eb9ee3617abb1d9dba7264ce9c39016c825936 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 24 May 2018 17:17:52 +0000 Subject: Updated branches and tag icons in commit details --- app/helpers/commits_helper.rb | 4 ++-- app/views/projects/commit/branches.html.haml | 3 ++- changelogs/unreleased/commit-branch-tag-icon-update.yml | 5 +++++ package.json | 2 +- yarn.lock | 6 +++--- 5 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/commit-branch-tag-icon-update.yml diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index e594a1d0ba3..f5a7871a6e0 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -63,7 +63,7 @@ module CommitsHelper # Returns a link formatted as a commit branch link def commit_branch_link(url, text) link_to(url, class: 'label label-gray ref-name branch-link') do - sprite_icon('fork', size: 12, css_class: 'fork-svg') + "#{text}" + sprite_icon('branch', size: 12, css_class: 'fork-svg') + "#{text}" end end @@ -77,7 +77,7 @@ module CommitsHelper # Returns a link formatted as a commit tag link def commit_tag_link(url, text) link_to(url, class: 'label label-gray ref-name') do - icon('tag', class: 'append-right-5') + "#{text}" + sprite_icon('tag', size: 12, css_class: 'append-right-5 vertical-align-middle') + "#{text}" end end diff --git a/app/views/projects/commit/branches.html.haml b/app/views/projects/commit/branches.html.haml index 8611129b356..a91e31afc2b 100644 --- a/app/views/projects/commit/branches.html.haml +++ b/app/views/projects/commit/branches.html.haml @@ -6,7 +6,8 @@ - if @branches.any? || @tags.any? || @tags_limit_exceeded %span - = link_to "…", "#", class: "js-details-expand label label-gray" + = link_to "#", class: "js-details-expand label label-gray ref-name" do + = sprite_icon('ellipsis_h', size: 12, css_class: 'vertical-align-middle') %span.js-details-content.hide = commit_branches_links(@project, @branches) - if @tags_limit_exceeded diff --git a/changelogs/unreleased/commit-branch-tag-icon-update.yml b/changelogs/unreleased/commit-branch-tag-icon-update.yml new file mode 100644 index 00000000000..136b7cbf0f4 --- /dev/null +++ b/changelogs/unreleased/commit-branch-tag-icon-update.yml @@ -0,0 +1,5 @@ +--- +title: Updated icons for branch and tag names in commit details +merge_request: 18953 +author: Constance Okoghenun +type: changed diff --git a/package.json b/package.json index 1382afd41d6..68e5e0a9b6a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js" }, "dependencies": { - "@gitlab-org/gitlab-svgs": "^1.22.0", + "@gitlab-org/gitlab-svgs": "^1.23.0", "autosize": "^4.0.0", "axios": "^0.17.1", "babel-core": "^6.26.3", diff --git a/yarn.lock b/yarn.lock index e0d46609f7d..bbc4672f84b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -54,9 +54,9 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@gitlab-org/gitlab-svgs@^1.22.0": - version "1.22.0" - resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.22.0.tgz#9f2daefebcda911cba8341313c8c464c8087fe44" +"@gitlab-org/gitlab-svgs@^1.23.0": + version "1.23.0" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.23.0.tgz#42047aeedcc06bc12d417ed1efadad1749af9670" "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" -- cgit v1.2.3