From f4be516a89293bddb0dcc8afb991e627c90ec8dd Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Sat, 12 Apr 2025 12:09:40 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .rubocop.yml | 1 + app/graphql/types/tree/tree_type.rb | 7 ++ app/presenters/projects/tree_presenter.rb | 20 ++++++ doc/api/graphql/reference/_index.md | 1 + doc/api/openapi/openapi_v2.yaml | 8 +++ gems/config/rubocop.yml | 6 ++ .../cop/rake/top_level_method_definition.rb | 14 ---- spec/graphql/types/tree/tree_type_spec.rb | 2 +- .../projects/tree_presenter_spec.rb | 57 ++++++++++++++++ .../rake/top_level_method_definition_spec.rb | 65 ++++++++++++++++--- 10 files changed, 156 insertions(+), 25 deletions(-) create mode 100644 app/presenters/projects/tree_presenter.rb create mode 100644 spec/presenters/projects/tree_presenter_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 5a4c13bed36..c50a536e2f6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1274,6 +1274,7 @@ Gitlab/TokenWithoutPrefix: Rake/TopLevelMethodDefinition: Enabled: true Include: + - '**/Rakefile' - 'lib/tasks/**/*.rake' - 'ee/lib/tasks/**/*.rake' diff --git a/app/graphql/types/tree/tree_type.rb b/app/graphql/types/tree/tree_type.rb index 9fadb44220d..e943f987499 100644 --- a/app/graphql/types/tree/tree_type.rb +++ b/app/graphql/types/tree/tree_type.rb @@ -6,6 +6,8 @@ module Types class TreeType < BaseObject graphql_name 'Tree' + present_using ::Projects::TreePresenter + # Complexity 10 as it triggers a Gitaly call on each render field :last_commit, Types::Repositories::CommitType, null: true, complexity: 10, calls_gitaly: true, resolver: Resolvers::LastCommitResolver, @@ -22,6 +24,11 @@ module Types description: 'Blobs of the tree.', calls_gitaly: true + field :permalink_path, GraphQL::Types::String, null: true, + description: 'Web path to tree permalink.', + calls_gitaly: true, + experiment: { milestone: '17.11' } + def trees Gitlab::Graphql::Representation::TreeEntry.decorate(object.trees, object.repository) end diff --git a/app/presenters/projects/tree_presenter.rb b/app/presenters/projects/tree_presenter.rb new file mode 100644 index 00000000000..51ce5b5d4e1 --- /dev/null +++ b/app/presenters/projects/tree_presenter.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Projects + class TreePresenter < Gitlab::View::Presenter::Delegated + presents Tree, as: :tree + + def permalink_path + return unless tree.sha.present? + + project = tree.repository.project + commit = tree.repository.commit(tree.sha) + return unless commit + + path = tree.path.presence + full_path = path.present? ? File.join(commit.sha, path) : commit.sha + + Gitlab::Routing.url_helpers.project_tree_path(project, full_path) + end + end +end diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md index 63e33548167..eb4616f5894 100644 --- a/doc/api/graphql/reference/_index.md +++ b/doc/api/graphql/reference/_index.md @@ -39085,6 +39085,7 @@ Representing a to-do entry. | Name | Type | Description | | ---- | ---- | ----------- | | `blobs` | [`BlobConnection!`](#blobconnection) | Blobs of the tree. (see [Connections](#connections)) | +| `permalinkPath` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 17.11. **Status**: Experiment. Web path to tree permalink. | | `submodules` | [`SubmoduleConnection!`](#submoduleconnection) | Sub-modules of the tree. (see [Connections](#connections)) | | `trees` | [`TreeEntryConnection!`](#treeentryconnection) | Trees of the tree. (see [Connections](#connections)) | diff --git a/doc/api/openapi/openapi_v2.yaml b/doc/api/openapi/openapi_v2.yaml index 6ef336bdddb..52be670e6c6 100644 --- a/doc/api/openapi/openapi_v2.yaml +++ b/doc/api/openapi/openapi_v2.yaml @@ -43524,6 +43524,8 @@ definitions: type: string repository_storage: type: string + duo_nano_features_enabled: + type: string duo_features_enabled: type: string lock_duo_features_enabled: @@ -44050,6 +44052,10 @@ definitions: service_access_tokens_expiration_enforced: type: boolean description: To enforce token expiration for Service accounts users for group + duo_nano_features_enabled: + type: boolean + description: Indicates whether GitLab Duo Nano features are enabled for the + group duo_features_enabled: type: boolean description: Indicates whether GitLab Duo features are enabled for the group @@ -44163,6 +44169,8 @@ definitions: type: string repository_storage: type: string + duo_nano_features_enabled: + type: string duo_features_enabled: type: string lock_duo_features_enabled: diff --git a/gems/config/rubocop.yml b/gems/config/rubocop.yml index 9feeebb40b5..91884794b4b 100644 --- a/gems/config/rubocop.yml +++ b/gems/config/rubocop.yml @@ -147,3 +147,9 @@ Rails/StrongParams: # This cop doesn't make sense in the context of gems Gitlab/NoFindInWorkers: Enabled: false + +Rake/TopLevelMethodDefinition: + Enabled: true + Include: + - '**/Rakefile' + - '**/*.rake' diff --git a/rubocop/cop/rake/top_level_method_definition.rb b/rubocop/cop/rake/top_level_method_definition.rb index 408d0b170cf..bb227825354 100644 --- a/rubocop/cop/rake/top_level_method_definition.rb +++ b/rubocop/cop/rake/top_level_method_definition.rb @@ -79,34 +79,20 @@ module RuboCop 'See https://github.com/rubocop/rubocop-rake/issues/42' def on_def(node) - return unless in_rake_file? - add_offense(node) end def on_defs(node) - return unless in_rake_file? - add_offense(node) end def on_class(node) - return unless in_rake_file? - add_offense(node, message: CLASS_MSG) end def on_module(node) - return unless in_rake_file? - add_offense(node, message: MODULE_MSG) end - - private - - def in_rake_file? - processed_source.file_path.end_with?('.rake') - end end end end diff --git a/spec/graphql/types/tree/tree_type_spec.rb b/spec/graphql/types/tree/tree_type_spec.rb index 362ecdfca91..573b6904742 100644 --- a/spec/graphql/types/tree/tree_type_spec.rb +++ b/spec/graphql/types/tree/tree_type_spec.rb @@ -5,5 +5,5 @@ require 'spec_helper' RSpec.describe Types::Tree::TreeType do specify { expect(described_class.graphql_name).to eq('Tree') } - specify { expect(described_class).to have_graphql_fields(:trees, :submodules, :blobs, :last_commit) } + specify { expect(described_class).to have_graphql_fields(:trees, :submodules, :blobs, :last_commit, :permalink_path) } end diff --git a/spec/presenters/projects/tree_presenter_spec.rb b/spec/presenters/projects/tree_presenter_spec.rb new file mode 100644 index 00000000000..72bd89015e6 --- /dev/null +++ b/spec/presenters/projects/tree_presenter_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::TreePresenter, feature_category: :source_code_management do + let_it_be(:project) { create(:project, :repository) } # rubocop:disable RSpec/FactoryBot/AvoidCreate -- Need persisted objects + let(:repository) { project.repository } + let(:user) { project.first_owner } + + let(:ref) { 'HEAD' } + let(:path) { 'lib' } + + let(:commit) { repository.commit(ref) } + let(:tree) { repository.tree(ref, path) } + + subject(:presenter) { described_class.new(tree, current_user: user) } + + describe '#permalink_path' do + it 'returns the permalink path with commit SHA and directory path' do + expect(presenter.permalink_path).to eq("/#{project.full_path}/-/tree/#{commit.sha}/#{path}") + end + + context 'when tree path is empty (root tree)' do + let(:path) { '' } + + it 'returns the permalink path pointing to the commit SHA only' do + expect(presenter.permalink_path).to eq("/#{project.full_path}/-/tree/#{commit.sha}/") + end + end + + context 'when tree has no sha' do + before do + tree.sha = nil + end + + it 'returns nil' do + expect(presenter.permalink_path).to be_nil + end + end + + context 'when commit is not found' do + before do + allow(repository).to receive(:commit).and_return(nil) + end + + let(:tree) do + repository.tree(ref, path).tap do |t| + t.sha = 'nonexistentsha123' + end + end + + it 'returns nil' do + expect(presenter.permalink_path).to be_nil + end + end + end +end diff --git a/spec/rubocop/cop/rake/top_level_method_definition_spec.rb b/spec/rubocop/cop/rake/top_level_method_definition_spec.rb index 385965ade66..62673f4545e 100644 --- a/spec/rubocop/cop/rake/top_level_method_definition_spec.rb +++ b/spec/rubocop/cop/rake/top_level_method_definition_spec.rb @@ -180,32 +180,77 @@ RSpec.describe RuboCop::Cop::Rake::TopLevelMethodDefinition, :aggregate_failures end end - context 'in a non-rake file' do - let(:source_file) { 'elastic.rb' } + context 'in a Rakefile' do + let(:source_file) { 'Rakefile' } - it 'does not register an offense for method definitions outside modules' do - expect_no_offenses(<<~RUBY, source_file) + it 'registers an offense for method definitions' do + expect_offense(<<~RUBY, source_file) def task_executor_service + ^^^^^^^^^^^^^^^^^^^^^^^^^ Methods defined in rake tasks share the same namespace and can cause collisions. Please define it in a bounded contexts module in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 Search::RakeTaskExecutorService.new(logger: stdout_logger) end RUBY end - it 'does not register an offense for method definitions inside blocks' do - expect_no_offenses(<<~RUBY, source_file) - something do - def task_executor_service + it 'registers an offense for class definitions' do + expect_offense(<<~RUBY, source_file) + class TaskHelper + ^^^^^^^^^^^^^^^^ Classes should not be defined in rake files. Please define it in a bounded contexts module in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 + def self.task_executor_service + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Methods defined in rake tasks share the same namespace and can cause collisions. Please define it in a bounded contexts module in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 Search::RakeTaskExecutorService.new(logger: stdout_logger) end end RUBY end - it 'does not register an offense for class and module definitions' do - expect_no_offenses(<<~RUBY, source_file) + it 'registers an offense for module definitions' do + expect_offense(<<~RUBY, source_file) module SomeNamespace + ^^^^^^^^^^^^^^^^^^^^ Modules should not be defined in rake files. Please define it in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 class TaskHelper + ^^^^^^^^^^^^^^^^ Classes should not be defined in rake files. Please define it in a bounded contexts module in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 def self.task_executor_service + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Methods defined in rake tasks share the same namespace and can cause collisions. Please define it in a bounded contexts module in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 + Search::RakeTaskExecutorService.new(logger: stdout_logger) + end + end + end + RUBY + end + end + + context 'in non-rake files (should still be checked based on .rubocop.yml Include directive)' do + let(:source_file) { 'elastic.rb' } + + it 'registers an offense for method definitions outside modules' do + expect_offense(<<~RUBY, source_file) + def task_executor_service + ^^^^^^^^^^^^^^^^^^^^^^^^^ Methods defined in rake tasks share the same namespace and can cause collisions. Please define it in a bounded contexts module in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 + Search::RakeTaskExecutorService.new(logger: stdout_logger) + end + RUBY + end + + it 'registers an offense for method definitions inside blocks' do + expect_offense(<<~RUBY, source_file) + something do + def task_executor_service + ^^^^^^^^^^^^^^^^^^^^^^^^^ Methods defined in rake tasks share the same namespace and can cause collisions. Please define it in a bounded contexts module in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 + Search::RakeTaskExecutorService.new(logger: stdout_logger) + end + end + RUBY + end + + it 'registers an offense for class and module definitions' do + expect_offense(<<~RUBY, source_file) + module SomeNamespace + ^^^^^^^^^^^^^^^^^^^^ Modules should not be defined in rake files. Please define it in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 + class TaskHelper + ^^^^^^^^^^^^^^^^ Classes should not be defined in rake files. Please define it in a bounded contexts module in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 + def self.task_executor_service + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Methods defined in rake tasks share the same namespace and can cause collisions. Please define it in a bounded contexts module in a separate Ruby file. For example, Search::RakeTask::. See https://github.com/rubocop/rubocop-rake/issues/42 Search::RakeTaskExecutorService.new(logger: stdout_logger) end end