diff --git a/app/components/pajamas/avatar_component.rb b/app/components/pajamas/avatar_component.rb index 423934b6887..0821818103a 100644 --- a/app/components/pajamas/avatar_component.rb +++ b/app/components/pajamas/avatar_component.rb @@ -1,16 +1,21 @@ # frozen_string_literal: true module Pajamas + AvatarEmail = Struct.new(:email) do + def name + email + end + end class AvatarComponent < Pajamas::Component include Gitlab::Utils::StrongMemoize - # @param record [User, Project, Group] + # @param item [User, Project, Group, AvatarEmail] # @param alt [String] text for the alt tag # @param class [String] custom CSS class(es) # @param size [Integer] size in pixel # @param [Hash] avatar_options - def initialize(record, alt: nil, class: "", size: 64, avatar_options: {}) - @record = record + def initialize(item, alt: nil, class: "", size: 64, avatar_options: {}) + @item = item @alt = alt @class = binding.local_variable_get(:class) @size = filter_attribute(size.to_i, SIZE_OPTIONS, default: 64) @@ -23,11 +28,11 @@ module Pajamas def avatar_classes classes = ["gl-avatar", "gl-avatar-s#{@size}", @class] - classes.push("gl-avatar-circle") if @record.is_a?(User) + classes.push("gl-avatar-circle") if @item.is_a?(User) || @item.is_a?(AvatarEmail) unless src classes.push("gl-avatar-identicon") - classes.push("gl-avatar-identicon-bg#{((@record.id || 0) % 7) + 1}") + classes.push("gl-avatar-identicon-bg#{((@item.id || 0) % 7) + 1}") end classes.join(' ') @@ -35,7 +40,7 @@ module Pajamas def src strong_memoize(:src) do - if @record.is_a?(User) + if @item.is_a?(User) # Users show a gravatar instead of an identicon. Also avatars of # blocked users are only shown if the current_user is an admin. # To not duplicate this logic, we are using existing helpers here. @@ -44,9 +49,11 @@ module Pajamas rescue StandardError nil end - helpers.avatar_icon_for_user(@record, @size, current_user: current_user) - elsif @record.try(:avatar_url) - "#{@record.avatar_url}?width=#{@size}" + helpers.avatar_icon_for_user(@item, @size, current_user: current_user) + elsif @item.is_a?(AvatarEmail) + helpers.avatar_icon_for_email(@item.email, @size) + elsif @item.try(:avatar_url) + "#{@item.avatar_url}?width=#{@size}" end end end @@ -59,11 +66,11 @@ module Pajamas end def alt - @alt || @record.name + @alt || @item.name end def initial - @record.name[0, 1].upcase + @item.name[0, 1].upcase end end end diff --git a/app/graphql/resolvers/ml/find_models_resolver.rb b/app/graphql/resolvers/ml/find_models_resolver.rb new file mode 100644 index 00000000000..b9901100e22 --- /dev/null +++ b/app/graphql/resolvers/ml/find_models_resolver.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Resolvers + module Ml + class FindModelsResolver < Resolvers::BaseResolver + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + + type ::Types::Ml::ModelType.connection_type, null: true + + argument :name, GraphQL::Types::String, + required: false, + description: 'Search for names that include the string.' + + argument :order_by, ::Types::Ml::ModelsOrderByEnum, + required: false, + description: 'Ordering column. Default is created_at.' + + argument :sort, ::Types::SortDirectionEnum, + required: false, + description: 'Ordering column. Default is desc.' + + def resolve(**args) + return unless current_user.can?(:read_model_registry, object) + + find_params = { + name: args[:name], + order_by: args[:order_by].to_s, + sort: args[:sort].to_s + } + + ::Projects::Ml::ModelFinder.new(object, find_params).execute + end + end + end +end diff --git a/app/graphql/types/ml/model_links_type.rb b/app/graphql/types/ml/model_links_type.rb new file mode 100644 index 00000000000..9d18efb2e17 --- /dev/null +++ b/app/graphql/types/ml/model_links_type.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Types + module Ml + # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver + class ModelLinksType < BaseObject + graphql_name 'MLModelLinks' + description 'Represents links to perform actions on the model' + + present_using ::Ml::ModelPresenter + + field :show_path, GraphQL::Types::String, + null: true, description: 'Path to the details page of the model.', method: :path + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/ml/model_type.rb b/app/graphql/types/ml/model_type.rb index ca63918b370..e677b37547a 100644 --- a/app/graphql/types/ml/model_type.rb +++ b/app/graphql/types/ml/model_type.rb @@ -7,10 +7,21 @@ module Types graphql_name 'MlModel' description 'Machine learning model in the model registry' + connection_type_class Types::LimitedCountableConnectionType + field :id, ::Types::GlobalIDType[::Ml::Model], null: false, description: 'ID of the model.' field :name, ::GraphQL::Types::String, null: false, description: 'Name of the model.' + field :created_at, Types::TimeType, null: false, description: 'Date of creation.' + + field :latest_version, ::Types::Ml::ModelVersionType, null: true, description: 'Latest version of the model.' + + field :version_count, ::GraphQL::Types::Int, null: true, description: 'Count of versions in the model.' + + field :_links, ::Types::Ml::ModelLinksType, null: false, method: :itself, + description: 'Map of links to perform actions on the model.' + field :versions, ::Types::Ml::ModelVersionType.connection_type, null: true, description: 'Versions of the model.' diff --git a/app/graphql/types/ml/model_version_links_type.rb b/app/graphql/types/ml/model_version_links_type.rb index 142f62bfad2..a8497334fc6 100644 --- a/app/graphql/types/ml/model_version_links_type.rb +++ b/app/graphql/types/ml/model_version_links_type.rb @@ -11,6 +11,9 @@ module Types field :show_path, GraphQL::Types::String, null: true, description: 'Path to the details page of the model version.', method: :path + + field :package_path, GraphQL::Types::String, + null: true, description: 'Path to the package of the model version.', method: :package_path end # rubocop: enable Graphql/AuthorizeTypes end diff --git a/app/graphql/types/ml/models_order_by_enum.rb b/app/graphql/types/ml/models_order_by_enum.rb new file mode 100644 index 00000000000..db96a2e2d7d --- /dev/null +++ b/app/graphql/types/ml/models_order_by_enum.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Ml + class ModelsOrderByEnum < BaseEnum + graphql_name 'MlModelsOrderBy' + description 'Values for ordering machine learning models by a specific field' + + value 'NAME', 'Ordered by name.', value: :name + value 'CREATED_AT', 'Ordered by creation time.', value: :created_at + value 'UPDATED_AT', 'Ordered by update time.', value: :updated_at + value 'ID', 'Ordered by id.', value: :id + end + end +end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 8e84605cb05..68a55687419 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -663,6 +663,12 @@ module Types null: true, resolver: Resolvers::Analytics::CycleAnalytics::ValueStreamsResolver + field :ml_models, ::Types::Ml::ModelType.connection_type, + null: true, + alpha: { milestone: '16.8' }, + description: 'Finds machine learning models', + resolver: Resolvers::Ml::FindModelsResolver + def timelog_categories object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories) end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index bf21eca8857..2471c0bdb29 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -2194,6 +2194,8 @@ class MergeRequest < ApplicationRecord attr_accessor :skip_fetch_ref def merge_base_pipelines + return ::Ci::Pipeline.none unless actual_head_pipeline + target_branch_pipelines_for(sha: actual_head_pipeline.target_sha) end diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index c86993f5b77..47ab2d5f4be 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -49,7 +49,7 @@ = _("Expires %{preposition} %{expires_at}").html_safe % { expires_at: time_ago_with_tooltip(member.expires_at), preposition: preposition } - else - = image_tag avatar_icon_for_email(member.invite_email, 40), class: "avatar s40 flex-shrink-0 flex-grow-0", alt: '' + = render Pajamas::AvatarComponent.new(Pajamas::AvatarEmail.new(member.invite_email), size: 32, class: 'gl-mr-3 flex-shrink-0 flex-grow-0') .user-info .member= member.invite_email .cgray diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 3701abbc237..ee44d76a773 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -11801,6 +11801,43 @@ The edge type for [`MlCandidate`](#mlcandidate). | `cursor` | [`String!`](#string) | A cursor for use in pagination. | | `node` | [`MlCandidate`](#mlcandidate) | The item at the end of the edge. | +#### `MlModelConnection` + +The connection type for [`MlModel`](#mlmodel). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `edges` | [`[MlModelEdge]`](#mlmodeledge) | A list of edges. | +| `nodes` | [`[MlModel]`](#mlmodel) | A list of nodes. | +| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +##### Fields with arguments + +###### `MlModelConnection.count` + +Limited count of collection. Returns limit + 1 for counts greater than the limit. + +Returns [`Int!`](#int). + +####### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `limit` | [`Int`](#int) | Limit value to be applied to the count query. Default is 1000. | + +#### `MlModelEdge` + +The edge type for [`MlModel`](#mlmodel). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `cursor` | [`String!`](#string) | A cursor for use in pagination. | +| `node` | [`MlModel`](#mlmodel) | The item at the end of the edge. | + #### `MlModelVersionConnection` The connection type for [`MlModelVersion`](#mlmodelversion). @@ -21288,6 +21325,16 @@ Represents links to perform actions on the candidate. | `artifactPath` | [`String`](#string) | Path to the artifact. | | `showPath` | [`String`](#string) | Path to the details page of the candidate. | +### `MLModelLinks` + +Represents links to perform actions on the model. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `showPath` | [`String`](#string) | Path to the details page of the model. | + ### `MLModelVersionLinks` Represents links to perform actions on the model version. @@ -21296,6 +21343,7 @@ Represents links to perform actions on the model version. | Name | Type | Description | | ---- | ---- | ----------- | +| `packagePath` | [`String`](#string) | Path to the package of the model version. | | `showPath` | [`String`](#string) | Path to the details page of the model version. | ### `MavenMetadata` @@ -22923,9 +22971,13 @@ Machine learning model in the model registry. | Name | Type | Description | | ---- | ---- | ----------- | +| `_links` | [`MLModelLinks!`](#mlmodellinks) | Map of links to perform actions on the model. | | `candidates` | [`MlCandidateConnection`](#mlcandidateconnection) | Version candidates of the model. (see [Connections](#connections)) | +| `createdAt` | [`Time!`](#time) | Date of creation. | | `id` | [`MlModelID!`](#mlmodelid) | ID of the model. | +| `latestVersion` | [`MlModelVersion`](#mlmodelversion) | Latest version of the model. | | `name` | [`String!`](#string) | Name of the model. | +| `versionCount` | [`Int`](#int) | Count of versions in the model. | | `versions` | [`MlModelVersionConnection`](#mlmodelversionconnection) | Versions of the model. (see [Connections](#connections)) | ### `MlModelVersion` @@ -25103,6 +25155,28 @@ four standard [pagination arguments](#connection-pagination-arguments): | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `title` | [`String`](#string) | Title of the milestone. | +##### `Project.mlModels` + +Finds machine learning models. + +WARNING: +**Introduced** in 16.8. +This feature is an Experiment. It can be changed or removed at any time. + +Returns [`MlModelConnection`](#mlmodelconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, and `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `name` | [`String`](#string) | Search for names that include the string. | +| `orderBy` | [`MlModelsOrderBy`](#mlmodelsorderby) | Ordering column. Default is created_at. | +| `sort` | [`SortDirectionEnum`](#sortdirectionenum) | Ordering column. Default is desc. | + ##### `Project.nestedEnvironments` Environments for this project with nested folders, can only be resolved for one project in any single request. @@ -30820,6 +30894,17 @@ Milestone ID wildcard values. | `STARTED` | Milestone assigned is open and started (start date <= today). | | `UPCOMING` | Milestone assigned is due in the future (due date > today). | +### `MlModelsOrderBy` + +Values for ordering machine learning models by a specific field. + +| Value | Description | +| ----- | ----------- | +| `CREATED_AT` | Ordered by creation time. | +| `ID` | Ordered by id. | +| `NAME` | Ordered by name. | +| `UPDATED_AT` | Ordered by update time. | + ### `MoveType` The position to which the adjacent object should be moved. diff --git a/doc/development/code_suggestions/index.md b/doc/development/code_suggestions/index.md index 409efc41fc2..f282dbb1223 100644 --- a/doc/development/code_suggestions/index.md +++ b/doc/development/code_suggestions/index.md @@ -52,5 +52,5 @@ with the deployed staging Model Gateway. To do this: ``` 1. Restart the GDK. -1. Ensure you followed the necessary [steps to enable the Code Suggestions feature](../../user/project/repository/code_suggestions/self_managed.md#gitlab-163-and-later). +1. Ensure you followed the necessary [steps to enable the Code Suggestions feature](../../user/project/repository/code_suggestions/self_managed.md). 1. Test out the Code Suggestions feature by opening the Web IDE for a project. diff --git a/doc/user/analytics/analytics_dashboards.md b/doc/user/analytics/analytics_dashboards.md index 0699be4e0ad..76acba44682 100644 --- a/doc/user/analytics/analytics_dashboards.md +++ b/doc/user/analytics/analytics_dashboards.md @@ -226,7 +226,7 @@ You can define different charts, and add visualization options to some of them: - Line chart, with the options listed in the [ECharts documentation](https://echarts.apache.org/en/option.html). - Column chart, with the options listed in the [ECharts documentation](https://echarts.apache.org/en/option.html). -- Data table, with the only option to render `links` (array of objects, each with `text` and `href` properties to specify the dimensions to be used in links). See [example](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/validators/json_schemas/analytics_visualization.json?ref_type=heads#L112)). +- Data table. - Single stat, with the only option to set `decimalPlaces` (number, default value is 0). To define a chart for your dashboards: diff --git a/doc/user/application_security/vulnerability_report/index.md b/doc/user/application_security/vulnerability_report/index.md index 23454bf387a..c5806ed6124 100644 --- a/doc/user/application_security/vulnerability_report/index.md +++ b/doc/user/application_security/vulnerability_report/index.md @@ -133,6 +133,10 @@ The content of the Project filter depends on the current level: ### Activity filter +> Introduced in GitLab 16.7 [with a flag](../../../administration/feature_flags.md) named `activity_filter_has_remediations`. Disabled by default. + +FLAG: +On self-managed GitLab, by default the Solution Available filter is not available. To make it available, an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `activity_filter_has_remediations`. On GitLab.com, this feature is not available. This feature is not ready for production use. The activity filter behaves differently from the other filters. You can select only one value in each category. To remove a filter, from the activity filter dropdown list select the filter you want to remove. @@ -149,6 +153,9 @@ Selection behavior when using the activity filter: - **Merge request** - **Has merge request**: Vulnerabilities with one or more associated merge requests. - **Does not have merge request**: Vulnerabilities without an associated merge request. +- **Solution available** + - **Has a solution**: Vulnerabilities with an available solution. + - **Does not have a solution**: Vulnerabilities without an available solution. ## View details of a vulnerability diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md index 81f15227c8c..dfcbc8a171d 100644 --- a/doc/user/discussions/index.md +++ b/doc/user/discussions/index.md @@ -163,7 +163,7 @@ To lock an issue or merge request: 1. On the left sidebar, select **Search or go to** and find your project. 1. For merge requests, select **Code > Merge requests**, and find your merge request. 1. For issues, select **Plan > Issues**, and find your issue. -1. On the upper-right corner, select **Merge request actions** or **Issue actions** (**{ellipsis_v}**), then select **Lock discussion**. +1. In the upper-right corner, select **Merge request actions** or **Issue actions** (**{ellipsis_v}**), then select **Lock discussion**. A system note is added to the page details. diff --git a/doc/user/product_analytics/index.md b/doc/user/product_analytics/index.md index 75e44471f92..9c2826855fa 100644 --- a/doc/user/product_analytics/index.md +++ b/doc/user/product_analytics/index.md @@ -150,6 +150,8 @@ The `cube_analytics` data type connects to the Cube instance defined when [produ All filters and queries are sent to the Cube instance and the returned data is processed by the product analytics data source to be rendered by the appropriate visualizations. +Data table visualizations from `cube_analytics` have an additional configuration option for rendering `links` (array of objects, each with `text` and `href` properties to specify the dimensions to be used in links. If `href` contains multiple dimensions, values are joined into a single URL). See [example](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/validators/json_schemas/analytics_visualization.json?ref_type=heads#L112)). + ### Filling missing data - Introduced in GitLab 16.3 behind the [feature flag](../../administration/feature_flags.md) named `product_analytics_dashboards`. Disabled by default. diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md index 6cc5f7c5039..de8ab4b25e9 100644 --- a/doc/user/profile/preferences.md +++ b/doc/user/profile/preferences.md @@ -321,20 +321,14 @@ To access your **Followers** and **Following** tabs: ## Enable Code Suggestions **(FREE SAAS)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121079) in GitLab 16.1 as [Beta](../../policy/experiment-beta-support.md#beta). -> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139916) in GitLab 16.8. Available to a percentage of users. +> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139916) in GitLab 16.8. UI user setting removed. -A percentage of users can use Code Suggestions without any additional configuration. +If Code Suggestions is +[enabled for your top-level group](../group/manage.md#enable-code-suggestions-for-a-group), +you can use Code Suggestions after you +[configure a supported IDE extension](../project/repository/code_suggestions/index.md#supported-editor-extensions). -If the following options are available to you, it means you are **not** part of the percentage of users -and you must manually enable Code Suggestions for your account: - -1. On the left sidebar, select your avatar. -1. Select **Preferences**. -1. Select the **Enable Code Suggestions** checkbox. -1. Select **Save changes**. - -NOTE: -If Code Suggestions are disabled [for any groups that you belong to](../../user/group/manage.md#enable-code-suggestions-for-a-group), then you cannot enable them for yourself. (Your setting has no effect.) +If Code Suggestions are disabled [for all the groups that you belong to](../../user/group/manage.md#enable-code-suggestions-for-a-group), then you cannot enable them for yourself. ## Integrate your GitLab instance with third-party services diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md index 832c1ad63f6..ae7afd35af4 100644 --- a/doc/user/project/issues/managing_issues.md +++ b/doc/user/project/issues/managing_issues.md @@ -355,7 +355,7 @@ To delete an issue: 1. On the left sidebar, select **Search or go to** and find your project. 1. Select **Plan > Issues**, then select your issue to view it. -1. On the upper-right corner, select **Issue actions** (**{ellipsis_v}**). +1. In the upper-right corner, select **Issue actions** (**{ellipsis_v}**). 1. Select **Delete issue**. Alternatively: @@ -377,7 +377,7 @@ To promote an issue to an epic: 1. On the left sidebar, select **Search or go to** and find your project. 1. Select **Plan > Issues**, then select your issue to view it. -1. On the upper-right corner, select **Issue actions** (**{ellipsis_v}**). +1. In the upper-right corner, select **Issue actions** (**{ellipsis_v}**). 1. Select **Promote to epic**. Alternatively, you can use the `/promote` [quick action](../quick_actions.md#issues-merge-requests-and-epics). diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md index 72d482edb1a..eeb5be2fab6 100644 --- a/doc/user/project/repository/branches/index.md +++ b/doc/user/project/repository/branches/index.md @@ -35,7 +35,7 @@ To create a new branch from the GitLab UI: 1. On the left sidebar, select **Search or go to** and find your project. 1. Select **Code > Branches**. -1. On the upper-right corner, select **New branch**. +1. In the upper-right corner, select **New branch**. 1. Enter a **Branch name**. 1. In **Create from**, select the base of your branch: an existing branch, an existing tag, or a commit SHA. @@ -283,7 +283,7 @@ To do this: 1. On the left sidebar, select **Search or go to** and find your project. 1. Select **Code > Branches**. -1. On the upper right corner of the page, select **More** **{ellipsis_v}**. +1. In the upper right corner of the page, select **More** **{ellipsis_v}**. 1. Select **Delete merged branches**. 1. In the dialog, enter the word `delete` to confirm, then select **Delete merged branches**. diff --git a/doc/user/project/repository/code_suggestions/index.md b/doc/user/project/repository/code_suggestions/index.md index 61b6d3724cd..cc036ae00d8 100644 --- a/doc/user/project/repository/code_suggestions/index.md +++ b/doc/user/project/repository/code_suggestions/index.md @@ -147,3 +147,10 @@ Code Suggestions do not prevent you from writing code in your IDE. ## Feedback Let us know about your Code Suggestions experience in [issue 435783](https://gitlab.com/gitlab-org/gitlab/-/issues/435783). + +## Troubleshooting + +### Disable Code Suggestions + +Individual users can disable Code Suggestions by disabling the feature in their +[installed IDE editor extension](index.md#supported-editor-extensions). diff --git a/doc/user/project/repository/code_suggestions/saas.md b/doc/user/project/repository/code_suggestions/saas.md index 52b023445d5..78355ccb11f 100644 --- a/doc/user/project/repository/code_suggestions/saas.md +++ b/doc/user/project/repository/code_suggestions/saas.md @@ -20,7 +20,7 @@ Learn about [data usage when using Code Suggestions](index.md#code-suggestions-d ## Enable Code Suggestions > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121079) in GitLab 16.1 as [Beta](../../../../policy/experiment-beta-support.md#beta). -> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139916) in GitLab 16.8. Available to a percentage of users. +> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139916) in GitLab 16.8. UI user setting removed. A group owner must [enable Code Suggestions for your top-level group](../../../group/manage.md#enable-code-suggestions-for-a-group). @@ -35,16 +35,10 @@ Prerequisites: - You must have configured Code Suggestions in 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-for-a-group). - - [Your own account](../../../profile/preferences.md#enable-code-suggestions), if your - account is not part of the percentage rollout. +- Code Suggestions must be enabled for [the top-level group](../../../group/manage.md#enable-code-suggestions-for-a-group). To use Code Suggestions: -1. Determine if your user account is part of the percentage rollout. See - [Enable Code Suggestions](../../../profile/preferences.md#enable-code-suggestions) - for more information. 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. diff --git a/doc/user/project/repository/code_suggestions/self_managed.md b/doc/user/project/repository/code_suggestions/self_managed.md index 8c3c4aadc71..7bc5ea9346b 100644 --- a/doc/user/project/repository/code_suggestions/self_managed.md +++ b/doc/user/project/repository/code_suggestions/self_managed.md @@ -28,16 +28,19 @@ Learn about [data usage when using Code Suggestions](index.md#code-suggestions-d ## Enable Code Suggestions on self-managed GitLab > - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10653) in GitLab 16.1 as [Beta](../../../../policy/experiment-beta-support.md#beta). -> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139916) in GitLab 16.8. Available to a percentage of users. +> - [Enabled self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139916) in GitLab 16.8. When you enable Code Suggestions for your self-managed instance, you: - Agree to the [GitLab testing agreement](https://about.gitlab.com/handbook/legal/testing-agreement/). - Acknowledge that GitLab sends data from the instance, including personal data, to GitLab.com infrastructure. -How you enable Code Suggestions for your instance differs depending on your version of GitLab. +How you enable Code Suggestions for your instance differs depending on your +version of GitLab. This setting is visible only in self-managed GitLab instances. -### GitLab 16.3 and later **(PREMIUM)** +::Tabs + +:::TabTitle GitLab 16.3 and later **(PREMIUM)** Prerequisites: @@ -53,17 +56,12 @@ To enable Code Suggestions for your self-managed GitLab instance: In GitLab 16.3, you do not need to enter anything into the **Personal access token** field. In GitLab 16.4 and later, there is no **Personal access token** field. 1. Select **Save changes**. - -This setting is visible only in self-managed GitLab instances. - -WARNING: -In GitLab 16.2 and earlier, if you clear the **Turn on Code Suggestions for this instance** checkbox, the users in your instance can still use Code Suggestions for up to one hour, until the issued JSON web token (JWT) expires. - -To make sure Code Suggestions works immediately, you must [manually synchronize your subscription](#manually-synchronize-your-subscription). +1. To make sure Code Suggestions works immediately, you must + [manually synchronize your subscription](#manually-synchronize-your-subscription). The users in your instance can now use Code Suggestions. -### GitLab 16.2 and earlier +:::TabTitle GitLab 16.2 and earlier FLAG: On self-managed GitLab 16.0 and earlier, GitLab Duo Code Suggestions is not available. To use this feature, you must have GitLab 16.1 or later. For optimal performance and full feature access, you should upgrade to GitLab 16.3 or later, which supports cloud licensing. @@ -75,7 +73,7 @@ Prerequisites: - You have a [GitLab SaaS account](https://gitlab.com/users/sign_up). You do not need to have a GitLab SaaS subscription. NOTE: -If you do not have a customer success manager, you cannot participate in the free trial of Code Suggestions on self-managed GitLab. Upgrade to GitLab 16.3 to [perform self-service onboarding](#gitlab-163-and-later). +If you do not have a customer success manager, you cannot participate in the free trial of Code Suggestions on self-managed GitLab. Upgrade to GitLab 16.3 or later to perform self-service onboarding. Then, you will: @@ -83,7 +81,7 @@ Then, you will: 1. Enable Code Suggestions for the instance. 1. [Request early access](#request-access-to-code-suggestions) to the Code Suggestions Beta. -#### Enable Code Suggestions for your SaaS account +### Enable Code Suggestions for your SaaS account To enable Code Suggestions for your GitLab SaaS account: @@ -94,7 +92,7 @@ To enable Code Suggestions for your GitLab SaaS account: 1. In the **Code Suggestions** section, select **Enable Code Suggestions**. 1. Select **Save changes**. -#### Enable Code Suggestions for the instance +### Enable Code Suggestions for the instance To enable Code Suggestions for your self-managed GitLab instance: @@ -110,7 +108,7 @@ This setting is visible only in self-managed GitLab instances. WARNING: If you clear the **Turn on Code Suggestions for this instance** checkbox, the users in your instance can still use Code Suggestions for up to one hour, until the issued JSON web token (JWT) expires. -#### Request access to Code Suggestions +### Request access to Code Suggestions GitLab provisions access on a customer-by-customer basis for Code Suggestions on self-managed instances. To request access, contact your customer success manager. @@ -120,7 +118,7 @@ Your customer success manager then provisions access by commenting on [issue 415 After GitLab has provisioned access to Code Suggestions for your instance, the users in your instance can now enable Code Suggestions. -### Configure network and proxy settings +## Configure network and proxy settings Configure any firewalls to allow outbound connections to `https://codesuggestions.gitlab.com/`. @@ -128,7 +126,7 @@ If your GitLab instance uses an HTTP proxy server to access the internet, ensure the server is configured to allow outbound connections, including the [`gitlab_workhorse` environment variable](https://docs.gitlab.com/omnibus/settings/environment-variables.html). -### Upgrade GitLab +## Upgrade GitLab In GitLab 16.3 and later, GitLab is enforcing the cloud licensing requirement for Code Suggestions: @@ -142,7 +140,7 @@ to continue having early access to Code Suggestions, you must: 1. Make sure you have the latest version of your [IDE extension](index.md#supported-editor-extensions). 1. [Manually synchronize your subscription](#manually-synchronize-your-subscription). -#### Manually synchronize your subscription +### Manually synchronize your subscription You must [manually synchronize your subscription](../../../../subscriptions/self_managed/index.md#manually-synchronize-your-subscription-details) if either: @@ -151,6 +149,8 @@ You must [manually synchronize your subscription](../../../../subscriptions/self Without the manual synchronization, it might take up to 24 hours to active Code Suggestions on your instance. +::EndTabs + ## Use Code Suggestions Prerequisites: diff --git a/doc/user/project/repository/code_suggestions/troubleshooting.md b/doc/user/project/repository/code_suggestions/troubleshooting.md index 22398395c2c..067831d3fa9 100644 --- a/doc/user/project/repository/code_suggestions/troubleshooting.md +++ b/doc/user/project/repository/code_suggestions/troubleshooting.md @@ -14,7 +14,6 @@ If Code Suggestions are not displayed, and you have [installed a supported IDE e In GitLab, ensure Code Suggestions is enabled: -- [For your user account](../../../profile/preferences.md#enable-code-suggestions). - [For **all** top-level groups your account belongs to](../../../group/manage.md#enable-code-suggestions-for-a-group). If you don't have a role that lets you view the top-level group's settings, contact a group owner. ### Code Suggestions not displayed in VS Code or GitLab WebIDE diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a2feeeedf89..172d1f3d9aa 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -51119,6 +51119,16 @@ msgstr "" msgid "Tracing" msgstr "" +msgid "Tracing|%{count} match" +msgid_plural "Tracing|%{count} matches" +msgstr[0] "" +msgstr[1] "" + +msgid "Tracing|%{count} span" +msgid_plural "Tracing|%{count} spans" +msgstr[0] "" +msgstr[1] "" + msgid "Tracing|%{ms} ms" msgstr "" diff --git a/spec/components/pajamas/avatar_component_spec.rb b/spec/components/pajamas/avatar_component_spec.rb index d59ef390fad..9c1a40ad5b5 100644 --- a/spec/components/pajamas/avatar_component_spec.rb +++ b/spec/components/pajamas/avatar_component_spec.rb @@ -5,16 +5,25 @@ RSpec.describe Pajamas::AvatarComponent, type: :component do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:group) { create(:group) } + let_it_be(:email) { Pajamas::AvatarEmail.new('kitty@cat.com') } let(:options) { {} } before do - render_inline(described_class.new(record, **options)) + render_inline(described_class.new(item, **options)) end describe "avatar shape" do context "for a User" do - let(:record) { user } + let(:item) { user } + + it "has a circle shape" do + expect(page).to have_css ".gl-avatar.gl-avatar-circle" + end + end + + context "for an Email" do + let(:item) { email } it "has a circle shape" do expect(page).to have_css ".gl-avatar.gl-avatar-circle" @@ -22,7 +31,7 @@ RSpec.describe Pajamas::AvatarComponent, type: :component do end context "for a Project" do - let(:record) { project } + let(:item) { project } it "has default shape (rect)" do expect(page).to have_css ".gl-avatar" @@ -31,7 +40,7 @@ RSpec.describe Pajamas::AvatarComponent, type: :component do end context "for a Group" do - let(:record) { group } + let(:item) { group } it "has default shape (rect)" do expect(page).to have_css ".gl-avatar" @@ -42,11 +51,11 @@ RSpec.describe Pajamas::AvatarComponent, type: :component do describe "avatar image" do context "when it has an uploaded image" do - let(:record) { project } + let(:item) { project } before do - allow(record).to receive(:avatar_url).and_return "/example.png" - render_inline(described_class.new(record, **options)) + allow(item).to receive(:avatar_url).and_return "/example.png" + render_inline(described_class.new(item, **options)) end it "uses the avatar_url as image src" do @@ -73,14 +82,14 @@ RSpec.describe Pajamas::AvatarComponent, type: :component do end context "when a project or group has no uploaded image" do - let(:record) { project } + let(:item) { project } - it "uses an identicon with the record's initial" do - expect(page).to have_css "div.gl-avatar.gl-avatar-identicon", text: record.name[0].upcase + it "uses an identicon with the item's initial" do + expect(page).to have_css "div.gl-avatar.gl-avatar-identicon", text: item.name[0].upcase end - context "when the record has no id" do - let(:record) { build :group } + context "when the item has no id" do + let(:item) { build :group } it "uses an identicon with default background color" do expect(page).to have_css "div.gl-avatar.gl-avatar-identicon-bg1" @@ -89,16 +98,34 @@ RSpec.describe Pajamas::AvatarComponent, type: :component do end context "when a user has no uploaded image" do - let(:record) { user } + let(:item) { user } it "uses a gravatar" do expect(rendered_content).to match /gravatar\.com/ end end + + context "when an email has no linked user" do + context "when the email is blank" do + let(:item) { Pajamas::AvatarEmail.new('') } + + it "uses the default avatar" do + expect(rendered_content).to match /no_avatar/ + end + end + + context "when the email is not blank" do + let(:item) { email } + + it "uses a agravatar" do + expect(rendered_content).to match /gravatar\.com/ + end + end + end end describe "options" do - let(:record) { user } + let(:item) { user } describe "alt" do context "with a value" do @@ -110,8 +137,8 @@ RSpec.describe Pajamas::AvatarComponent, type: :component do end context "without a value" do - it "uses the record's name as alt text" do - expect(page).to have_css ".gl-avatar[alt='#{record.name}']" + it "uses the item's name as alt text" do + expect(page).to have_css ".gl-avatar[alt='#{item.name}']" end end end diff --git a/spec/graphql/resolvers/ml/find_models_resolver_spec.rb b/spec/graphql/resolvers/ml/find_models_resolver_spec.rb new file mode 100644 index 00000000000..ce85dd62515 --- /dev/null +++ b/spec/graphql/resolvers/ml/find_models_resolver_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Ml::FindModelsResolver, feature_category: :mlops do + include GraphqlHelpers + + describe '#resolve' do + let_it_be(:project) { create(:project) } + let_it_be(:models) { create_list(:ml_models, 2, project: project) } + let_it_be(:model_in_another_project) { create(:ml_models) } + let_it_be(:user) { project.owner } + + let(:args) { { name: 'model', orderBy: 'CREATED_AT', sort: 'desc', invalid: 'blah' } } + let(:read_model_registry) { true } + + before do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?) + .with(user, :read_model_registry, project) + .and_return(read_model_registry) + end + + subject(:resolve_models) do + force(resolve(described_class, obj: project, ctx: { current_user: user }, args: args))&.to_a + end + + context 'when user is allowed and model exists' do + it { is_expected.to eq(models.reverse) } + + it 'only passes name, sort_by and order to finder' do + expect(::Projects::Ml::ModelFinder).to receive(:new) + .with(project, { name: 'model', order_by: 'created_at', +sort: 'desc' }) + .and_call_original + + resolve_models + end + end + + context 'when user does not have permission' do + let(:read_model_registry) { false } + + it { is_expected.to be_nil } + end + end +end diff --git a/spec/graphql/types/ml/model_links_type_spec.rb b/spec/graphql/types/ml/model_links_type_spec.rb new file mode 100644 index 00000000000..e33102c8f7f --- /dev/null +++ b/spec/graphql/types/ml/model_links_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['MLModelLinks'], feature_category: :mlops do + it 'has the expected fields' do + expected_fields = %w[showPath] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/ml/model_type_spec.rb b/spec/graphql/types/ml/model_type_spec.rb index ee0473ccafe..79edea29eb8 100644 --- a/spec/graphql/types/ml/model_type_spec.rb +++ b/spec/graphql/types/ml/model_type_spec.rb @@ -6,7 +6,7 @@ RSpec.describe GitlabSchema.types['MlModel'], feature_category: :mlops do specify { expect(described_class.description).to eq('Machine learning model in the model registry') } it 'includes all the package fields' do - expected_fields = %w[id name versions candidates] + expected_fields = %w[id name versions candidates version_count _links created_at latest_version] expect(described_class).to include_graphql_fields(*expected_fields) end diff --git a/spec/graphql/types/ml/model_version_links_type_spec.rb b/spec/graphql/types/ml/model_version_links_type_spec.rb index d2a11643c35..63083b441e3 100644 --- a/spec/graphql/types/ml/model_version_links_type_spec.rb +++ b/spec/graphql/types/ml/model_version_links_type_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['MLModelVersionLinks'], feature_category: :mlops do it 'has the expected fields' do - expected_fields = %w[showPath] + expected_fields = %w[showPath packagePath] expect(described_class).to include_graphql_fields(*expected_fields) end diff --git a/spec/graphql/types/ml/models_order_by_enum_spec.rb b/spec/graphql/types/ml/models_order_by_enum_spec.rb new file mode 100644 index 00000000000..211e073e8c1 --- /dev/null +++ b/spec/graphql/types/ml/models_order_by_enum_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['MlModelsOrderBy'], feature_category: :mlops do + specify { expect(described_class.graphql_name).to eq('MlModelsOrderBy') } + + it 'exposes all the existing order by types' do + expect(described_class.values.keys).to match_array(%w[CREATED_AT ID UPDATED_AT NAME]) + end +end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 3965312316b..f97ec4514f9 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -41,7 +41,7 @@ RSpec.describe GitlabSchema.types['Project'], feature_category: :groups_and_proj recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables timelog_categories fork_targets branch_rules ci_config_variables pipeline_schedules languages incident_management_timeline_event_tags visible_forks inherited_ci_variables autocomplete_users - ci_cd_settings detailed_import_status value_streams + ci_cd_settings detailed_import_status value_streams ml_models ] expect(described_class).to include_graphql_fields(*expected_fields) @@ -532,6 +532,13 @@ RSpec.describe GitlabSchema.types['Project'], feature_category: :groups_and_proj it { is_expected.to have_graphql_type(Types::IncidentManagement::TimelineEventTagType) } end + describe 'mlModels field' do + subject { described_class.fields['mlModels'] } + + it { is_expected.to have_graphql_type(Types::Ml::ModelType.connection_type) } + it { is_expected.to have_graphql_resolver(Resolvers::Ml::FindModelsResolver) } + end + describe 'agent_configurations' do let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/project/container_repositories_spec.rb b/spec/requests/api/graphql/project/container_repositories_spec.rb index c86d3bdd14c..2307409c383 100644 --- a/spec/requests/api/graphql/project/container_repositories_spec.rb +++ b/spec/requests/api/graphql/project/container_repositories_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'getting container repositories in a project', feature_category: let_it_be(:container_repositories) { [container_repository, container_repositories_delete_scheduled, container_repositories_delete_failed].flatten } let_it_be(:container_expiration_policy) { project.container_expiration_policy } - let(:excluded_fields) { %w[pipeline jobs productAnalyticsState] } + let(:excluded_fields) { %w[pipeline jobs productAnalyticsState mlModels] } let(:container_repositories_fields) do <<~GQL edges { @@ -155,7 +155,7 @@ RSpec.describe 'getting container repositories in a project', feature_category: it_behaves_like 'handling graphql network errors with the container registry' it_behaves_like 'not hitting graphql network errors with the container registry' do - let(:excluded_fields) { %w[pipeline jobs tags tagsCount productAnalyticsState] } + let(:excluded_fields) { %w[pipeline jobs tags tagsCount productAnalyticsState mlModels] } end it 'returns the total count of container repositories' do diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb index 23be9fa5286..96933505838 100644 --- a/spec/requests/api/graphql/project/merge_request_spec.rb +++ b/spec/requests/api/graphql/project/merge_request_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'getting merge request information nested in a project', feature_ # we exclude Project.pipeline because it needs arguments, # codequalityReportsComparer because it is behind a feature flag # and runners because the user is not an admin and therefore has no access - let(:excluded) { %w[jobs pipeline runners codequalityReportsComparer] } + let(:excluded) { %w[jobs pipeline runners codequalityReportsComparer mlModels] } let(:mr_fields) { all_graphql_fields_for('MergeRequest', excluded: excluded) } before do diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index 913316c8622..0cdddeaa84a 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -157,7 +157,7 @@ module LoginHelpers mock_auth_hash(provider, uid, email, response_object: response_object) end - def configure_mock_auth(provider, uid, email, response_object: nil, additional_info: {}, name: 'mockuser') + def configure_mock_auth(provider, uid, email, response_object: nil, additional_info: {}, name: 'mockuser', groups: []) # The mock_auth configuration allows you to set per-provider (or default) # authentication hashes to return during integration testing. @@ -180,7 +180,8 @@ module LoginHelpers name: 'mockuser', email: email, image: 'mock_user_thumbnail_url' - } + }, + 'groups' => groups } ), response_object: response_object @@ -188,9 +189,9 @@ module LoginHelpers }).merge(additional_info) { |_, old_hash, new_hash| old_hash.merge(new_hash) } end - def mock_auth_hash(provider, uid, email, additional_info: {}, response_object: nil, name: 'mockuser') + def mock_auth_hash(provider, uid, email, additional_info: {}, response_object: nil, name: 'mockuser', groups: []) configure_mock_auth( - provider, uid, email, additional_info: additional_info, response_object: response_object, name: name + provider, uid, email, additional_info: additional_info, response_object: response_object, name: name, groups: groups ) original_env_config_omniauth_auth = Rails.application.env_config['omniauth.auth']