diff --git a/app/assets/javascripts/ci/job_details/index.js b/app/assets/javascripts/ci/job_details/index.js
index dee77d404a8..424ae698861 100644
--- a/app/assets/javascripts/ci/job_details/index.js
+++ b/app/assets/javascripts/ci/job_details/index.js
@@ -26,7 +26,6 @@ export const initJobDetails = () => {
subscriptionsMoreMinutesUrl,
endpoint,
pagePath,
- buildStatus,
projectPath,
retryOutdatedJobDocsUrl,
aiRootCauseAnalysisAvailable,
@@ -55,10 +54,6 @@ export const initJobDetails = () => {
deploymentHelpUrl,
runnerSettingsUrl,
subscriptionsMoreMinutesUrl,
- endpoint,
- pagePath,
- buildStatus,
- projectPath,
},
});
},
diff --git a/app/assets/javascripts/ci/job_details/job_app.vue b/app/assets/javascripts/ci/job_details/job_app.vue
index 1c581a2df78..491cd1d35b9 100644
--- a/app/assets/javascripts/ci/job_details/job_app.vue
+++ b/app/assets/javascripts/ci/job_details/job_app.vue
@@ -56,15 +56,6 @@ export default {
required: false,
default: null,
},
- terminalPath: {
- type: String,
- required: false,
- default: null,
- },
- projectPath: {
- type: String,
- required: true,
- },
subscriptionsMoreMinutesUrl: {
type: String,
required: false,
@@ -261,7 +252,6 @@ export default {
v-if="shouldRenderSharedRunnerLimitWarning"
:quota-used="job.runners.quota.used"
:quota-limit="job.runners.quota.limit"
- :project-path="projectPath"
:subscriptions-more-minutes-url="subscriptionsMoreMinutesUrl"
/>
diff --git a/app/graphql/resolvers/work_item_references_resolver.rb b/app/graphql/resolvers/work_item_references_resolver.rb
new file mode 100644
index 00000000000..4aa071519db
--- /dev/null
+++ b/app/graphql/resolvers/work_item_references_resolver.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class WorkItemReferencesResolver < BaseResolver
+ prepend ::WorkItems::LookAheadPreloads
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ REFERENCES_LIMIT = 10
+
+ authorize :read_work_item
+
+ type ::Types::WorkItemType.connection_type, null: true
+
+ argument :context_namespace_path, GraphQL::Types::ID,
+ required: false,
+ description: 'Full path of the context namespace (project or group).'
+
+ argument :refs, [GraphQL::Types::String], required: true,
+ description: 'Work item references. Can be either a short reference or URL.'
+
+ def ready?(**args)
+ if args[:refs].size > REFERENCES_LIMIT
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ format(
+ _('Number of references exceeds the limit. ' \
+ 'Please provide no more than %{refs_limit} references at the same time.'),
+ refs_limit: REFERENCES_LIMIT
+ )
+ end
+
+ super
+ end
+
+ def resolve_with_lookahead(context_namespace_path: nil, refs: [])
+ return WorkItem.none if refs.empty?
+
+ @container = authorized_find!(context_namespace_path)
+ # Only ::Project is supported at the moment, future iterations will include ::Group.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/432555
+ return WorkItem.none if container.is_a?(::Group)
+
+ apply_lookahead(find_work_items(refs))
+ end
+
+ private
+
+ attr_reader :container
+
+ # rubocop: disable CodeReuse/ActiveRecord -- #references is not an ActiveRecord method
+ def find_work_items(references)
+ links, short_references = references.partition { |r| r.include?('/work_items/') }
+
+ item_ids = references_extractor(short_references).references(:issue, ids_only: true)
+ item_ids << references_extractor(links).references(:work_item, ids_only: true) if links.any?
+
+ WorkItem.id_in(item_ids.flatten)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def references_extractor(refs)
+ extractor = ::Gitlab::ReferenceExtractor.new(container, context[:current_user])
+ extractor.analyze(refs.join(' '), {})
+
+ extractor
+ end
+
+ def find_object(full_path)
+ Routable.find_by_full_path(full_path)
+ end
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 9c64e056f87..0e39ff2c030 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -216,6 +216,13 @@ module Types
description: 'Find machine learning models.',
resolver: Resolvers::Ml::ModelDetailResolver
+ field :work_items_by_reference,
+ null: true,
+ alpha: { milestone: '16.7' },
+ description: 'Find work items by their reference.',
+ extras: [:lookahead],
+ resolver: Resolvers::WorkItemReferencesResolver
+
def design_management
DesignManagementObject.new(nil)
end
diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb
index 5e902ce7bc6..c3af2de16e1 100644
--- a/app/helpers/ci/jobs_helper.rb
+++ b/app/helpers/ci/jobs_helper.rb
@@ -10,8 +10,6 @@ module Ci
"artifact_help_url" => help_page_path('user/gitlab_com/index.md', anchor: 'gitlab-cicd'),
"deployment_help_url" => help_page_path('user/project/clusters/deploy_to_cluster.md', anchor: 'troubleshooting'),
"runner_settings_url" => project_runners_path(build.project, anchor: 'js-runners-settings'),
- "build_status" => build.status,
- "build_stage" => build.stage_name,
"retry_outdated_job_docs_url" => help_page_path('ci/pipelines/settings', anchor: 'retry-outdated-jobs'),
"test_report_summary_url" => test_report_summary_project_job_path(project, build, format: :json),
"pipeline_test_report_url" => test_report_project_pipeline_path(project, build.pipeline)
diff --git a/app/workers/concerns/update_repository_storage_worker.rb b/app/workers/concerns/update_repository_storage_worker.rb
index ba4998c2102..fd437ebc158 100644
--- a/app/workers/concerns/update_repository_storage_worker.rb
+++ b/app/workers/concerns/update_repository_storage_worker.rb
@@ -38,36 +38,32 @@ module UpdateRepositoryStorageWorker
container_id ||= repository_storage_move.container_id
- if Feature.enabled?(:use_lock_for_update_repository_storage)
- # Use exclusive lock to prevent multiple storage migrations at the same time
- #
- # Note: instead of using a randomly generated `uuid`, we provide a worker jid value.
- # That will allow to track a worker that requested a lease.
- lease_key = [self.class.name.underscore, container_id].join(':')
- exclusive_lease = Gitlab::ExclusiveLease.new(lease_key, uuid: jid, timeout: LEASE_TIMEOUT)
- lease = exclusive_lease.try_obtain
+ # Use exclusive lock to prevent multiple storage migrations at the same time
+ #
+ # Note: instead of using a randomly generated `uuid`, we provide a worker jid value.
+ # That will allow to track a worker that requested a lease.
+ lease_key = [self.class.name.underscore, container_id].join(':')
+ exclusive_lease = Gitlab::ExclusiveLease.new(lease_key, uuid: jid, timeout: LEASE_TIMEOUT)
+ lease = exclusive_lease.try_obtain
- if lease
- begin
- update_repository_storage(repository_storage_move)
- ensure
- exclusive_lease.cancel
- end
- else
- # If there is an ungoing storage migration, then the current one should be marked as failed
- repository_storage_move.do_fail!
-
- # A special case
- # Sidekiq can receive an interrupt signal during the processing.
- # It kills existing workers and reschedules their jobs using the same jid.
- # But it can cause a situation when the migration is only half complete (see https://gitlab.com/gitlab-org/gitlab/-/issues/429049#note_1635650597)
- #
- # Here we detect this case and release the lock.
- uuid = Gitlab::ExclusiveLease.get_uuid(lease_key)
- exclusive_lease.cancel if uuid == jid
+ if lease
+ begin
+ update_repository_storage(repository_storage_move)
+ ensure
+ exclusive_lease.cancel
end
else
- update_repository_storage(repository_storage_move)
+ # If there is an ungoing storage migration, then the current one should be marked as failed
+ repository_storage_move.do_fail!
+
+ # A special case
+ # Sidekiq can receive an interrupt signal during the processing.
+ # It kills existing workers and reschedules their jobs using the same jid.
+ # But it can cause a situation when the migration is only half complete (see https://gitlab.com/gitlab-org/gitlab/-/issues/429049#note_1635650597)
+ #
+ # Here we detect this case and release the lock.
+ uuid = Gitlab::ExclusiveLease.get_uuid(lease_key)
+ exclusive_lease.cancel if uuid == jid
end
end
diff --git a/config/feature_flags/development/use_lock_for_update_repository_storage.yml b/config/feature_flags/development/use_lock_for_update_repository_storage.yml
deleted file mode 100644
index 8f08208bca0..00000000000
--- a/config/feature_flags/development/use_lock_for_update_repository_storage.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: use_lock_for_update_repository_storage
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136169
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431198
-milestone: '16.6'
-type: development
-group: group::source code
-default_enabled: false
diff --git a/db/docs/batched_background_migrations/backfill_code_suggestions_namespace_settings.yml b/db/docs/batched_background_migrations/backfill_code_suggestions_namespace_settings.yml
index 23f3fd43174..a6e04394d24 100644
--- a/db/docs/batched_background_migrations/backfill_code_suggestions_namespace_settings.yml
+++ b/db/docs/batched_background_migrations/backfill_code_suggestions_namespace_settings.yml
@@ -4,3 +4,4 @@ description: Updates default value of code_suggestions on namespace_settings tab
feature_category: code_suggestions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121126
milestone: '16.1'
+finalized_by: '20231206145850'
diff --git a/db/post_migrate/20231205092529_drop_unique_index_job_id_file_type_to_ci_job_artifact.rb b/db/post_migrate/20231205092529_drop_unique_index_job_id_file_type_to_ci_job_artifact.rb
new file mode 100644
index 00000000000..df52f76e1c4
--- /dev/null
+++ b/db/post_migrate/20231205092529_drop_unique_index_job_id_file_type_to_ci_job_artifact.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class DropUniqueIndexJobIdFileTypeToCiJobArtifact < Gitlab::Database::Migration[2.2]
+ milestone '16.7'
+ disable_ddl_transaction!
+ TABLE_NAME = :ci_job_artifacts
+ INDEX_NAME = :index_ci_job_artifacts_on_job_id_and_file_type
+
+ def up
+ remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index(TABLE_NAME, %i[job_id file_type], unique: true, name: INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20231206145850_finalize_backfill_code_suggestions_namespace_settings.rb b/db/post_migrate/20231206145850_finalize_backfill_code_suggestions_namespace_settings.rb
new file mode 100644
index 00000000000..3aa30ef2bb6
--- /dev/null
+++ b/db/post_migrate/20231206145850_finalize_backfill_code_suggestions_namespace_settings.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class FinalizeBackfillCodeSuggestionsNamespaceSettings < Gitlab::Database::Migration[2.2]
+ milestone '16.7'
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
+
+ def up
+ ensure_batched_background_migration_is_finished(
+ job_class_name: 'BackfillCodeSuggestionsNamespaceSettings',
+ table_name: :namespace_settings,
+ column_name: :namespace_id,
+ job_arguments: [],
+ finalize: true
+ )
+ end
+
+ def down; end
+end
diff --git a/db/schema_migrations/20231205092529 b/db/schema_migrations/20231205092529
new file mode 100644
index 00000000000..adc729e9f7a
--- /dev/null
+++ b/db/schema_migrations/20231205092529
@@ -0,0 +1 @@
+5ca9bd14a7c69b4b77745303c47c7d11890f3ced97c8a1a68b5b713b29a2dab7
\ No newline at end of file
diff --git a/db/schema_migrations/20231206145850 b/db/schema_migrations/20231206145850
new file mode 100644
index 00000000000..6d291813c04
--- /dev/null
+++ b/db/schema_migrations/20231206145850
@@ -0,0 +1 @@
+53442f9c3ef0e0f3f31b4be177faf3d073ee8b74d20ede7a1673bedfa097f0b9
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 551de511faf..b8b0f57a22e 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -32155,8 +32155,6 @@ CREATE INDEX index_ci_job_artifacts_on_id_project_id_and_created_at ON ci_job_ar
CREATE INDEX index_ci_job_artifacts_on_id_project_id_and_file_type ON ci_job_artifacts USING btree (project_id, file_type, id);
-CREATE UNIQUE INDEX index_ci_job_artifacts_on_job_id_and_file_type ON ci_job_artifacts USING btree (job_id, file_type);
-
CREATE INDEX index_ci_job_artifacts_on_partition_id_job_id ON ci_job_artifacts USING btree (partition_id, job_id);
CREATE INDEX index_ci_job_artifacts_on_project_id ON ci_job_artifacts USING btree (project_id);
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index a8dc50834d4..757bc559c29 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1033,6 +1033,27 @@ Returns [`WorkItem`](#workitem).
| ---- | ---- | ----------- |
| `id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. |
+### `Query.workItemsByReference`
+
+Find work items by their reference.
+
+WARNING:
+**Introduced** in 16.7.
+This feature is an Experiment. It can be changed or removed at any time.
+
+Returns [`WorkItemConnection`](#workitemconnection).
+
+This field returns a [connection](#connections). It accepts the
+four standard [pagination arguments](#connection-pagination-arguments):
+`before: String`, `after: String`, `first: Int`, `last: Int`.
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `contextNamespacePath` | [`ID`](#id) | Full path of the context namespace (project or group). |
+| `refs` | [`[String!]!`](#string) | Work item references. Can be either a short reference or URL. |
+
### `Query.workspace`
Find a workspace.
diff --git a/doc/user/project/repository/code_suggestions/index.md b/doc/user/project/repository/code_suggestions/index.md
index f7a8d9cac8f..d73668c632c 100644
--- a/doc/user/project/repository/code_suggestions/index.md
+++ b/doc/user/project/repository/code_suggestions/index.md
@@ -16,16 +16,18 @@ Beta users should read about the [known limitations](#known-limitations). We loo
Write code more efficiently by using generative AI to suggest code while you're developing.
-Code Suggestions supports two distinct types of interactions:
+With Code Suggestions, you get:
- Code Completion, which suggests completions the current line you are typing. These suggestions are usually low latency.
- Code Generation, which generates code based on a natural language code comment block. Generating code can exceed multiple seconds.
+## Start using Code Suggestions
+
GitLab Duo Code Suggestions are available:
-- On [self-managed](self_managed.md) and [SaaS](saas.md).
+- On [self-managed](self_managed.md) and [SaaS](saas.md). View these pages to get started.
- In VS Code, Microsoft Visual Studio, JetBrains IDEs, and Neovim. You must have the corresponding GitLab extension installed.
-- In the GitLab WebIDE.
+- In the GitLab Web IDE.
View how to setup and use GitLab Duo Code Suggestions.
@@ -37,36 +39,6 @@ GitLab Duo Code Suggestions are available:
During Beta, usage of Code Suggestions is governed by the [GitLab Testing Agreement](https://about.gitlab.com/handbook/legal/testing-agreement/).
Learn about [data usage when using Code Suggestions](#code-suggestions-data-usage). As Code Suggestions matures to General Availability it will be governed by our [AI Functionality Terms](https://about.gitlab.com/handbook/legal/ai-functionality-terms/).
-## Use Code Suggestions
-
-Prerequisites:
-
-- You must have installed and configured a [supported IDE editor extension](index.md#supported-editor-extensions).
-- If you are a **SaaS** user, you must enable Code Suggestions for:
- - [The top-level group](../../../group/manage.md#enable-code-suggestions) (you must have the Owner role for that group).
- - [Your own account](../../../profile/preferences.md#enable-code-suggestions).
-- If you are a **self-managed** user, you must enable Code Suggestions [for your instance](self_managed.md#enable-code-suggestions-on-self-managed-gitlab). How you enable Code Suggestions differs depending on your version of GitLab.
-
-To use Code Suggestions:
-
-1. Author your code. As you type, suggestions are displayed. Code Suggestions, depending on the cursor position, either provides code snippets or completes the current line.
-1. Describe the requirements in natural language. Be concise and specific. Code Suggestions generates functions and code snippets as appropriate.
-1. To accept a suggestion, press
Tab.
-1. To ignore a suggestion, keep typing as you usually would.
-1. To explicitly reject a suggestion, press
esc.
-
-Things to remember:
-
-- AI is non-deterministic, so you may not get the same suggestion every time with the same input.
-- Just like product requirements, writing clear, descriptive, and specific tasks results in quality generated code.
-
-### Progressive enhancement
-
-This feature is designed as a progressive enhancement to developer's IDEs.
-Code Suggestions offer a completion if a suitable recommendation is provided to the user in a timely matter.
-In the event of a connection issue or model inference failure, the feature gracefully degrades.
-Code Suggestions do not prevent you from writing code in your IDE.
-
## Supported languages
Code Suggestions support is a function of the:
@@ -170,6 +142,13 @@ However, Code Suggestions may generate suggestions that are:
- Insecure code
- Offensive or insensitive
+## Progressive enhancement
+
+This feature is designed as a progressive enhancement to developer's IDEs.
+Code Suggestions offer a completion if a suitable recommendation is provided to the user in a timely matter.
+In the event of a connection issue or model inference failure, the feature gracefully degrades.
+Code Suggestions do not prevent you from writing code in your IDE.
+
## Feedback
Report issues in the [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/405152).
diff --git a/doc/user/project/repository/code_suggestions/saas.md b/doc/user/project/repository/code_suggestions/saas.md
index 77a364f14ff..9abafa72616 100644
--- a/doc/user/project/repository/code_suggestions/saas.md
+++ b/doc/user/project/repository/code_suggestions/saas.md
@@ -34,7 +34,21 @@ If you are having issues enabling Code Suggestions, view the
Prerequisites:
-- Ensure Code Suggestions is enabled for your user and group.
-- You must have installed and configured a [supported IDE editor extension](index.md#supported-editor-extensions).
+- You must have a [supported IDE editor extension](index.md#supported-editor-extensions).
+- Code Suggestions must be enabled for:
+ - [The top-level group](../../../group/manage.md#enable-code-suggestions).
+ - [Your own account](../../../profile/preferences.md#enable-code-suggestions).
-[Use Code Suggestions](index.md#use-code-suggestions).
+To use Code Suggestions:
+
+1. Author your code. As you type, suggestions are displayed.
+ Code Suggestions provide code snippets or complete the current line, depending on the cursor position.
+1. Describe the requirements in natural language. Be concise and specific. Code Suggestions generates functions and code snippets as appropriate.
+1. To accept a suggestion, press
Tab.
+1. To ignore a suggestion, keep typing as you usually would.
+1. To explicitly reject a suggestion, press
Esc.
+
+Things to remember:
+
+- AI is non-deterministic, so you may not get the same suggestion every time with the same input.
+- Just like product requirements, writing clear, descriptive, and specific tasks results in quality generated code.
diff --git a/doc/user/project/repository/code_suggestions/self_managed.md b/doc/user/project/repository/code_suggestions/self_managed.md
index 2e06191a002..6d05aacca54 100644
--- a/doc/user/project/repository/code_suggestions/self_managed.md
+++ b/doc/user/project/repository/code_suggestions/self_managed.md
@@ -151,10 +151,22 @@ Without the manual synchronization, it might take up to 24 hours to active Code
Prerequisites:
-- Code Suggestions must be enabled [for the instance](#enable-code-suggestions-on-self-managed-gitlab).
-- You must have installed and configured a [supported IDE editor extension](index.md#supported-editor-extensions).
+- You must have a [supported IDE editor extension](index.md#supported-editor-extensions).
+- Code Suggestions must be enabled [for your instance](self_managed.md#enable-code-suggestions-on-self-managed-gitlab).
-[Use Code Suggestions](index.md#use-code-suggestions).
+To use Code Suggestions:
+
+1. Author your code. As you type, suggestions are displayed.
+ Code Suggestions provide code snippets or complete the current line, depending on the cursor position.
+1. Describe the requirements in natural language. Be concise and specific. Code Suggestions generates functions and code snippets as appropriate.
+1. To accept a suggestion, press
Tab.
+1. To ignore a suggestion, keep typing as you usually would.
+1. To explicitly reject a suggestion, press
Esc.
+
+Things to remember:
+
+- AI is non-deterministic, so you may not get the same suggestion every time with the same input.
+- Just like product requirements, writing clear, descriptive, and specific tasks results in quality generated code.
### Data privacy
diff --git a/lib/api/entities/ml/mlflow/model_version.rb b/lib/api/entities/ml/mlflow/model_version.rb
index 2f4851d6d58..10fdf3822a5 100644
--- a/lib/api/entities/ml/mlflow/model_version.rb
+++ b/lib/api/entities/ml/mlflow/model_version.rb
@@ -57,7 +57,7 @@ module API
end
def run_id
- ""
+ object.candidate.eid
end
def status
diff --git a/lib/api/entities/ml/mlflow/run_info.rb b/lib/api/entities/ml/mlflow/run_info.rb
index 6133b3a9d4b..04c6069617c 100644
--- a/lib/api/entities/ml/mlflow/run_info.rb
+++ b/lib/api/entities/ml/mlflow/run_info.rb
@@ -5,6 +5,8 @@ module API
module Ml
module Mlflow
class RunInfo < Grape::Entity
+ include ::API::Helpers::RelatedResourcesHelpers
+
expose :run_id
expose :run_id, as: :run_uuid
expose(:experiment_id) { |candidate| candidate.experiment.iid.to_s }
@@ -12,7 +14,7 @@ module API
expose :end_time, expose_nil: false
expose :name, as: :run_name, expose_nil: false
expose(:status) { |candidate| candidate.status.to_s.upcase }
- expose(:artifact_uri) { |candidate, options| "#{options[:packages_url]}#{candidate.artifact_root}" }
+ expose :artifact_uri
expose(:lifecycle_stage) { |candidate| 'active' }
expose(:user_id) { |candidate| candidate.user_id.to_s }
@@ -21,6 +23,34 @@ module API
def run_id
object.eid.to_s
end
+
+ def artifact_uri
+ expose_url(model_version_uri || generic_package_uri)
+ end
+
+ # Example: http://127.0.0.1:3000/api/v4/projects/20/packages/ml_models/my-model-name-4/3.0.0
+ def model_version_uri
+ return unless object.model_version_id
+
+ model_version = object.model_version
+
+ path = api_v4_projects_packages_ml_models_model_version_path(
+ id: object.project.id, model_name: model_version.model.name, model_version: '', file_name: ''
+ )
+
+ path.sub('/model_version', "/#{model_version.version}")
+ end
+
+ # Example: http://127.0.0.1:3000/api/v4/projects/20/packages/generic/ml_experiment_1/1/
+ # Note: legacy format
+ def generic_package_uri
+ path = api_v4_projects_packages_generic_package_version_path(
+ id: object.project.id, package_name: '', file_name: ''
+ )
+ path = path.delete_suffix('/package_version')
+
+ [path, object.artifact_root].join('')
+ end
end
end
end
diff --git a/lib/api/ml/mlflow/api_helpers.rb b/lib/api/ml/mlflow/api_helpers.rb
index fb830dfe9e3..66d79753110 100644
--- a/lib/api/ml/mlflow/api_helpers.rb
+++ b/lib/api/ml/mlflow/api_helpers.rb
@@ -132,15 +132,6 @@ module API
def model
@model ||= find_model(user_project, params[:name])
end
-
- def packages_url
- path = api_v4_projects_packages_generic_package_version_path(
- id: user_project.id, package_name: '', file_name: ''
- )
- path = path.delete_suffix('/package_version')
-
- expose_url(path)
- end
end
end
end
diff --git a/lib/api/ml/mlflow/runs.rb b/lib/api/ml/mlflow/runs.rb
index 6716db21407..d03bf1fc576 100644
--- a/lib/api/ml/mlflow/runs.rb
+++ b/lib/api/ml/mlflow/runs.rb
@@ -31,7 +31,7 @@ module API
end
post 'create', urgency: :low do
present candidate_repository.create!(experiment, params[:start_time], params[:tags], params[:run_name]),
- with: Entities::Ml::Mlflow::GetRun, packages_url: packages_url
+ with: Entities::Ml::Mlflow::GetRun
end
desc 'Gets an MLFlow Run, which maps to GitLab Candidates' do
@@ -43,7 +43,7 @@ module API
optional :run_uuid, type: String, desc: 'This parameter is ignored'
end
get 'get', urgency: :low do
- present candidate, with: Entities::Ml::Mlflow::GetRun, packages_url: packages_url
+ present candidate, with: Entities::Ml::Mlflow::GetRun
end
desc 'Searches runs/candidates within a project' do
@@ -83,7 +83,7 @@ module API
next_page_token: paginator.cursor_for_next_page
}
- present result, with: Entities::Ml::Mlflow::SearchRuns, packages_url: packages_url
+ present result, with: Entities::Ml::Mlflow::SearchRuns
end
desc 'Updates a Run.' do
@@ -101,7 +101,7 @@ module API
post 'update', urgency: :low do
candidate_repository.update(candidate, params[:status], params[:end_time])
- present candidate, with: Entities::Ml::Mlflow::UpdateRun, packages_url: packages_url
+ present candidate, with: Entities::Ml::Mlflow::UpdateRun
end
desc 'Logs a metric to a run.' do
diff --git a/lib/extracts_ref/ref_extractor.rb b/lib/extracts_ref/ref_extractor.rb
index ac9b0ebb7af..e716c64e63a 100644
--- a/lib/extracts_ref/ref_extractor.rb
+++ b/lib/extracts_ref/ref_extractor.rb
@@ -15,7 +15,7 @@ module ExtractsRef
class << self
def ref_type(type)
- return unless REF_TYPES.include?(type&.downcase)
+ return unless REF_TYPES.include?(type.to_s.downcase)
type.downcase
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3254a800c53..af4930a341b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -32845,6 +32845,9 @@ msgstr ""
msgid "Number of files touched"
msgstr ""
+msgid "Number of references exceeds the limit. Please provide no more than %{refs_limit} references at the same time."
+msgstr ""
+
msgid "Number of replicas"
msgstr ""
diff --git a/rubocop/cop/graphql/id_type.rb b/rubocop/cop/graphql/id_type.rb
index 53d79751fa8..deed68da0b3 100644
--- a/rubocop/cop/graphql/id_type.rb
+++ b/rubocop/cop/graphql/id_type.rb
@@ -6,7 +6,10 @@ module RuboCop
class IDType < RuboCop::Cop::Base
MSG = 'Do not use GraphQL::Types::ID, use a specific GlobalIDType instead'
- ALLOWLISTED_ARGUMENTS = %i[iid full_path project_path group_path target_project_path namespace_path].freeze
+ ALLOWLISTED_ARGUMENTS = %i[
+ iid full_path project_path group_path target_project_path namespace_path
+ context_namespace_path
+ ].freeze
def_node_search :graphql_id_type?, <<~PATTERN
(send nil? :argument (_ #does_not_match?) (const (const (const nil? :GraphQL) :Types) :ID) ...)
diff --git a/spec/helpers/ci/jobs_helper_spec.rb b/spec/helpers/ci/jobs_helper_spec.rb
index f877e0d87b3..315bda9b2c1 100644
--- a/spec/helpers/ci/jobs_helper_spec.rb
+++ b/spec/helpers/ci/jobs_helper_spec.rb
@@ -25,8 +25,6 @@ RSpec.describe Ci::JobsHelper, feature_category: :continuous_integration do
"artifact_help_url" => "/help/user/gitlab_com/index.md#gitlab-cicd",
"deployment_help_url" => "/help/user/project/clusters/deploy_to_cluster.md#troubleshooting",
"runner_settings_url" => "/#{project.full_path}/-/runners#js-runners-settings",
- "build_status" => "pending",
- "build_stage" => "test",
"retry_outdated_job_docs_url" => "/help/ci/pipelines/settings#retry-outdated-jobs",
"test_report_summary_url" => "/#{project.full_path}/-/jobs/#{job.id}/test_report_summary.json",
"pipeline_test_report_url" => "/#{project.full_path}/-/pipelines/#{job.pipeline.id}/test_report"
diff --git a/spec/lib/api/entities/ml/mlflow/run_info_spec.rb b/spec/lib/api/entities/ml/mlflow/run_info_spec.rb
index 1664d9f18d2..f631a9cb803 100644
--- a/spec/lib/api/entities/ml/mlflow/run_info_spec.rb
+++ b/spec/lib/api/entities/ml/mlflow/run_info_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
RSpec.describe API::Entities::Ml::Mlflow::RunInfo, feature_category: :mlops do
- let_it_be(:candidate) { build(:ml_candidates) }
+ let_it_be(:candidate) { build_stubbed(:ml_candidates, internal_id: 1) }
- subject { described_class.new(candidate, packages_url: 'http://example.com').as_json }
+ subject { described_class.new(candidate).as_json }
context 'when start_time is nil' do
it { expect(subject[:start_time]).to eq(0) }
@@ -66,8 +66,19 @@ RSpec.describe API::Entities::Ml::Mlflow::RunInfo, feature_category: :mlops do
end
describe 'artifact_uri' do
- it 'is not implemented' do
- expect(subject[:artifact_uri]).to eq("http://example.com#{candidate.artifact_root}")
+ context 'when candidate does not belong to a model version' do
+ it 'returns the generic package (legacy) format of the artifact_uri' do
+ expect(subject[:artifact_uri]).to eq("http://localhost/api/v4/projects/#{candidate.project_id}/packages/generic#{candidate.artifact_root}")
+ end
+ end
+
+ context 'when candidate belongs to a model version' do
+ let!(:version) { create(:ml_model_versions, :with_package) }
+ let!(:candidate) { version.candidate }
+
+ it 'returns the model version format of the artifact_uri' do
+ expect(subject[:artifact_uri]).to eq("http://localhost/api/v4/projects/#{candidate.project_id}/packages/ml_models/#{version.model.name}/#{version.version}")
+ end
end
end
diff --git a/spec/lib/api/ml/mlflow/api_helpers_spec.rb b/spec/lib/api/ml/mlflow/api_helpers_spec.rb
index 1f2490fe5eb..3e7a0187d86 100644
--- a/spec/lib/api/ml/mlflow/api_helpers_spec.rb
+++ b/spec/lib/api/ml/mlflow/api_helpers_spec.rb
@@ -5,39 +5,6 @@ require 'spec_helper'
RSpec.describe API::Ml::Mlflow::ApiHelpers, feature_category: :mlops do
include described_class
- describe '#packages_url' do
- subject { packages_url }
-
- let_it_be(:user_project) { build_stubbed(:project) }
-
- context 'with an empty relative URL root' do
- before do
- allow(Gitlab::Application.routes).to receive(:default_url_options)
- .and_return(protocol: 'http', host: 'localhost', script_name: '')
- end
-
- it { is_expected.to eql("http://localhost/api/v4/projects/#{user_project.id}/packages/generic") }
- end
-
- context 'with a forward slash relative URL root' do
- before do
- allow(Gitlab::Application.routes).to receive(:default_url_options)
- .and_return(protocol: 'http', host: 'localhost', script_name: '/')
- end
-
- it { is_expected.to eql("http://localhost/api/v4/projects/#{user_project.id}/packages/generic") }
- end
-
- context 'with a relative URL root' do
- before do
- allow(Gitlab::Application.routes).to receive(:default_url_options)
- .and_return(protocol: 'http', host: 'localhost', script_name: '/gitlab/root')
- end
-
- it { is_expected.to eql("http://localhost/gitlab/root/api/v4/projects/#{user_project.id}/packages/generic") }
- end
- end
-
describe '#candidates_order_params' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/lib/extracts_ref_spec.rb b/spec/lib/extracts_ref_spec.rb
index 9ff11899e89..c7186011654 100644
--- a/spec/lib/extracts_ref_spec.rb
+++ b/spec/lib/extracts_ref_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ExtractsRef do
+RSpec.describe ExtractsRef, feature_category: :source_code_management do
include described_class
include RepoHelpers
@@ -98,6 +98,12 @@ RSpec.describe ExtractsRef do
it { is_expected.to eq(nil) }
end
+
+ context 'when ref_type is a hash' do
+ let(:ref_type) { { 'just' => 'hash' } }
+
+ it { is_expected.to eq(nil) }
+ end
end
it_behaves_like 'extracts refs'
diff --git a/spec/requests/api/graphql/work_items_by_reference_spec.rb b/spec/requests/api/graphql/work_items_by_reference_spec.rb
new file mode 100644
index 00000000000..ad2303a81e7
--- /dev/null
+++ b/spec/requests/api/graphql/work_items_by_reference_spec.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'find work items by reference', feature_category: :portfolio_management do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:group2) { create(:group, :public) }
+ let_it_be(:project2) { create(:project, :repository, :public, group: group2) }
+ let_it_be(:private_project2) { create(:project, :repository, :private, group: group2) }
+ let_it_be(:work_item) { create(:work_item, :task, project: project2) }
+ let_it_be(:private_work_item) { create(:work_item, :task, project: private_project2) }
+
+ let(:references) { [work_item.to_reference(full: true), private_work_item.to_reference(full: true)] }
+
+ shared_examples 'response with matching work items' do
+ it 'returns accessible work item' do
+ post_graphql(query, current_user: current_user)
+
+ expected_items = items.map { |item| a_graphql_entity_for(item) }
+ expect(graphql_data_at('workItemsByReference', 'nodes')).to match(expected_items)
+ end
+ end
+
+ context 'when user has access only to public work items' do
+ it_behaves_like 'a working graphql query that returns data' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
+
+ it_behaves_like 'response with matching work items' do
+ let(:items) { [work_item] }
+ end
+
+ it 'avoids N+1 queries', :use_sql_query_cache do
+ post_graphql(query, current_user: current_user) # warm up
+
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ post_graphql(query, current_user: current_user)
+ end
+ expect(graphql_data_at('workItemsByReference', 'nodes').size).to eq(1)
+
+ extra_work_items = create_list(:work_item, 2, :task, project: project2)
+ refs = references + extra_work_items.map { |item| item.to_reference(full: true) }
+
+ expect do
+ post_graphql(query(refs: refs), current_user: current_user)
+ end.not_to exceed_all_query_limit(control_count)
+ expect(graphql_data_at('workItemsByReference', 'nodes').size).to eq(3)
+ end
+ end
+
+ context 'when user has access to work items in private project' do
+ before_all do
+ private_project2.add_guest(current_user)
+ end
+
+ it_behaves_like 'response with matching work items' do
+ let(:items) { [private_work_item, work_item] }
+ end
+ end
+
+ context 'when refs includes links' do
+ let_it_be(:work_item_with_url) { create(:work_item, :task, project: project2) }
+ let(:references) { [work_item.to_reference(full: true), Gitlab::UrlBuilder.build(work_item_with_url)] }
+
+ it_behaves_like 'response with matching work items' do
+ let(:items) { [work_item_with_url, work_item] }
+ end
+ end
+
+ context 'when refs includes a short reference present in the context project' do
+ let_it_be(:same_project_work_item) { create(:work_item, :task, project: project) }
+ let(:references) { ["##{same_project_work_item.iid}"] }
+
+ it_behaves_like 'response with matching work items' do
+ let(:items) { [same_project_work_item] }
+ end
+ end
+
+ context 'when user cannot access context namespace' do
+ it 'returns error' do
+ post_graphql(query(namespace_path: private_project2.full_path), current_user: current_user)
+
+ expect(graphql_data_at('workItemsByReference')).to be_nil
+ expect(graphql_errors).to contain_exactly(a_hash_including(
+ 'message' => a_string_including("you don't have permission to perform this action"),
+ 'path' => %w[workItemsByReference]
+ ))
+ end
+ end
+
+ context 'when the context is a group' do
+ it 'returns empty result' do
+ group2.add_guest(current_user)
+ post_graphql(query(namespace_path: group2.full_path), current_user: current_user)
+
+ expect_graphql_errors_to_be_empty
+ expect(graphql_data_at('workItemsByReference', 'nodes')).to be_empty
+ end
+ end
+
+ context 'when there are more than the max allowed references' do
+ let(:references_limit) { ::Resolvers::WorkItemReferencesResolver::REFERENCES_LIMIT }
+ let(:references) { (0..references_limit).map { |n| "##{n}" } }
+ let(:error_msg) do
+ "Number of references exceeds the limit. " \
+ "Please provide no more than #{references_limit} references at the same time."
+ end
+
+ it 'returns an error message' do
+ post_graphql(query, current_user: current_user)
+
+ expect_graphql_errors_to_include(error_msg)
+ end
+ end
+
+ def query(namespace_path: project.full_path, refs: references)
+ fields = <<~GRAPHQL
+ nodes {
+ #{all_graphql_fields_for('WorkItem', max_depth: 2)}
+ }
+ GRAPHQL
+
+ graphql_query_for('workItemsByReference', { contextNamespacePath: namespace_path, refs: refs }, fields)
+ end
+end
diff --git a/spec/support/rspec_run_time.rb b/spec/support/rspec_run_time.rb
index 71e2178f4db..875bdc0852e 100644
--- a/spec/support/rspec_run_time.rb
+++ b/spec/support/rspec_run_time.rb
@@ -30,7 +30,8 @@ module Support
duration = time_now - @start_time
elapsed_time = time_now - @rspec_test_suite_start_time
- output.puts "\n# Example group #{notification.group.description} took #{readable_duration(duration)}."
+ output.puts "\n# Example group #{notification.group.description} " \
+ "(#{notification.group.metadata[:file_path]}) took #{readable_duration(duration)}."
output.puts "# RSpec elapsed time: #{readable_duration(elapsed_time)}.\n\n"
end
end
diff --git a/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb b/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb
index 257ccc553fe..6ab41d87f44 100644
--- a/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb
+++ b/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb
@@ -43,6 +43,7 @@ RSpec.shared_context 'with FOSS query type fields' do
:user,
:users,
:work_item,
+ :work_items_by_reference,
:audit_event_definitions,
:abuse_report,
:abuse_report_labels
diff --git a/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb b/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
index e5a3975c0a4..cb3cd81f5ce 100644
--- a/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
+++ b/spec/support/shared_examples/workers/update_repository_move_shared_examples.rb
@@ -96,18 +96,6 @@ RSpec.shared_examples 'an update storage move worker' do
expect(repository_storage_move.reload).to be_failed
end
end
-
- context 'when feature flag "use_lock_for_update_repository_storage" is disabled' do
- before do
- stub_feature_flags(use_lock_for_update_repository_storage: false)
- end
-
- it 'ignores lock and calls the update repository storage service' do
- expect(service).to receive(:execute)
-
- subject
- end
- end
end
end
end
@@ -172,18 +160,6 @@ RSpec.shared_examples 'an update storage move worker' do
expect(repository_storage_move.reload).to be_failed
end
end
-
- context 'when feature flag "use_lock_for_update_repository_storage" is disabled' do
- before do
- stub_feature_flags(use_lock_for_update_repository_storage: false)
- end
-
- it 'ignores lock and calls the update repository storage service' do
- expect(service).to receive(:execute)
-
- subject
- end
- end
end
end
end
diff --git a/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb b/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb
index 62de742e125..27869974b39 100644
--- a/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb
+++ b/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb
@@ -148,6 +148,12 @@ RSpec.describe Tooling::ParallelRSpecRunner, feature_category: :tooling do # rub
Parsing expected rspec suite duration...
03_spec.rb not found in master report
RSpec suite is expected to take 1 minute 5 seconds.
+ Expected duration for tests:
+
+ {
+ "01_spec.rb": 65
+ }
+
Running command: bundle exec rspec -- 01_spec.rb 03_spec.rb
MARKDOWN
diff --git a/tooling/lib/tooling/parallel_rspec_runner.rb b/tooling/lib/tooling/parallel_rspec_runner.rb
index 22d84603318..ba4bdb9df41 100644
--- a/tooling/lib/tooling/parallel_rspec_runner.rb
+++ b/tooling/lib/tooling/parallel_rspec_runner.rb
@@ -90,6 +90,9 @@ module Tooling
knapsack_dir = File.dirname(ENV['KNAPSACK_RSPEC_SUITE_REPORT_PATH'])
FileUtils.mkdir_p(knapsack_dir)
File.write(File.join(knapsack_dir, 'node_specs_expected_duration.json'), JSON.dump(expected_duration_report))
+
+ Knapsack.logger.info "Expected duration for tests:\n\n"
+ Knapsack.logger.info "#{JSON.pretty_generate(expected_duration_report)}\n\n"
end
if node_tests.empty?