Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/graphql/resolvers/ml/model_detail_resolver.rb27
-rw-r--r--app/graphql/types/ml/model_type.rb19
-rw-r--r--app/graphql/types/ml/model_version_links_type.rb17
-rw-r--r--app/graphql/types/ml/model_version_type.rb22
-rw-r--r--app/graphql/types/query_type.rb6
-rw-r--r--app/views/groups/settings/_permissions.html.haml1
-rw-r--r--config/feature_flags/development/approval_rules_disable_joins.yml8
-rw-r--r--config/feature_flags/development/duo_chat_absolute_doc_links.yml8
-rw-r--r--config/feature_flags/development/enforce_ssh_certificates_via_settings.yml8
-rw-r--r--db/migrate/20231109165512_add_enforce_ssh_certificates_to_namespace_settings.rb11
-rw-r--r--db/schema_migrations/202311091655121
-rw-r--r--db/structure.sql1
-rw-r--r--doc/api/graphql/reference/index.md100
-rw-r--r--lib/banzai/filter/absolute_link_filter.rb14
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/graphql/resolvers/ml/model_detail_resolver_spec.rb41
-rw-r--r--spec/graphql/types/ml/model_type_spec.rb13
-rw-r--r--spec/graphql/types/ml/model_version_links_type_spec.rb11
-rw-r--r--spec/graphql/types/ml/model_version_type_spec.rb13
-rw-r--r--spec/graphql/types/query_type_spec.rb10
20 files changed, 329 insertions, 5 deletions
diff --git a/app/graphql/resolvers/ml/model_detail_resolver.rb b/app/graphql/resolvers/ml/model_detail_resolver.rb
new file mode 100644
index 00000000000..01c025c1d8a
--- /dev/null
+++ b/app/graphql/resolvers/ml/model_detail_resolver.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ml
+ class ModelDetailResolver < Resolvers::BaseResolver
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+
+ type ::Types::Ml::ModelType, null: true
+
+ argument :id, ::Types::GlobalIDType[::Ml::Model],
+ required: true,
+ description: 'ID of the model.'
+
+ def resolve(id:)
+ Gitlab::Graphql::Lazy.with_value(find_object(id: id)) do |ml_model|
+ ml_model if current_user.can?(:read_model_registry, ml_model&.project)
+ end
+ end
+
+ private
+
+ def find_object(id:)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ml/model_type.rb b/app/graphql/types/ml/model_type.rb
new file mode 100644
index 00000000000..2d686b185dd
--- /dev/null
+++ b/app/graphql/types/ml/model_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module Ml
+ # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver
+ class ModelType < ::Types::BaseObject
+ graphql_name 'MlModel'
+ description 'Machine learning model in the model registry'
+
+ 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 :versions, ::Types::Ml::ModelVersionType.connection_type, null: true,
+ description: 'Versions of the model.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ml/model_version_links_type.rb b/app/graphql/types/ml/model_version_links_type.rb
new file mode 100644
index 00000000000..142f62bfad2
--- /dev/null
+++ b/app/graphql/types/ml/model_version_links_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module Ml
+ # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver
+ class ModelVersionLinksType < BaseObject
+ graphql_name 'MLModelVersionLinks'
+ description 'Represents links to perform actions on the model version'
+
+ present_using ::Ml::ModelVersionPresenter
+
+ field :show_path, GraphQL::Types::String,
+ null: true, description: 'Path to the details page of the model version.', method: :path
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ml/model_version_type.rb b/app/graphql/types/ml/model_version_type.rb
new file mode 100644
index 00000000000..15c36a7a0d8
--- /dev/null
+++ b/app/graphql/types/ml/model_version_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Ml
+ # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver
+ class ModelVersionType < ::Types::BaseObject
+ graphql_name 'MlModelVersion'
+ description 'Version of a machine learning model'
+
+ connection_type_class Types::LimitedCountableConnectionType
+
+ field :id, ::Types::GlobalIDType[::Ml::ModelVersion], null: false, description: 'ID of the model version.'
+
+ field :created_at, Types::TimeType, null: false, description: 'Date of creation.'
+ field :version, ::GraphQL::Types::String, null: false, description: 'Name of the version.'
+
+ field :_links, ::Types::Ml::ModelVersionLinksType, null: false, method: :itself,
+ description: 'Map of links to perform actions on the model version.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 173e877d86c..6b7814754a4 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -212,6 +212,12 @@ module Types
description: 'Abuse report labels.',
resolver: Resolvers::AbuseReportLabelsResolver
+ field :ml_model, ::Types::Ml::ModelType,
+ null: true,
+ alpha: { milestone: '16.7' },
+ description: 'Find machine learning models.',
+ resolver: Resolvers::Ml::ModelDetailResolver
+
def design_management
DesignManagementObject.new(nil)
end
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index 8ea80700340..b1fa63dbf56 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -44,6 +44,7 @@
= render 'groups/settings/subgroup_creation_level', f: f, group: @group
= render_if_exists 'groups/settings/prevent_forking', f: f, group: @group
= render_if_exists 'groups/settings/service_access_tokens_expiration_enforced', f: f, group: @group
+ = render_if_exists 'groups/settings/enforce_ssh_certificates', f: f, group: @group
= render 'groups/settings/two_factor_auth', f: f, group: @group
= render_if_exists 'groups/personal_access_token_expiration_policy', f: f, group: @group
= render 'groups/settings/membership', f: f, group: @group
diff --git a/config/feature_flags/development/approval_rules_disable_joins.yml b/config/feature_flags/development/approval_rules_disable_joins.yml
new file mode 100644
index 00000000000..1ee33b45ba7
--- /dev/null
+++ b/config/feature_flags/development/approval_rules_disable_joins.yml
@@ -0,0 +1,8 @@
+---
+name: approval_rules_disable_joins
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136588
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431564
+milestone: '16.7'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/duo_chat_absolute_doc_links.yml b/config/feature_flags/development/duo_chat_absolute_doc_links.yml
new file mode 100644
index 00000000000..56c894beb78
--- /dev/null
+++ b/config/feature_flags/development/duo_chat_absolute_doc_links.yml
@@ -0,0 +1,8 @@
+---
+name: duo_chat_absolute_doc_links
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136240'
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431338
+milestone: '16.6'
+type: development
+group: group::ai framework
+default_enabled: false
diff --git a/config/feature_flags/development/enforce_ssh_certificates_via_settings.yml b/config/feature_flags/development/enforce_ssh_certificates_via_settings.yml
new file mode 100644
index 00000000000..e721c2afe8c
--- /dev/null
+++ b/config/feature_flags/development/enforce_ssh_certificates_via_settings.yml
@@ -0,0 +1,8 @@
+---
+name: enforce_ssh_certificates_via_settings
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136498
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/426235
+milestone: '16.7'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/db/migrate/20231109165512_add_enforce_ssh_certificates_to_namespace_settings.rb b/db/migrate/20231109165512_add_enforce_ssh_certificates_to_namespace_settings.rb
new file mode 100644
index 00000000000..98c4de0cb6a
--- /dev/null
+++ b/db/migrate/20231109165512_add_enforce_ssh_certificates_to_namespace_settings.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddEnforceSshCertificatesToNamespaceSettings < Gitlab::Database::Migration[2.2]
+ enable_lock_retries!
+
+ milestone '16.7'
+
+ def change
+ add_column :namespace_settings, :enforce_ssh_certificates, :boolean, default: false, null: false
+ end
+end
diff --git a/db/schema_migrations/20231109165512 b/db/schema_migrations/20231109165512
new file mode 100644
index 00000000000..1e3a229c9d1
--- /dev/null
+++ b/db/schema_migrations/20231109165512
@@ -0,0 +1 @@
+2d3abd070d856db04eea298bbbe82681ca01912e19f978de876fce68ed2ada26 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index e93ca31dd51..d803e1551ef 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -19463,6 +19463,7 @@ CREATE TABLE namespace_settings (
service_access_tokens_expiration_enforced boolean DEFAULT true NOT NULL,
product_analytics_enabled boolean DEFAULT false NOT NULL,
allow_merge_without_pipeline boolean DEFAULT false NOT NULL,
+ enforce_ssh_certificates boolean DEFAULT false NOT NULL,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)),
CONSTRAINT namespace_settings_unique_project_download_limit_alertlist_size CHECK ((cardinality(unique_project_download_limit_alertlist) <= 100)),
CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100))
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 5649b65c598..51b68078824 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -584,6 +584,22 @@ Returns [`Milestone`](#milestone).
| ---- | ---- | ----------- |
| <a id="querymilestoneid"></a>`id` | [`MilestoneID!`](#milestoneid) | Find a milestone by its ID. |
+### `Query.mlModel`
+
+Find machine learning models.
+
+WARNING:
+**Introduced** in 16.7.
+This feature is an Experiment. It can be changed or removed at any time.
+
+Returns [`MlModel`](#mlmodel).
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="querymlmodelid"></a>`id` | [`MlModelID!`](#mlmodelid) | ID of the model. |
+
### `Query.namespace`
Find a namespace.
@@ -11364,6 +11380,43 @@ The edge type for [`Milestone`](#milestone).
| <a id="milestoneedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="milestoneedgenode"></a>`node` | [`Milestone`](#milestone) | The item at the end of the edge. |
+#### `MlModelVersionConnection`
+
+The connection type for [`MlModelVersion`](#mlmodelversion).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mlmodelversionconnectionedges"></a>`edges` | [`[MlModelVersionEdge]`](#mlmodelversionedge) | A list of edges. |
+| <a id="mlmodelversionconnectionnodes"></a>`nodes` | [`[MlModelVersion]`](#mlmodelversion) | A list of nodes. |
+| <a id="mlmodelversionconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+##### Fields with arguments
+
+###### `MlModelVersionConnection.count`
+
+Limited count of collection. Returns limit + 1 for counts greater than the limit.
+
+Returns [`Int!`](#int).
+
+####### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mlmodelversionconnectioncountlimit"></a>`limit` | [`Int`](#int) | Limit value to be applied to the count query. Default is 1000. |
+
+#### `MlModelVersionEdge`
+
+The edge type for [`MlModelVersion`](#mlmodelversion).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mlmodelversionedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="mlmodelversionedgenode"></a>`node` | [`MlModelVersion`](#mlmodelversion) | The item at the end of the edge. |
+
#### `NamespaceCommitEmailConnection`
The connection type for [`NamespaceCommitEmail`](#namespacecommitemail).
@@ -20332,6 +20385,16 @@ Represents an entry from the Cloud License history.
| <a id="locationblobpath"></a>`blobPath` | [`String`](#string) | HTTP URI path to view the input file in GitLab. |
| <a id="locationpath"></a>`path` | [`String`](#string) | Path, relative to the root of the repository, of the filewhich was analyzed to detect the dependency. |
+### `MLModelVersionLinks`
+
+Represents links to perform actions on the model version.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mlmodelversionlinksshowpath"></a>`showPath` | [`String`](#string) | Path to the details page of the model version. |
+
### `MavenMetadata`
Maven metadata.
@@ -21911,6 +21974,31 @@ Contains statistics about a milestone.
| <a id="milestonestatsclosedissuescount"></a>`closedIssuesCount` | [`Int`](#int) | Number of closed issues associated with the milestone. |
| <a id="milestonestatstotalissuescount"></a>`totalIssuesCount` | [`Int`](#int) | Total number of issues associated with the milestone. |
+### `MlModel`
+
+Machine learning model in the model registry.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mlmodelid"></a>`id` | [`MlModelID!`](#mlmodelid) | ID of the model. |
+| <a id="mlmodelname"></a>`name` | [`String!`](#string) | Name of the model. |
+| <a id="mlmodelversions"></a>`versions` | [`MlModelVersionConnection`](#mlmodelversionconnection) | Versions of the model. (see [Connections](#connections)) |
+
+### `MlModelVersion`
+
+Version of a machine learning model.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mlmodelversion_links"></a>`_links` | [`MLModelVersionLinks!`](#mlmodelversionlinks) | Map of links to perform actions on the model version. |
+| <a id="mlmodelversioncreatedat"></a>`createdAt` | [`Time!`](#time) | Date of creation. |
+| <a id="mlmodelversionid"></a>`id` | [`MlModelVersionID!`](#mlmodelversionid) | ID of the model version. |
+| <a id="mlmodelversionversion"></a>`version` | [`String!`](#string) | Name of the version. |
+
### `Namespace`
#### Fields
@@ -31232,6 +31320,18 @@ A `MilestoneID` is a global ID. It is encoded as a string.
An example `MilestoneID` is: `"gid://gitlab/Milestone/1"`.
+### `MlModelID`
+
+A `MlModelID` is a global ID. It is encoded as a string.
+
+An example `MlModelID` is: `"gid://gitlab/Ml::Model/1"`.
+
+### `MlModelVersionID`
+
+A `MlModelVersionID` is a global ID. It is encoded as a string.
+
+An example `MlModelVersionID` is: `"gid://gitlab/Ml::ModelVersion/1"`.
+
### `NamespaceID`
A `NamespaceID` is a global ID. It is encoded as a string.
diff --git a/lib/banzai/filter/absolute_link_filter.rb b/lib/banzai/filter/absolute_link_filter.rb
index cc7bf3ed556..7e3024c521c 100644
--- a/lib/banzai/filter/absolute_link_filter.rb
+++ b/lib/banzai/filter/absolute_link_filter.rb
@@ -6,13 +6,13 @@ module Banzai
module Filter
# HTML filter that converts relative urls into absolute ones.
class AbsoluteLinkFilter < HTML::Pipeline::Filter
- CSS = 'a.gfm'
+ CSS = 'a.gfm'
XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze
def call
- return doc unless context[:only_path] == false
+ return doc if skip?
- doc.xpath(XPATH).each do |el|
+ doc.xpath(self.class::XPATH).each do |el|
process_link_attr el.attribute('href')
end
@@ -21,17 +21,21 @@ module Banzai
protected
+ def skip?
+ context[:only_path] != false
+ end
+
def process_link_attr(html_attr)
return if html_attr.blank?
return if html_attr.value.start_with?('//')
uri = URI(html_attr.value)
- html_attr.value = absolute_link_attr(uri) if uri.relative?
+ html_attr.value = convert_link_href(uri) if uri.relative?
rescue URI::Error
# noop
end
- def absolute_link_attr(uri)
+ def convert_link_href(uri)
# Here we really want to expand relative path to absolute path
URI.join(Gitlab.config.gitlab.url, uri).to_s
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d7e4dbdbae6..1ed622246ed 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -23284,6 +23284,9 @@ msgstr ""
msgid "GroupSettings|Enabling these features is your acceptance of the %{link_start}GitLab Testing Agreement%{link_end}."
msgstr ""
+msgid "GroupSettings|Enforce SSH Certificates"
+msgstr ""
+
msgid "GroupSettings|Experiment and Beta features"
msgstr ""
diff --git a/spec/graphql/resolvers/ml/model_detail_resolver_spec.rb b/spec/graphql/resolvers/ml/model_detail_resolver_spec.rb
new file mode 100644
index 00000000000..1da208eb4d8
--- /dev/null
+++ b/spec/graphql/resolvers/ml/model_detail_resolver_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Ml::ModelDetailResolver, feature_category: :mlops do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:model) { create(:ml_models, project: project) }
+ let_it_be(:user) { project.owner }
+
+ let(:args) { { id: global_id_of(model) } }
+ 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 { force(resolve(described_class, ctx: { current_user: user }, args: args)) }
+
+ context 'when user is allowed and model exists' do
+ it { is_expected.to eq(model) }
+ end
+
+ context 'when user does not have permission' do
+ let(:read_model_registry) { false }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when model does not exist' do
+ let(:args) { { id: global_id_of(id: non_existing_record_id, model_name: 'Ml::Model') } }
+
+ it { is_expected.to be_nil }
+ end
+ end
+end
diff --git a/spec/graphql/types/ml/model_type_spec.rb b/spec/graphql/types/ml/model_type_spec.rb
new file mode 100644
index 00000000000..9320b251dd4
--- /dev/null
+++ b/spec/graphql/types/ml/model_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+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]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+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
new file mode 100644
index 00000000000..d2a11643c35
--- /dev/null
+++ b/spec/graphql/types/ml/model_version_links_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['MLModelVersionLinks'], 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_version_type_spec.rb b/spec/graphql/types/ml/model_version_type_spec.rb
new file mode 100644
index 00000000000..03652c55e20
--- /dev/null
+++ b/spec/graphql/types/ml/model_version_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['MlModelVersion'], feature_category: :mlops do
+ specify { expect(described_class.description).to eq('Version of a machine learning model') }
+
+ it 'includes all the package fields' do
+ expected_fields = %w[id version created_at _links]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb
index 8bda738751d..f2a63fbeb57 100644
--- a/spec/graphql/types/query_type_spec.rb
+++ b/spec/graphql/types/query_type_spec.rb
@@ -137,4 +137,14 @@ RSpec.describe GitlabSchema.types['Query'], feature_category: :shared do
is_expected.to have_graphql_resolver(Resolvers::BoardListResolver)
end
end
+
+ describe 'mlModel field' do
+ subject { described_class.fields['mlModel'] }
+
+ it 'returns metadata', :aggregate_failures do
+ is_expected.to have_graphql_type(Types::Ml::ModelType)
+ is_expected.to have_graphql_arguments(:id)
+ is_expected.to have_graphql_resolver(Resolvers::Ml::ModelDetailResolver)
+ end
+ end
end