From 6438df3a1e0fb944485cebf07976160184697d72 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 20 Jan 2021 13:34:23 -0600 Subject: Add latest changes from gitlab-org/gitlab@13-8-stable-ee --- .../services/projects/after_import_service_spec.rb | 6 +- .../cleanup_tags_service_spec.rb | 116 +++++++++++++++--- .../delete_tags_service_spec.rb | 4 +- .../gitlab/delete_tags_service_spec.rb | 17 ++- spec/services/projects/fork_service_spec.rb | 18 +++ .../services/projects/housekeeping_service_spec.rb | 134 ++------------------- ...ule_bulk_repository_shard_moves_service_spec.rb | 43 +------ .../services/projects/update_pages_service_spec.rb | 13 ++ spec/services/projects/update_service_spec.rb | 43 ------- 9 files changed, 165 insertions(+), 229 deletions(-) (limited to 'spec/services/projects') diff --git a/spec/services/projects/after_import_service_spec.rb b/spec/services/projects/after_import_service_spec.rb index a109348ea19..a16aec891a9 100644 --- a/spec/services/projects/after_import_service_spec.rb +++ b/spec/services/projects/after_import_service_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Projects::AfterImportService do describe '#execute' do before do - allow(Projects::HousekeepingService) + allow(Repositories::HousekeepingService) .to receive(:new).with(project).and_return(housekeeping_service) allow(housekeeping_service) @@ -73,10 +73,10 @@ RSpec.describe Projects::AfterImportService do end context 'when housekeeping service lease is taken' do - let(:exception) { Projects::HousekeepingService::LeaseTaken.new } + let(:exception) { Repositories::HousekeepingService::LeaseTaken.new } it 'logs the error message' do - allow_next_instance_of(Projects::HousekeepingService) do |instance| + allow_next_instance_of(Repositories::HousekeepingService) do |instance| expect(instance).to receive(:execute).and_raise(exception) end diff --git a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb index 8ddcb8ce660..17c2f0f6c17 100644 --- a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb +++ b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb @@ -3,11 +3,14 @@ require 'spec_helper' RSpec.describe Projects::ContainerRepository::CleanupTagsService do + using RSpec::Parameterized::TableSyntax + let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project, :private) } let_it_be(:repository) { create(:container_repository, :root, project: project) } let(:service) { described_class.new(project, user, params) } + let(:tags) { %w[latest A Ba Bb C D E] } before do project.add_maintainer(user) @@ -16,7 +19,8 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do stub_container_registry_tags( repository: repository.path, - tags: %w(latest A Ba Bb C D E)) + tags: tags + ) stub_tag_digest('latest', 'sha256:configA') stub_tag_digest('A', 'sha256:configA') @@ -30,6 +34,8 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do stub_digest_config('sha256:configB', 5.days.ago) stub_digest_config('sha256:configC', 1.month.ago) stub_digest_config('sha256:configD', nil) + + stub_feature_flags(container_registry_expiration_policies_throttling: false) end describe '#execute' do @@ -42,7 +48,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do expect_any_instance_of(Projects::ContainerRepository::DeleteTagsService) .not_to receive(:execute) - is_expected.to include(status: :success, deleted: []) + is_expected.to eq(expected_service_response(before_truncate_size: 0, after_truncate_size: 0, before_delete_size: 0)) end end @@ -51,7 +57,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do it 'does remove all tags except latest' do expect_delete(%w(A Ba Bb C D E)) - is_expected.to include(status: :success, deleted: %w(A Ba Bb C D E)) + is_expected.to eq(expected_service_response(deleted: %w(A Ba Bb C D E))) end end @@ -78,12 +84,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do subject end - it 'returns an error' do - response = subject - - expect(response[:status]).to eq(:error) - expect(response[:message]).to eq('invalid regex') - end + it { is_expected.to eq(status: :error, message: 'invalid regex') } it 'calls error tracking service' do expect(Gitlab::ErrorTracking).to receive(:log_exception).and_call_original @@ -119,7 +120,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do it 'does remove C and D' do expect_delete(%w(C D)) - is_expected.to include(status: :success, deleted: %w(C D)) + is_expected.to eq(expected_service_response(deleted: %w(C D), before_truncate_size: 2, after_truncate_size: 2, before_delete_size: 2)) end context 'with overriding allow regex' do @@ -131,7 +132,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do it 'does not remove C' do expect_delete(%w(D)) - is_expected.to include(status: :success, deleted: %w(D)) + is_expected.to eq(expected_service_response(deleted: %w(D), before_truncate_size: 1, after_truncate_size: 1, before_delete_size: 1)) end end @@ -144,7 +145,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do it 'does not remove C' do expect_delete(%w(D)) - is_expected.to include(status: :success, deleted: %w(D)) + is_expected.to eq(expected_service_response(deleted: %w(D), before_truncate_size: 1, after_truncate_size: 1, before_delete_size: 1)) end end end @@ -158,7 +159,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do it 'does not remove B*' do expect_delete(%w(A C D E)) - is_expected.to include(status: :success, deleted: %w(A C D E)) + is_expected.to eq(expected_service_response(deleted: %w(A C D E), before_truncate_size: 4, after_truncate_size: 4, before_delete_size: 4)) end end @@ -173,7 +174,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do expect(service).to receive(:order_by_date).and_call_original - is_expected.to include(status: :success, deleted: %w(Bb Ba C)) + is_expected.to eq(expected_service_response(deleted: %w(Bb Ba C), before_truncate_size: 4, after_truncate_size: 4, before_delete_size: 3)) end end @@ -187,7 +188,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do expect(service).not_to receive(:order_by_date) - is_expected.to include(status: :success, deleted: %w(A Ba Bb C)) + is_expected.to eq(expected_service_response(deleted: %w(A Ba Bb C), before_truncate_size: 4, after_truncate_size: 4, before_delete_size: 4)) end end @@ -200,7 +201,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do it 'does remove B* and C as they are the oldest' do expect_delete(%w(Bb Ba C)) - is_expected.to include(status: :success, deleted: %w(Bb Ba C)) + is_expected.to eq(expected_service_response(deleted: %w(Bb Ba C), before_delete_size: 3)) end end @@ -213,7 +214,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do it 'does remove B* and C as they are older than 1 day' do expect_delete(%w(Ba Bb C)) - is_expected.to include(status: :success, deleted: %w(Ba Bb C)) + is_expected.to eq(expected_service_response(deleted: %w(Ba Bb C), before_delete_size: 3)) end end @@ -227,7 +228,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do it 'does remove B* and C' do expect_delete(%w(Bb Ba C)) - is_expected.to include(status: :success, deleted: %w(Bb Ba C)) + is_expected.to eq(expected_service_response(deleted: %w(Bb Ba C), before_delete_size: 3)) end end @@ -245,7 +246,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do it 'succeeds without a user' do expect_delete(%w(Bb Ba C), container_expiration_policy: true) - is_expected.to include(status: :success, deleted: %w(Bb Ba C)) + is_expected.to eq(expected_service_response(deleted: %w(Bb Ba C), before_delete_size: 3)) end end @@ -257,10 +258,73 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do end it 'fails' do - is_expected.to include(status: :error, message: 'access denied') + is_expected.to eq(status: :error, message: 'access denied') end end end + + context 'truncating the tags list' do + let(:params) do + { + 'name_regex_delete' => '.*', + 'keep_n' => 1 + } + end + + shared_examples 'returning the response' do |status:, original_size:, before_truncate_size:, after_truncate_size:, before_delete_size:| + it 'returns the response' do + result = subject + + service_response = expected_service_response( + status: status, + original_size: original_size, + before_truncate_size: before_truncate_size, + after_truncate_size: after_truncate_size, + before_delete_size: before_delete_size, + deleted: nil + ) + + expect(result).to eq(service_response.compact) + end + end + + where(:feature_flag_enabled, :max_list_size, :delete_tags_service_status, :expected_status, :expected_truncated) do + false | 10 | :success | :success | false + false | 10 | :error | :error | false + false | 3 | :success | :success | false + false | 3 | :error | :error | false + false | 0 | :success | :success | false + false | 0 | :error | :error | false + true | 10 | :success | :success | false + true | 10 | :error | :error | false + true | 3 | :success | :error | true + true | 3 | :error | :error | true + true | 0 | :success | :success | false + true | 0 | :error | :error | false + end + + with_them do + before do + stub_feature_flags(container_registry_expiration_policies_throttling: feature_flag_enabled) + stub_application_setting(container_registry_cleanup_tags_service_max_list_size: max_list_size) + allow_next_instance_of(Projects::ContainerRepository::DeleteTagsService) do |service| + expect(service).to receive(:execute).and_return(status: delete_tags_service_status) + end + end + + original_size = 7 + keep_n = 1 + + it_behaves_like( + 'returning the response', + status: params[:expected_status], + original_size: original_size, + before_truncate_size: original_size - keep_n, + after_truncate_size: params[:expected_truncated] ? params[:max_list_size] + keep_n : original_size - keep_n, + before_delete_size: params[:expected_truncated] ? params[:max_list_size] : original_size - keep_n - 1 # one tag is filtered out with older_than filter + ) + end + end end private @@ -295,4 +359,16 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do .to receive(:execute) .with(repository) { { status: :success, deleted: tags } } end + + # all those -1 because the default tags on L13 have a "latest" that will be filtered out + def expected_service_response(status: :success, deleted: [], original_size: tags.size, before_truncate_size: tags.size - 1, after_truncate_size: tags.size - 1, before_delete_size: tags.size - 1) + { + status: status, + deleted: deleted, + original_size: original_size, + before_truncate_size: before_truncate_size, + after_truncate_size: after_truncate_size, + before_delete_size: before_delete_size + }.compact + end end diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb index 4da416d9698..94037d6de1e 100644 --- a/spec/services/projects/container_repository/delete_tags_service_spec.rb +++ b/spec/services/projects/container_repository/delete_tags_service_spec.rb @@ -119,9 +119,9 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do end end - it { is_expected.to include(status: :error, message: 'timeout while deleting tags') } + it { is_expected.to include(status: :error, message: 'error while deleting tags') } - it_behaves_like 'logging an error response', message: 'timeout while deleting tags', extra_log: { deleted_tags_count: 0 } + it_behaves_like 'logging an error response', message: 'error while deleting tags', extra_log: { deleted_tags_count: 0 } end end end diff --git a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb index 988971171fc..74f782538c5 100644 --- a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb +++ b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb @@ -67,7 +67,7 @@ RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService do stub_delete_reference_requests('A' => 200) end - it { is_expected.to eq(status: :error, message: 'timeout while deleting tags', deleted: ['A']) } + it { is_expected.to eq(status: :error, message: 'error while deleting tags', deleted: ['A'], exception_class_name: Projects::ContainerRepository::Gitlab::DeleteTagsService::TimeoutError.name) } it 'tracks the exception' do expect(::Gitlab::ErrorTracking) @@ -89,6 +89,21 @@ RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService do it_behaves_like 'deleting tags' end end + + context 'with a network error' do + before do + expect(service).to receive(:delete_tags).and_raise(::Faraday::TimeoutError) + end + + it { is_expected.to eq(status: :error, message: 'error while deleting tags', deleted: [], exception_class_name: ::Faraday::TimeoutError.name) } + + it 'tracks the exception' do + expect(::Gitlab::ErrorTracking) + .to receive(:track_exception).with(::Faraday::TimeoutError, tags_count: tags.size, container_repository_id: repository.id) + + subject + end + end end end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index 60dfee820ca..a11f16573f5 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -116,6 +116,24 @@ RSpec.describe Projects::ForkService do expect(to_project.fork_network_member.forked_from_project).to eq(from_forked_project) end + context 'when the forked project has higher visibility than the root project' do + let(:root_project) { create(:project, :public) } + + it 'successfully creates a fork of the fork with correct visibility' do + forked_project = fork_project(root_project, @to_user, using_service: true) + + root_project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + + # Forked project visibility is not affected by root project visibility change + expect(forked_project).to have_attributes(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + + fork_of_the_fork = fork_project(forked_project, @to_user, namespace: other_namespace, using_service: true) + + expect(fork_of_the_fork).to be_valid + expect(fork_of_the_fork).to have_attributes(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + end + end + it_behaves_like 'forks count cache refresh' do let(:from_project) { from_forked_project } let(:to_user) { @to_user } diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb index 18871f010f8..0aa4a1cd312 100644 --- a/spec/services/projects/housekeeping_service_spec.rb +++ b/spec/services/projects/housekeeping_service_spec.rb @@ -2,127 +2,19 @@ require 'spec_helper' +# This is a compatibility class to avoid calling a non-existent +# class from sidekiq during deployment. +# +# We're deploying the name of the referenced class in 13.9. Nevertheless, +# we cannot remove the class entirely because there can be jobs +# referencing it. We still need this specs to ensure that the old +# class still has the old behavior. +# +# We can get rid of this class in 13.10 +# https://gitlab.com/gitlab-org/gitlab/-/issues/297580 +# RSpec.describe Projects::HousekeepingService do - subject { described_class.new(project) } - - let_it_be(:project) { create(:project, :repository) } - - before do - project.reset_pushes_since_gc - end - - after do - project.reset_pushes_since_gc - end - - describe '#execute' do - it 'enqueues a sidekiq job' do - expect(subject).to receive(:try_obtain_lease).and_return(:the_uuid) - expect(subject).to receive(:lease_key).and_return(:the_lease_key) - expect(subject).to receive(:task).and_return(:incremental_repack) - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid).and_call_original - - Sidekiq::Testing.fake! do - expect { subject.execute }.to change(GitGarbageCollectWorker.jobs, :size).by(1) - end - end - - it 'yields the block if given' do - expect do |block| - subject.execute(&block) - end.to yield_with_no_args - end - - it 'resets counter after execution' do - expect(subject).to receive(:try_obtain_lease).and_return(:the_uuid) - allow(subject).to receive(:gc_period).and_return(1) - project.increment_pushes_since_gc - - perform_enqueued_jobs do - expect { subject.execute }.to change { project.pushes_since_gc }.to(0) - end - end - - context 'when no lease can be obtained' do - before do - expect(subject).to receive(:try_obtain_lease).and_return(false) - end - - it 'does not enqueue a job' do - expect(GitGarbageCollectWorker).not_to receive(:perform_async) - - expect { subject.execute }.to raise_error(Projects::HousekeepingService::LeaseTaken) - end - - it 'does not reset pushes_since_gc' do - expect do - expect { subject.execute }.to raise_error(Projects::HousekeepingService::LeaseTaken) - end.not_to change { project.pushes_since_gc } - end - - it 'does not yield' do - expect do |block| - expect { subject.execute(&block) } - .to raise_error(Projects::HousekeepingService::LeaseTaken) - end.not_to yield_with_no_args - end - end - - context 'task type' do - it 'goes through all three housekeeping tasks, executing only the highest task when there is overlap' do - allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid) - allow(subject).to receive(:lease_key).and_return(:the_lease_key) - - # At push 200 - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid) - .once - # At push 50, 100, 150 - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid) - .exactly(3).times - # At push 10, 20, ... (except those above) - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid) - .exactly(16).times - # At push 6, 12, 18, ... (except those above) - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :pack_refs, :the_lease_key, :the_uuid) - .exactly(27).times - - 201.times do - subject.increment! - subject.execute if subject.needed? - end - - expect(project.pushes_since_gc).to eq(1) - end - end - - it 'runs the task specifically requested' do - housekeeping = described_class.new(project, :gc) - - allow(housekeeping).to receive(:try_obtain_lease).and_return(:gc_uuid) - allow(housekeeping).to receive(:lease_key).and_return(:gc_lease_key) - - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :gc_lease_key, :gc_uuid).twice - - 2.times do - housekeeping.execute - end - end - end - - describe '#needed?' do - it 'when the count is low enough' do - expect(subject.needed?).to eq(false) - end - - it 'when the count is high enough' do - allow(project).to receive(:pushes_since_gc).and_return(10) - expect(subject.needed?).to eq(true) - end - end - - describe '#increment!' do - it 'increments the pushes_since_gc counter' do - expect { subject.increment! }.to change { project.pushes_since_gc }.by(1) - end + it_behaves_like 'housekeeps repository' do + let_it_be(:resource) { create(:project, :repository) } end end diff --git a/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb b/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb index 5b76386bfab..15c9d1e5925 100644 --- a/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb +++ b/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb @@ -3,45 +3,10 @@ require 'spec_helper' RSpec.describe Projects::ScheduleBulkRepositoryShardMovesService do - before do - stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' }) - end - - let!(:project) { create(:project, :repository).tap { |project| project.track_project_repository } } - let(:source_storage_name) { 'default' } - let(:destination_storage_name) { 'test_second_storage' } - - describe '#execute' do - it 'schedules project repository storage moves' do - expect { subject.execute(source_storage_name, destination_storage_name) } - .to change(ProjectRepositoryStorageMove, :count).by(1) - - storage_move = project.repository_storage_moves.last! - - expect(storage_move).to have_attributes( - source_storage_name: source_storage_name, - destination_storage_name: destination_storage_name, - state_name: :scheduled - ) - end - - context 'read-only repository' do - let!(:project) { create(:project, :repository, :read_only).tap { |project| project.track_project_repository } } - - it 'does not get scheduled' do - expect(subject).to receive(:log_info) - .with("Project #{project.full_path} (#{project.id}) was skipped: Project is read only") - expect { subject.execute(source_storage_name, destination_storage_name) } - .to change(ProjectRepositoryStorageMove, :count).by(0) - end - end - end - - describe '.enqueue' do - it 'defers to the worker' do - expect(::ProjectScheduleBulkRepositoryShardMovesWorker).to receive(:perform_async).with(source_storage_name, destination_storage_name) + it_behaves_like 'moves repository shard in bulk' do + let_it_be_with_reload(:container) { create(:project, :repository).tap { |project| project.track_project_repository } } - described_class.enqueue(source_storage_name, destination_storage_name) - end + let(:move_service_klass) { ProjectRepositoryStorageMove } + let(:bulk_worker_klass) { ::ProjectScheduleBulkRepositoryShardMovesWorker } end end diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index a15f6bdbe2c..a6730c5de52 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -79,6 +79,19 @@ RSpec.describe Projects::UpdatePagesService do end end + it 'fails if sha on branch was updated before deployment was uploaded' do + expect(subject).to receive(:create_pages_deployment).and_wrap_original do |m, *args| + build.update!(ref: 'feature') + m.call(*args) + end + + expect(execute).not_to eq(:success) + expect(project.pages_metadatum).not_to be_deployed + + expect(deploy_status).to be_failed + expect(deploy_status.description).to eq('build SHA is outdated for this ref') + end + it 'does not fail if pages_metadata is absent' do project.pages_metadatum.destroy! project.reload diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 760cd85bf71..a59b6adf346 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -15,13 +15,6 @@ RSpec.describe Projects::UpdateService do let(:admin) { create(:admin) } context 'when changing visibility level' do - def expect_to_call_unlink_fork_service - service = Projects::UnlinkForkService.new(project, user) - - expect(Projects::UnlinkForkService).to receive(:new).with(project, user).and_return(service) - expect(service).to receive(:execute).and_call_original - end - context 'when visibility_level changes to INTERNAL' do it 'updates the project to internal' do expect(TodosDestroyer::ProjectPrivateWorker).not_to receive(:perform_in) @@ -31,18 +24,6 @@ RSpec.describe Projects::UpdateService do expect(result).to eq({ status: :success }) expect(project).to be_internal end - - context 'and project is PUBLIC' do - before do - project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) - end - - it 'unlinks project from fork network' do - expect_to_call_unlink_fork_service - - update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL) - end - end end context 'when visibility_level changes to PUBLIC' do @@ -78,30 +59,6 @@ RSpec.describe Projects::UpdateService do expect(result).to eq({ status: :success }) expect(project).to be_private end - - context 'and project is PUBLIC' do - before do - project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) - end - - it 'unlinks project from fork network' do - expect_to_call_unlink_fork_service - - update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE) - end - end - - context 'and project is INTERNAL' do - before do - project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) - end - - it 'unlinks project from fork network' do - expect_to_call_unlink_fork_service - - update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE) - end - end end context 'when visibility levels are restricted to PUBLIC only' do -- cgit v1.2.3