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--.markdownlint.yml1
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/super_sidebar/components/flyout_menu.vue6
-rw-r--r--app/finders/projects/ml/model_finder.rb9
-rw-r--r--app/models/ml/model.rb4
-rw-r--r--app/models/ml/model_version.rb3
-rw-r--r--app/models/packages/package.rb2
-rw-r--r--app/presenters/ml/models_index_presenter.rb12
-rw-r--r--app/services/groups/transfer_service.rb32
-rw-r--r--app/services/projects/transfer_service.rb34
-rw-r--r--db/migrate/20230720142334_index_ml_model_versions_on_model_id_and_id.rb15
-rw-r--r--db/migrate/20230725210728_drop_index_ml_model_versions_on_model_id.rb15
-rw-r--r--db/schema_migrations/202307201423341
-rw-r--r--db/schema_migrations/202307252107281
-rw-r--r--db/structure.sql4
-rw-r--r--doc/administration/backup_restore/backup_gitlab.md24
-rw-r--r--doc/administration/backup_restore/backup_large_reference_architectures.md98
-rw-r--r--doc/api/graphql/reference/index.md85
-rw-r--r--doc/architecture/blueprints/cells/cells-feature-snippets.md17
-rw-r--r--doc/update/package/index.md3
-rw-r--r--doc/user/project/repository/code_suggestions.md6
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/factories/ml/model_versions.rb2
-rw-r--r--spec/factories/ml/models.rb12
-rw-r--r--spec/finders/projects/ml/model_finder_spec.rb35
-rw-r--r--spec/models/ml/model_spec.rb29
-rw-r--r--spec/models/ml/model_version_spec.rb42
-rw-r--r--spec/models/packages/package_spec.rb26
-rw-r--r--spec/presenters/ml/models_index_presenter_spec.rb12
-rw-r--r--spec/requests/projects/ml/models_controller_spec.rb8
32 files changed, 444 insertions, 108 deletions
diff --git a/.markdownlint.yml b/.markdownlint.yml
index bf891177ae8..a44c236a7f2 100644
--- a/.markdownlint.yml
+++ b/.markdownlint.yml
@@ -57,6 +57,7 @@ proper-names:
"Git",
"Gitaly",
"GitHub",
+ "gitlab.vim",
"GitLab Geo",
"GitLab Monitor",
"GitLab Operator",
diff --git a/Gemfile b/Gemfile
index f9111ae8356..e2adb62aab5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -182,7 +182,7 @@ gem 'seed-fu', '~> 2.3.7'
gem 'elasticsearch-model', '~> 7.2'
gem 'elasticsearch-rails', '~> 7.2', require: 'elasticsearch/rails/instrumentation'
gem 'elasticsearch-api', '7.13.3'
-gem 'aws-sdk-core', '~> 3.179.0'
+gem 'aws-sdk-core', '~> 3.180.0'
gem 'aws-sdk-cloudformation', '~> 1'
gem 'aws-sdk-s3', '~> 1.132.0'
gem 'faraday_middleware-aws-sigv4', '~>0.3.0'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 3a4dbd9cf68..3175354b3de 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -37,7 +37,7 @@
{"name":"aws-eventstream","version":"1.2.0","platform":"ruby","checksum":"ffa53482c92880b001ff2fb06919b9bb82fd847cbb0fa244985d2ebb6dd0d1df"},
{"name":"aws-partitions","version":"1.761.0","platform":"ruby","checksum":"291e444e1edfc92c5521a6dbdd1236ccc3f122b3520163b2be6ec5b6ef350ef2"},
{"name":"aws-sdk-cloudformation","version":"1.41.0","platform":"ruby","checksum":"31e47539719734413671edf9b1a31f8673fbf9688549f50c41affabbcb1c6b26"},
-{"name":"aws-sdk-core","version":"3.179.0","platform":"ruby","checksum":"5104754c4e7c1c3138d9502c8eabb3c22dee418123b4555ac4a3a905efc6fc30"},
+{"name":"aws-sdk-core","version":"3.180.0","platform":"ruby","checksum":"56cd5f109982ccde02d9639abfd64c4f6c6dda61dda0a87755d0ff35187111bf"},
{"name":"aws-sdk-kms","version":"1.64.0","platform":"ruby","checksum":"40de596c95047bfc6e1aacea24f3df6241aa716b6f7ce08ac4c5f7e3120395ad"},
{"name":"aws-sdk-s3","version":"1.132.0","platform":"ruby","checksum":"eec42306e6df54bacf5045a366c69acac8b02834b745b9562a90708838204b32"},
{"name":"aws-sigv4","version":"1.6.0","platform":"ruby","checksum":"ca9e6a15cd424f1f32b524b9760995331459bc22e67d3daad4fcf0c0084b087d"},
diff --git a/Gemfile.lock b/Gemfile.lock
index d6c6f8c8a7c..a1e8c0057cd 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -254,7 +254,7 @@ GEM
aws-sdk-cloudformation (1.41.0)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sigv4 (~> 1.1)
- aws-sdk-core (3.179.0)
+ aws-sdk-core (3.180.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
@@ -1739,7 +1739,7 @@ DEPENDENCIES
autoprefixer-rails (= 10.2.5.1)
awesome_print
aws-sdk-cloudformation (~> 1)
- aws-sdk-core (~> 3.179.0)
+ aws-sdk-core (~> 3.180.0)
aws-sdk-s3 (~> 1.132.0)
axe-core-rspec
babosa (~> 2.0)
diff --git a/app/assets/javascripts/super_sidebar/components/flyout_menu.vue b/app/assets/javascripts/super_sidebar/components/flyout_menu.vue
index 4f95b140a23..285fa8ee90b 100644
--- a/app/assets/javascripts/super_sidebar/components/flyout_menu.vue
+++ b/app/assets/javascripts/super_sidebar/components/flyout_menu.vue
@@ -1,5 +1,5 @@
<script>
-import { computePosition, autoUpdate, flip } from '@floating-ui/dom';
+import { computePosition, autoUpdate, flip, shift } from '@floating-ui/dom';
import NavItem from './nav_item.vue';
export default {
@@ -22,7 +22,7 @@ export default {
function updatePosition() {
return computePosition(target, flyout, {
- middleware: [flip()],
+ middleware: [flip(), shift()],
placement: 'right-start',
strategy: 'fixed',
}).then(({ x, y }) => {
@@ -44,7 +44,7 @@ export default {
<template>
<div
:id="`${targetId}-flyout`"
- class="gl-fixed gl-pl-3 gl-z-index-9999"
+ class="gl-fixed gl-pl-3 gl-z-index-9999 gl-max-h-full gl-overflow-y-auto"
@mouseover="$emit('mouseover')"
@mouseleave="$emit('mouseleave')"
>
diff --git a/app/finders/projects/ml/model_finder.rb b/app/finders/projects/ml/model_finder.rb
index 9ef5dacb551..99c66f53de7 100644
--- a/app/finders/projects/ml/model_finder.rb
+++ b/app/finders/projects/ml/model_finder.rb
@@ -8,12 +8,9 @@ module Projects
end
def execute
- @project
- .packages
- .installable
- .ml_model
- .order_name_desc_version_desc
- .select_only_first_by_name
+ ::Ml::Model
+ .by_project(@project)
+ .including_latest_version
.limit(100) # This is a temporary limit before we add pagination
end
end
diff --git a/app/models/ml/model.rb b/app/models/ml/model.rb
index f901eb69be4..cc71b15af52 100644
--- a/app/models/ml/model.rb
+++ b/app/models/ml/model.rb
@@ -14,6 +14,10 @@ module Ml
has_one :default_experiment, class_name: 'Ml::Experiment'
belongs_to :project
has_many :versions, class_name: 'Ml::ModelVersion'
+ has_one :latest_version, -> { latest_by_model }, class_name: 'Ml::ModelVersion', inverse_of: :model
+
+ scope :including_latest_version, -> { includes(:latest_version) }
+ scope :by_project, ->(project) { where(project_id: project.id) }
def valid_default_experiment?
return unless default_experiment
diff --git a/app/models/ml/model_version.rb b/app/models/ml/model_version.rb
index 934b924e4af..4bf37e228ab 100644
--- a/app/models/ml/model_version.rb
+++ b/app/models/ml/model_version.rb
@@ -18,6 +18,9 @@ module Ml
delegate :name, to: :model
+ scope :order_by_model_id_id_desc, -> { order('model_id, id DESC') }
+ scope :latest_by_model, -> { order_by_model_id_id_desc.select('DISTINCT ON (model_id) *') }
+
class << self
def find_or_create!(model, version, package)
create_with(package: package).find_or_create_by!(project: model.project, model: model, version: version)
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index 9d0ca1acc74..ee8a26e8f19 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -171,14 +171,12 @@ class Packages::Package < ApplicationRecord
scope :preload_pipelines, -> { preload(pipelines: :user) }
scope :limit_recent, ->(limit) { order_created_desc.limit(limit) }
scope :select_distinct_name, -> { select(:name).distinct }
- scope :select_only_first_by_name, -> { select('DISTINCT ON (name) *') }
# Sorting
scope :order_created, -> { reorder(created_at: :asc) }
scope :order_created_desc, -> { reorder(created_at: :desc) }
scope :order_name, -> { reorder(name: :asc) }
scope :order_name_desc, -> { reorder(name: :desc) }
- scope :order_name_desc_version_desc, -> { reorder(name: :desc, version: :desc) }
scope :order_version, -> { reorder(version: :asc) }
scope :order_version_desc, -> { reorder(version: :desc) }
scope :order_type, -> { reorder(package_type: :asc) }
diff --git a/app/presenters/ml/models_index_presenter.rb b/app/presenters/ml/models_index_presenter.rb
index e2cb8e2d6c1..c61f87fc4af 100644
--- a/app/presenters/ml/models_index_presenter.rb
+++ b/app/presenters/ml/models_index_presenter.rb
@@ -10,12 +10,20 @@ module Ml
data = @models.map do |m|
{
name: m.name,
- version: m.version,
- path: Gitlab::Routing.url_helpers.project_package_path(m.project, m)
+ version: m.latest_version&.version,
+ path: package_path(m)
}
end
Gitlab::Json.generate({ models: data })
end
+
+ private
+
+ def package_path(model)
+ return unless model.latest_version&.package.present?
+
+ Gitlab::Routing.url_helpers.project_package_path(model.project, model.latest_version.package_id)
+ end
end
end
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index 81d4dfddaab..64256e43ce3 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -11,19 +11,51 @@ module Groups
@error = nil
end
+ def log_group_transfer_success(group, new_parent_group)
+ log_transfer(group, new_parent_group, nil)
+ end
+
+ def log_group_transfer_error(group, new_parent_group, error_message)
+ log_transfer(group, new_parent_group, error_message)
+ end
+
def execute(new_parent_group)
@new_parent_group = new_parent_group
ensure_allowed_transfer
proceed_to_transfer
+ log_group_transfer_success(@group, @new_parent_group)
+
rescue TransferError, ActiveRecord::RecordInvalid, Gitlab::UpdatePathError => e
@group.errors.clear
@error = s_("TransferGroup|Transfer failed: %{error_message}") % { error_message: e.message }
+
+ log_group_transfer_error(@group, @new_parent_group, e.message)
+
false
end
private
+ def log_transfer(group, new_namespace, error_message = nil)
+ action = error_message.nil? ? "was" : "was not"
+
+ log_payload = {
+ message: "Group #{action} transferred to a new namespace",
+ group_path: group.full_path,
+ group_id: group.id,
+ new_parent_group_path: new_parent_group&.full_path,
+ new_parent_group_id: new_parent_group&.id,
+ error_message: error_message
+ }
+
+ if error_message.nil?
+ ::Gitlab::AppLogger.info(log_payload)
+ else
+ ::Gitlab::AppLogger.error(log_payload)
+ end
+ end
+
def proceed_to_transfer
old_root_ancestor_id = @group.root_ancestor.id
was_root_group = @group.root?
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 1bae7bde168..642ec37619f 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -13,6 +13,14 @@ module Projects
include Gitlab::ShellAdapter
TransferError = Class.new(StandardError)
+ def log_project_transfer_success(project, new_namespace)
+ log_transfer(project, new_namespace, nil)
+ end
+
+ def log_project_transfer_error(project, new_namespace, error_message)
+ log_transfer(project, new_namespace, error_message)
+ end
+
def execute(new_namespace)
@new_namespace = new_namespace
@@ -36,10 +44,15 @@ module Projects
transfer(project)
+ log_project_transfer_success(project, @new_namespace)
+
true
rescue Projects::TransferService::TransferError => ex
project.reset
project.errors.add(:new_namespace, ex.message)
+
+ log_project_transfer_error(project, @new_namespace, ex.message)
+
false
end
@@ -47,6 +60,27 @@ module Projects
attr_reader :old_path, :new_path, :new_namespace, :old_namespace
+ def log_transfer(project, new_namespace, error_message = nil)
+ action = error_message.nil? ? "was" : "was not"
+
+ log_payload = {
+ message: "Project #{action} transferred to a new namespace",
+ project_id: project.id,
+ project_path: project.full_path,
+ project_namespace: project.namespace.full_path,
+ namespace_id: project.namespace_id,
+ new_namespace_id: new_namespace&.id,
+ new_project_namespace: new_namespace&.full_path,
+ error_message: error_message
+ }
+
+ if error_message.nil?
+ ::Gitlab::AppLogger.info(log_payload)
+ else
+ ::Gitlab::AppLogger.error(log_payload)
+ end
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def transfer(project)
@old_path = project.full_path
diff --git a/db/migrate/20230720142334_index_ml_model_versions_on_model_id_and_id.rb b/db/migrate/20230720142334_index_ml_model_versions_on_model_id_and_id.rb
new file mode 100644
index 00000000000..934d5e1f8ee
--- /dev/null
+++ b/db/migrate/20230720142334_index_ml_model_versions_on_model_id_and_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class IndexMlModelVersionsOnModelIdAndId < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'unique_ml_model_versions_on_model_id_and_id'
+
+ def up
+ add_concurrent_index :ml_model_versions, [:model_id, :id], name: INDEX_NAME, order: { id: :desc }
+ end
+
+ def down
+ remove_concurrent_index_by_name :ml_model_versions, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20230725210728_drop_index_ml_model_versions_on_model_id.rb b/db/migrate/20230725210728_drop_index_ml_model_versions_on_model_id.rb
new file mode 100644
index 00000000000..9598e263488
--- /dev/null
+++ b/db/migrate/20230725210728_drop_index_ml_model_versions_on_model_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class DropIndexMlModelVersionsOnModelId < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_ml_model_versions_on_model_id'
+
+ def up
+ remove_concurrent_index_by_name :ml_model_versions, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :ml_model_versions, :model_id, name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20230720142334 b/db/schema_migrations/20230720142334
new file mode 100644
index 00000000000..d9a15727ec2
--- /dev/null
+++ b/db/schema_migrations/20230720142334
@@ -0,0 +1 @@
+f7f48a276a3ee396daa599d9f6f1d2478b0bbbdf79974ef4813070ba8fee9ef9 \ No newline at end of file
diff --git a/db/schema_migrations/20230725210728 b/db/schema_migrations/20230725210728
new file mode 100644
index 00000000000..97decc29d68
--- /dev/null
+++ b/db/schema_migrations/20230725210728
@@ -0,0 +1 @@
+4e79291e9c775309fe2e843887cc669e3146f19305aaaecf1252f1525ff5e032 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index f7b93fe07bf..d1f21674c21 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -32086,8 +32086,6 @@ CREATE UNIQUE INDEX index_ml_experiments_on_project_id_and_name ON ml_experiment
CREATE INDEX index_ml_experiments_on_user_id ON ml_experiments USING btree (user_id);
-CREATE INDEX index_ml_model_versions_on_model_id ON ml_model_versions USING btree (model_id);
-
CREATE INDEX index_ml_model_versions_on_package_id ON ml_model_versions USING btree (package_id);
CREATE INDEX index_ml_model_versions_on_project_id ON ml_model_versions USING btree (project_id);
@@ -33866,6 +33864,8 @@ CREATE UNIQUE INDEX unique_merge_request_diff_llm_summaries_on_mr_diff_id ON mer
CREATE UNIQUE INDEX unique_merge_request_metrics_by_merge_request_id ON merge_request_metrics USING btree (merge_request_id);
+CREATE INDEX unique_ml_model_versions_on_model_id_and_id ON ml_model_versions USING btree (model_id, id DESC);
+
CREATE UNIQUE INDEX unique_organizations_on_path ON organizations USING btree (path);
CREATE UNIQUE INDEX unique_packages_project_id_and_name_and_version_when_debian ON packages_packages USING btree (project_id, name, version) WHERE ((package_type = 9) AND (status <> 4));
diff --git a/doc/administration/backup_restore/backup_gitlab.md b/doc/administration/backup_restore/backup_gitlab.md
index 24b7b453517..5f396947341 100644
--- a/doc/administration/backup_restore/backup_gitlab.md
+++ b/doc/administration/backup_restore/backup_gitlab.md
@@ -18,7 +18,9 @@ As a rough guideline, if you are using a [1k reference architecture](../referenc
## Scaling backups
-As the volume of GitLab data grows, the [backup command](#backup-command) takes longer to execute. At some point, the execution time becomes impractical. For example, it can take 24 hours or more.
+As the volume of GitLab data grows, the [backup command](#backup-command) takes longer to execute. [Backup options](#backup-options) such as [back up Git repositories concurrently](#back-up-git-repositories-concurrently) and [incremental repository backups](#incremental-repository-backups) can help to reduce execution time. At some point, the backup command becomes impractical by itself. For example, it can take 24 hours or more.
+
+In some cases, architecture changes may be warranted to allow backups to scale. If you are using a GitLab reference architecture, see [Back up and restore large reference architectures](backup_large_reference_architectures.md).
For more information, see [alternative backup strategies](#alternative-backup-strategies).
@@ -27,6 +29,7 @@ For more information, see [alternative backup strategies](#alternative-backup-st
- [PostgreSQL databases](#postgresql-databases)
- [Git repositories](#git-repositories)
- [Blobs](#blobs)
+- [Container Registry](#container-registry)
- [Configuration files](#storing-configuration-files)
- [Other data](#other-data)
@@ -87,6 +90,25 @@ The [backup command](#backup-command) doesn't back up blobs that aren't stored o
- [Amazon S3 backups](https://docs.aws.amazon.com/aws-backup/latest/devguide/s3-backups.html)
- [Google Cloud Storage Transfer Service](https://cloud.google.com/storage-transfer-service) and [Google Cloud Storage Object Versioning](https://cloud.google.com/storage/docs/object-versioning)
+### Container Registry
+
+[GitLab Container Registry](../packages/container_registry.md) storage can be configured in either:
+
+- The file system in a specific location.
+- An [Object Storage](../object_storage.md) solution. Object Storage solutions can be:
+ - Cloud based like Amazon S3 and Google Cloud Storage.
+ - Hosted by you (like MinIO).
+ - A Storage Appliance that exposes an Object Storage-compatible API.
+
+The backup command backs up registry data when they are stored in the default location on the file system.
+
+#### Object storage
+
+The [backup command](#backup-command) doesn't back up blobs that aren't stored on the file system. If you're using [object storage](../object_storage.md), be sure to enable backups with your object storage provider. For example, see:
+
+- [Amazon S3 backups](https://docs.aws.amazon.com/aws-backup/latest/devguide/s3-backups.html)
+- [Google Cloud Storage Transfer Service](https://cloud.google.com/storage-transfer-service) and [Google Cloud Storage Object Versioning](https://cloud.google.com/storage/docs/object-versioning)
+
### Storing configuration files
WARNING:
diff --git a/doc/administration/backup_restore/backup_large_reference_architectures.md b/doc/administration/backup_restore/backup_large_reference_architectures.md
new file mode 100644
index 00000000000..b8983613f56
--- /dev/null
+++ b/doc/administration/backup_restore/backup_large_reference_architectures.md
@@ -0,0 +1,98 @@
+---
+stage: Systems
+group: Geo
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Back up and restore large reference architectures **(FREE SELF)**
+
+This document describes how to:
+
+- [Configure daily backups](#configure-daily-backups)
+- Take a backup now (planned)
+- Restore a backup (planned)
+
+This document is intended for environments using:
+
+- [Linux package (Omnibus) and cloud-native hybrid reference architectures 3,000 users and up](../reference_architectures/index.md)
+- Highly-automated deployment tooling such as [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit)
+- [Amazon RDS](https://aws.amazon.com/rds/) for PostgreSQL data
+- [Amazon S3](https://aws.amazon.com/s3/) for object storage
+- [Object storage](../object_storage.md) to store everything possible, including [blobs](backup_gitlab.md#blobs) and [Container Registry](backup_gitlab.md#container-registry)
+
+## Configure daily backups
+
+### Configure backup of PostgreSQL and object storage data
+
+The [backup command](backup_gitlab.md) uses `pg_dump`, which is [not appropriate for databases over 100 GB](backup_gitlab.md#postgresql-databases). You must choose a PostgreSQL solution which has native, robust backup capabilities.
+
+[Object storage](../object_storage.md), ([not NFS](../nfs.md)) is recommended for storing GitLab data, including [blobs](backup_gitlab.md#blobs) and [Container registry](backup_gitlab.md#container-registry).
+
+1. [Configure AWS Backup](https://docs.aws.amazon.com/aws-backup/latest/devguide/creating-a-backup-plan.html) to back up both RDS and S3 data. For maximum protection, [configure continuous backups as well as snapshot backups](https://docs.aws.amazon.com/aws-backup/latest/devguide/point-in-time-recovery.html).
+1. Configure AWS Backup to copy backups to a separate region. When AWS takes a backup, the backup can only be restored in the region the backup is stored.
+1. After AWS Backup has run at least one scheduled backup, then you can [create an on-demand backup](https://docs.aws.amazon.com/aws-backup/latest/devguide/recov-point-create-on-demand-backup.html) as needed.
+
+### Configure backup of Git repositories
+
+NOTE:
+There is a feature proposal to add the ability to back up repositories directly from Gitaly to object storage. See [epic 10077](https://gitlab.com/groups/gitlab-org/-/epics/10077).
+
+- Linux package (Omnibus):
+
+ We will continue to use the [backup command](backup_gitlab.md#backup-command) to back up Git repositories.
+
+ If utilization is low enough, you can run it from an existing GitLab Rails node. Otherwise, spin up another node.
+
+- Cloud native hybrid:
+
+ [The `backup-utility` command in a `toolbox` pod fails when there is a large amount of data](https://gitlab.com/gitlab-org/gitlab/-/issues/396343#note_1352989908). In this case, you must run the [backup command](backup_gitlab.md#backup-command) to back up Git repositories, and you must run it in a VM running the GitLab Linux package:
+
+ 1. Spin up a VM with 8 vCPU and 7.2 GB memory. This node will be used to back up Git repositories. Note that
+ [a Praefect node cannot be used to back up Git data](https://gitlab.com/gitlab-org/gitlab/-/issues/396343#note_1385950340).
+ 1. Configure the node as another **GitLab Rails** node as defined in your [reference architecture](../reference_architectures/index.md). Use the [GitLab Environment Toolkit `gitlab_rails.yml` playbook](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/2.8.5/ansible/playbooks/gitlab_rails.yml). As with other GitLab Rails nodes, this node must have access to your main Postgres database as well as to Gitaly Cluster.
+
+The backup node will copy all of the environment's Git data, so ensure that it has enough attached storage. For example, you need at least as much storage as one node in a Gitaly Cluster. Without Gitaly Cluster, you need at least as much storage as all Gitaly nodes. Keep in mind that Git repository backups can be significantly larger than Gitaly storage usage because forks are deduplicated in Gitaly but not in backups.
+
+To back up the Git repositories:
+
+1. SSH into the GitLab Rails node.
+1. [Configure uploading backups to remote cloud storage](backup_gitlab.md#upload-backups-to-a-remote-cloud-storage).
+1. [Configure AWS Backup](https://docs.aws.amazon.com/aws-backup/latest/devguide/creating-a-backup-plan.html) for this bucket, or use a bucket in the same account and region as your production data object storage buckets, and ensure this bucket is included in your [preexisting AWS Backup](#configure-backup-of-postgresql-and-object-storage-data).
+1. Run the [backup command](backup_gitlab.md#backup-command), skipping PostgreSQL data:
+
+ ```shell
+ sudo gitlab-backup create SKIP=db
+ ```
+
+ The resulting tar file will include only the Git repositories and some metadata. Blobs such as uploads, artifacts, and LFS do not need to be explicitly skipped, because the command does not back up object storage by default. The tar file will be created in the [`/var/opt/gitlab/backups` directory](https://docs.gitlab.com/omnibus/settings/backups.html#creating-an-application-backup) and [the filename will end in `_gitlab_backup.tar`](backup_gitlab.md#backup-timestamp).
+
+ Since we configured uploading backups to remote cloud storage, the tar file will be uploaded to the remote region and deleted from disk.
+
+1. Note the [timestamp](backup_gitlab.md#backup-timestamp) of the backup file for the next step. For example, if the backup name is `1493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar`, the timestamp is `1493107454_2018_04_25_10.6.4-ce`.
+1. Run the [backup command](backup_gitlab.md#backup-command) again, this time specifying [incremental backup of Git repositories](backup_gitlab.md#incremental-repository-backups), and the timestamp of the source backup file. Using the example timestamp from the previous step, the command is:
+
+ ```shell
+ sudo gitlab-backup create SKIP=db INCREMENTAL=yes PREVIOUS_BACKUP=1493107454_2018_04_25_10.6.4-ce
+ ```
+
+1. Check that the incremental backup succeeded and uploaded to object storage.
+1. [Configure cron to make daily backups](backup_gitlab.md#configuring-cron-to-make-daily-backups). Edit the crontab for the `root` user:
+
+ ```shell
+ sudo su -
+ crontab -e
+ ```
+
+1. There, add the following line to schedule the backup for everyday at 2 AM:
+
+ ```plaintext
+ 0 2 * * * /opt/gitlab/bin/gitlab-backup create SKIP=db INCREMENTAL=yes PREVIOUS_BACKUP=1493107454_2018_04_25_10.6.4-ce CRON=1
+ ```
+
+### Configure backup of configuration files
+
+We strongly recommend using rigorous automation tools such as [Terraform](https://www.terraform.io/) and [Ansible](https://www.ansible.com/) to administer large GitLab environments. [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) is a good example. You may choose to build up your own deployment tool and use it as a reference.
+
+Following this approach, your configuration files and secrets should already exist in secure, canonical locations outside of the production VMs or pods. This document does not cover backing up that data.
+
+As an example, you can store secrets in [AWS Secret Manager](https://aws.amazon.com/secrets-manager/) and pull them into your [Terraform configuration files](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/docs/environment_provision.md#terraform-data-sources). [AWS Secret Manager can be configured to replicate to multiple regions](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create-manage-multi-region-secrets.html).
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 2de3e507370..68808825b00 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -13742,7 +13742,12 @@ Represents the Geo replication and verification state of a ci_secure_file.
| <a id="cisecurefileregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the CiSecureFileRegistry is resynced. |
| <a id="cisecurefileregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the CiSecureFileRegistry. |
| <a id="cisecurefileregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the CiSecureFileRegistry. |
+| <a id="cisecurefileregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the CiSecureFileRegistry. |
+| <a id="cisecurefileregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the CiSecureFileRegistry. |
| <a id="cisecurefileregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the CiSecureFileRegistry is reverified. |
+| <a id="cisecurefileregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the CiSecureFileRegistry. |
+| <a id="cisecurefileregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of CiSecureFileRegistry. |
+| <a id="cisecurefileregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the CiSecureFileRegistry. |
| <a id="cisecurefileregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the CiSecureFileRegistry. |
### `CiStage`
@@ -14243,7 +14248,12 @@ Represents the Geo replication and verification state of an Container Repository
| <a id="containerrepositoryregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the ContainerRepositoryRegistry is resynced. |
| <a id="containerrepositoryregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the ContainerRepositoryRegistry. |
| <a id="containerrepositoryregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the ContainerRepositoryRegistry. |
+| <a id="containerrepositoryregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the ContainerRepositoryRegistry. |
+| <a id="containerrepositoryregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the ContainerRepositoryRegistry. |
| <a id="containerrepositoryregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the ContainerRepositoryRegistry is reverified. |
+| <a id="containerrepositoryregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the ContainerRepositoryRegistry. |
+| <a id="containerrepositoryregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of ContainerRepositoryRegistry. |
+| <a id="containerrepositoryregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the ContainerRepositoryRegistry. |
| <a id="containerrepositoryregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the ContainerRepositoryRegistry. |
### `ContainerRepositoryTag`
@@ -14591,7 +14601,12 @@ Represents the Geo replication and verification state of a dependency_proxy_blob
| <a id="dependencyproxyblobregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the DependencyProxyBlobRegistry is resynced. |
| <a id="dependencyproxyblobregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the DependencyProxyBlobRegistry. |
| <a id="dependencyproxyblobregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the DependencyProxyBlobRegistry. |
+| <a id="dependencyproxyblobregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the DependencyProxyBlobRegistry. |
+| <a id="dependencyproxyblobregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the DependencyProxyBlobRegistry. |
| <a id="dependencyproxyblobregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the DependencyProxyBlobRegistry is reverified. |
+| <a id="dependencyproxyblobregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the DependencyProxyBlobRegistry. |
+| <a id="dependencyproxyblobregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of DependencyProxyBlobRegistry. |
+| <a id="dependencyproxyblobregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the DependencyProxyBlobRegistry. |
| <a id="dependencyproxyblobregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the DependencyProxyBlobRegistry. |
### `DependencyProxyImageTtlGroupPolicy`
@@ -14640,7 +14655,12 @@ Represents the Geo replication and verification state of a dependency_proxy_mani
| <a id="dependencyproxymanifestregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the DependencyProxyManifestRegistry is resynced. |
| <a id="dependencyproxymanifestregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the DependencyProxyManifestRegistry. |
| <a id="dependencyproxymanifestregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the DependencyProxyManifestRegistry. |
+| <a id="dependencyproxymanifestregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the DependencyProxyManifestRegistry. |
+| <a id="dependencyproxymanifestregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the DependencyProxyManifestRegistry. |
| <a id="dependencyproxymanifestregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the DependencyProxyManifestRegistry is reverified. |
+| <a id="dependencyproxymanifestregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the DependencyProxyManifestRegistry. |
+| <a id="dependencyproxymanifestregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of DependencyProxyManifestRegistry. |
+| <a id="dependencyproxymanifestregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the DependencyProxyManifestRegistry. |
| <a id="dependencyproxymanifestregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the DependencyProxyManifestRegistry. |
### `DependencyProxySetting`
@@ -14966,7 +14986,12 @@ Represents the Geo replication and verification state of a Design Management Rep
| <a id="designmanagementrepositoryregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the DesignManagementRepositoryRegistry is resynced. |
| <a id="designmanagementrepositoryregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the DesignManagementRepositoryRegistry. |
| <a id="designmanagementrepositoryregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the DesignManagementRepositoryRegistry. |
+| <a id="designmanagementrepositoryregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the DesignManagementRepositoryRegistry. |
+| <a id="designmanagementrepositoryregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the DesignManagementRepositoryRegistry. |
| <a id="designmanagementrepositoryregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the DesignManagementRepositoryRegistry is reverified. |
+| <a id="designmanagementrepositoryregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the DesignManagementRepositoryRegistry. |
+| <a id="designmanagementrepositoryregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of DesignManagementRepositoryRegistry. |
+| <a id="designmanagementrepositoryregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the DesignManagementRepositoryRegistry. |
| <a id="designmanagementrepositoryregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the DesignManagementRepositoryRegistry. |
### `DesignVersion`
@@ -17431,7 +17456,12 @@ Represents the Geo sync and verification state of a group wiki repository.
| <a id="groupwikirepositoryregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the GroupWikiRepositoryRegistry is resynced. |
| <a id="groupwikirepositoryregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the GroupWikiRepositoryRegistry. |
| <a id="groupwikirepositoryregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the GroupWikiRepositoryRegistry. |
+| <a id="groupwikirepositoryregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the GroupWikiRepositoryRegistry. |
+| <a id="groupwikirepositoryregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the GroupWikiRepositoryRegistry. |
| <a id="groupwikirepositoryregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the GroupWikiRepositoryRegistry is reverified. |
+| <a id="groupwikirepositoryregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the GroupWikiRepositoryRegistry. |
+| <a id="groupwikirepositoryregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of GroupWikiRepositoryRegistry. |
+| <a id="groupwikirepositoryregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the GroupWikiRepositoryRegistry. |
| <a id="groupwikirepositoryregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the GroupWikiRepositoryRegistry. |
### `HelmFileMetadata`
@@ -17965,7 +17995,12 @@ Represents the Geo replication and verification state of a job_artifact.
| <a id="jobartifactregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the JobArtifactRegistry is resynced. |
| <a id="jobartifactregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the JobArtifactRegistry. |
| <a id="jobartifactregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the JobArtifactRegistry. |
+| <a id="jobartifactregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the JobArtifactRegistry. |
+| <a id="jobartifactregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the JobArtifactRegistry. |
| <a id="jobartifactregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the JobArtifactRegistry is reverified. |
+| <a id="jobartifactregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the JobArtifactRegistry. |
+| <a id="jobartifactregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of JobArtifactRegistry. |
+| <a id="jobartifactregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the JobArtifactRegistry. |
| <a id="jobartifactregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the JobArtifactRegistry. |
### `JobPermissions`
@@ -18033,7 +18068,12 @@ Represents the Geo sync and verification state of an LFS object.
| <a id="lfsobjectregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the LfsObjectRegistry is resynced. |
| <a id="lfsobjectregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the LfsObjectRegistry. |
| <a id="lfsobjectregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the LfsObjectRegistry. |
+| <a id="lfsobjectregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the LfsObjectRegistry. |
+| <a id="lfsobjectregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the LfsObjectRegistry. |
| <a id="lfsobjectregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the LfsObjectRegistry is reverified. |
+| <a id="lfsobjectregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the LfsObjectRegistry. |
+| <a id="lfsobjectregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of LfsObjectRegistry. |
+| <a id="lfsobjectregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the LfsObjectRegistry. |
| <a id="lfsobjectregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the LfsObjectRegistry. |
### `LicenseHistoryEntry`
@@ -18894,7 +18934,12 @@ Represents the Geo sync and verification state of a Merge Request diff.
| <a id="mergerequestdiffregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the MergeRequestDiffRegistry is resynced. |
| <a id="mergerequestdiffregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the MergeRequestDiffRegistry. |
| <a id="mergerequestdiffregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the MergeRequestDiffRegistry. |
+| <a id="mergerequestdiffregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the MergeRequestDiffRegistry. |
+| <a id="mergerequestdiffregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the MergeRequestDiffRegistry. |
| <a id="mergerequestdiffregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the MergeRequestDiffRegistry is reverified. |
+| <a id="mergerequestdiffregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the MergeRequestDiffRegistry. |
+| <a id="mergerequestdiffregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of MergeRequestDiffRegistry. |
+| <a id="mergerequestdiffregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the MergeRequestDiffRegistry. |
| <a id="mergerequestdiffregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the MergeRequestDiffRegistry. |
### `MergeRequestParticipant`
@@ -20036,7 +20081,12 @@ Represents the Geo sync and verification state of a package file.
| <a id="packagefileregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the PackageFileRegistry is resynced. |
| <a id="packagefileregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the PackageFileRegistry. |
| <a id="packagefileregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the PackageFileRegistry. |
+| <a id="packagefileregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the PackageFileRegistry. |
+| <a id="packagefileregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the PackageFileRegistry. |
| <a id="packagefileregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the PackageFileRegistry is reverified. |
+| <a id="packagefileregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the PackageFileRegistry. |
+| <a id="packagefileregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of PackageFileRegistry. |
+| <a id="packagefileregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the PackageFileRegistry. |
| <a id="packagefileregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the PackageFileRegistry. |
### `PackageHelmDependencyType`
@@ -20179,7 +20229,12 @@ Represents the Geo replication and verification state of a pages_deployment.
| <a id="pagesdeploymentregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the PagesDeploymentRegistry is resynced. |
| <a id="pagesdeploymentregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the PagesDeploymentRegistry. |
| <a id="pagesdeploymentregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the PagesDeploymentRegistry. |
+| <a id="pagesdeploymentregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the PagesDeploymentRegistry. |
+| <a id="pagesdeploymentregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the PagesDeploymentRegistry. |
| <a id="pagesdeploymentregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the PagesDeploymentRegistry is reverified. |
+| <a id="pagesdeploymentregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the PagesDeploymentRegistry. |
+| <a id="pagesdeploymentregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of PagesDeploymentRegistry. |
+| <a id="pagesdeploymentregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the PagesDeploymentRegistry. |
| <a id="pagesdeploymentregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the PagesDeploymentRegistry. |
### `PathLock`
@@ -20365,7 +20420,12 @@ Represents the Geo sync and verification state of a pipeline artifact.
| <a id="pipelineartifactregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the PipelineArtifactRegistry is resynced. |
| <a id="pipelineartifactregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the PipelineArtifactRegistry. |
| <a id="pipelineartifactregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the PipelineArtifactRegistry. |
+| <a id="pipelineartifactregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the PipelineArtifactRegistry. |
+| <a id="pipelineartifactregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the PipelineArtifactRegistry. |
| <a id="pipelineartifactregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the PipelineArtifactRegistry is reverified. |
+| <a id="pipelineartifactregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the PipelineArtifactRegistry. |
+| <a id="pipelineartifactregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of PipelineArtifactRegistry. |
+| <a id="pipelineartifactregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the PipelineArtifactRegistry. |
| <a id="pipelineartifactregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the PipelineArtifactRegistry. |
### `PipelineCounts`
@@ -22168,7 +22228,12 @@ Represents the Geo replication and verification state of a project repository.
| <a id="projectrepositoryregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the ProjectRepositoryRegistry is resynced. |
| <a id="projectrepositoryregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the ProjectRepositoryRegistry. |
| <a id="projectrepositoryregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the ProjectRepositoryRegistry. |
+| <a id="projectrepositoryregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the ProjectRepositoryRegistry. |
+| <a id="projectrepositoryregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the ProjectRepositoryRegistry. |
| <a id="projectrepositoryregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the ProjectRepositoryRegistry is reverified. |
+| <a id="projectrepositoryregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the ProjectRepositoryRegistry. |
+| <a id="projectrepositoryregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of ProjectRepositoryRegistry. |
+| <a id="projectrepositoryregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the ProjectRepositoryRegistry. |
| <a id="projectrepositoryregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the ProjectRepositoryRegistry. |
### `ProjectSecurityPolicySource`
@@ -22330,7 +22395,12 @@ Represents the Geo replication and verification state of a project_wiki_reposito
| <a id="projectwikirepositoryregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the ProjectWikiRepositoryRegistry is resynced. |
| <a id="projectwikirepositoryregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the ProjectWikiRepositoryRegistry. |
| <a id="projectwikirepositoryregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the ProjectWikiRepositoryRegistry. |
+| <a id="projectwikirepositoryregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the ProjectWikiRepositoryRegistry. |
+| <a id="projectwikirepositoryregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the ProjectWikiRepositoryRegistry. |
| <a id="projectwikirepositoryregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the ProjectWikiRepositoryRegistry is reverified. |
+| <a id="projectwikirepositoryregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the ProjectWikiRepositoryRegistry. |
+| <a id="projectwikirepositoryregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of ProjectWikiRepositoryRegistry. |
+| <a id="projectwikirepositoryregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the ProjectWikiRepositoryRegistry. |
| <a id="projectwikirepositoryregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the ProjectWikiRepositoryRegistry. |
### `PrometheusAlert`
@@ -23329,7 +23399,12 @@ Represents the Geo sync and verification state of a snippet repository.
| <a id="snippetrepositoryregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the SnippetRepositoryRegistry. |
| <a id="snippetrepositoryregistrysnippetrepositoryid"></a>`snippetRepositoryId` | [`ID!`](#id) | ID of the Snippet Repository. |
| <a id="snippetrepositoryregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the SnippetRepositoryRegistry. |
+| <a id="snippetrepositoryregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the SnippetRepositoryRegistry. |
+| <a id="snippetrepositoryregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the SnippetRepositoryRegistry. |
| <a id="snippetrepositoryregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the SnippetRepositoryRegistry is reverified. |
+| <a id="snippetrepositoryregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the SnippetRepositoryRegistry. |
+| <a id="snippetrepositoryregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of SnippetRepositoryRegistry. |
+| <a id="snippetrepositoryregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the SnippetRepositoryRegistry. |
| <a id="snippetrepositoryregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the SnippetRepositoryRegistry. |
### `SshSignature`
@@ -23471,7 +23546,12 @@ Represents the Geo sync and verification state of a terraform state version.
| <a id="terraformstateversionregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the TerraformStateVersionRegistry. |
| <a id="terraformstateversionregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the TerraformStateVersionRegistry. |
| <a id="terraformstateversionregistryterraformstateversionid"></a>`terraformStateVersionId` | [`ID!`](#id) | ID of the terraform state version. |
+| <a id="terraformstateversionregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the TerraformStateVersionRegistry. |
+| <a id="terraformstateversionregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the TerraformStateVersionRegistry. |
| <a id="terraformstateversionregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the TerraformStateVersionRegistry is reverified. |
+| <a id="terraformstateversionregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the TerraformStateVersionRegistry. |
+| <a id="terraformstateversionregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of TerraformStateVersionRegistry. |
+| <a id="terraformstateversionregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the TerraformStateVersionRegistry. |
| <a id="terraformstateversionregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the TerraformStateVersionRegistry. |
### `TestCase`
@@ -23778,7 +23858,12 @@ Represents the Geo replication and verification state of an upload.
| <a id="uploadregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the UploadRegistry is resynced. |
| <a id="uploadregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the UploadRegistry. |
| <a id="uploadregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the UploadRegistry. |
+| <a id="uploadregistryverificationchecksum"></a>`verificationChecksum` | [`String`](#string) | The local checksum of the UploadRegistry. |
+| <a id="uploadregistryverificationfailure"></a>`verificationFailure` | [`String`](#string) | Error message during verification of the UploadRegistry. |
| <a id="uploadregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the UploadRegistry is reverified. |
+| <a id="uploadregistryverificationretrycount"></a>`verificationRetryCount` | [`Int`](#int) | Number of consecutive failed verification attempts of the UploadRegistry. |
+| <a id="uploadregistryverificationstartedat"></a>`verificationStartedAt` | [`Time`](#time) | Timestamp when the verification started of UploadRegistry. |
+| <a id="uploadregistryverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Verification state of the UploadRegistry. |
| <a id="uploadregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the UploadRegistry. |
### `UsageTrendsMeasurement`
diff --git a/doc/architecture/blueprints/cells/cells-feature-snippets.md b/doc/architecture/blueprints/cells/cells-feature-snippets.md
index f5e72c0e3a0..8f0d279d997 100644
--- a/doc/architecture/blueprints/cells/cells-feature-snippets.md
+++ b/doc/architecture/blueprints/cells/cells-feature-snippets.md
@@ -15,16 +15,31 @@ we can document the reasons for not choosing this approach.
# Cells: Snippets
-> TL;DR
+Snippets will be scoped to an Organization. Initially it will not be possible to aggregate snippet collections across Organizations. See also [issue #416954](https://gitlab.com/gitlab-org/gitlab/-/issues/416954).
## 1. Definition
+Two different types of snippets exist:
+
+- [Project snippets](../../../api/project_snippets.md)
+- [Personal snippets](../../../user/snippets.md)
+
+Snippets are backed by a Git repository.
+
## 2. Data flow
## 3. Proposal
+- Both project and personal snippets will be scoped to an Organization.
+- Creation of snippets will also be scoped to a User's current Organization.
+- A User can create many independent snippet collections across multiple Organizations.
+- Snippets are limited to a Cell because Gitaly is confined to a Cell.
+
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
+
+- We will break [snippet discovery](/ee/user/snippets.md#discover-snippets).
+- Snippet access may become subordinate to the visibility of the Organization.
diff --git a/doc/update/package/index.md b/doc/update/package/index.md
index e61eaae5883..148791dbc75 100644
--- a/doc/update/package/index.md
+++ b/doc/update/package/index.md
@@ -42,9 +42,6 @@ check the version your are upgrading to:
- [GitLab 16](https://docs.gitlab.com/omnibus/update/gitlab_16_changes.html)
- [GitLab 15](https://docs.gitlab.com/omnibus/update/gitlab_15_changes.html)
- [GitLab 14](https://docs.gitlab.com/omnibus/update/gitlab_14_changes.html)
-- [GitLab 13](https://docs.gitlab.com/omnibus/update/gitlab_13_changes.html)
-- [GitLab 12](https://docs.gitlab.com/omnibus/update/gitlab_12_changes.html)
-- [GitLab 11](https://docs.gitlab.com/omnibus/update/gitlab_11_changes.html)
## Back up before upgrading
diff --git a/doc/user/project/repository/code_suggestions.md b/doc/user/project/repository/code_suggestions.md
index 95d5873a535..ba5819f9c50 100644
--- a/doc/user/project/repository/code_suggestions.md
+++ b/doc/user/project/repository/code_suggestions.md
@@ -26,8 +26,8 @@ Write code more efficiently by using generative AI to suggest code while you're
Code Suggestions are available:
- To users of GitLab SaaS (by default) and self-managed GitLab Enterprise Edition (when requested). Code Suggestions are not available for GitLab Community Edition.
-- In VS Code and Microsoft Visual Studio when you have the corresponding GitLab extension installed.
-- In the GitLab WebIDE
+- In VS Code, Microsoft Visual Studio, JetBrains IDEs, and Neovim. You must have the corresponding GitLab extension installed.
+- In the GitLab WebIDE.
<div class="video-fallback">
<a href="https://www.youtube.com/watch?v=WnxBYxN2-p4">View an end-to-end demo of Code Suggestions in VS Code</a>.
@@ -164,6 +164,8 @@ Prerequisites:
- For GitLab SaaS, Code Suggestions must be enabled [for the top-level group](../../group/manage.md#enable-code-suggestions) and [for your user account](#enable-code-suggestions-for-an-individual-user).
- To use VS Code, ensure you have installed [the VS Code GitLab Workflow extension](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow).
- To use Microsoft Visual Studio, ensure you have installed [the Visual Studio GitLab extension](https://marketplace.visualstudio.com/items?itemName=GitLab.GitLabExtensionForVisualStudio).
+- To use JetBrains IDEs, ensure you have installed [the GitLab plugin](https://plugins.jetbrains.com/plugin/22325-gitlab).
+- To use Neovim, ensure you have set up the GitLab plugin for Neovim ([`gitlab.vim`](https://gitlab.com/gitlab-org/editor-extensions/gitlab.vim)).
To use Code Suggestions:
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 269c4d9d9aa..c1a96a97fe2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -9356,6 +9356,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Chat not available."
+msgstr ""
+
msgid "ChatMessage|%{project_link}: Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status} in %{duration}"
msgstr ""
@@ -23088,9 +23091,6 @@ msgstr ""
msgid "I accept the %{terms_link}"
msgstr ""
-msgid "I do not know."
-msgstr ""
-
msgid "I forgot my password"
msgstr ""
diff --git a/spec/factories/ml/model_versions.rb b/spec/factories/ml/model_versions.rb
index 5ae0446b78d..61d42fa5b5c 100644
--- a/spec/factories/ml/model_versions.rb
+++ b/spec/factories/ml/model_versions.rb
@@ -9,7 +9,7 @@ FactoryBot.define do
trait :with_package do
package do
- association :ml_model_package, name: model.name, version: version, project_id: project.id
+ association :ml_model_package, name: model.name, version: version, project: project
end
end
end
diff --git a/spec/factories/ml/models.rb b/spec/factories/ml/models.rb
index 2d1b29289a5..158c26499b0 100644
--- a/spec/factories/ml/models.rb
+++ b/spec/factories/ml/models.rb
@@ -6,5 +6,17 @@ FactoryBot.define do
project
default_experiment { association :ml_experiments, project_id: project.id, name: name }
+
+ trait :with_versions do
+ versions { Array.new(2) { association(:ml_model_versions, model: instance) } }
+ end
+
+ trait :with_latest_version_and_package do
+ transient do
+ version { association(:ml_model_versions, :with_package, model: instance) }
+ end
+ versions { [version] }
+ latest_version { version }
+ end
end
end
diff --git a/spec/finders/projects/ml/model_finder_spec.rb b/spec/finders/projects/ml/model_finder_spec.rb
index 386d690a8d2..48333ae49e5 100644
--- a/spec/finders/projects/ml/model_finder_spec.rb
+++ b/spec/finders/projects/ml/model_finder_spec.rb
@@ -3,38 +3,23 @@
require 'spec_helper'
RSpec.describe Projects::Ml::ModelFinder, feature_category: :mlops do
- let_it_be(:model1_a) { create(:ml_model_package) }
- let_it_be(:project) { model1_a.project }
- let_it_be(:model1_b) do
- create(:ml_model_package, name: model1_a.name, project: project)
- end
-
- let_it_be(:model2) do
- create(:ml_model_package, status: :pending_destruction, project: project)
- end
-
- let_it_be(:model3) { create(:ml_model_package) }
- let_it_be(:model4) { create(:generic_package, project: project) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:model1) { create(:ml_models, :with_versions, project: project) }
+ let_it_be(:model2) { create(:ml_models, :with_versions, project: project) }
+ let_it_be(:model3) { create(:ml_models) }
- subject { described_class.new(project).execute.to_a }
+ subject(:models) { described_class.new(project).execute.to_a }
- it 'returns the most recent version of a model' do
- is_expected.to include(model1_b)
+ it 'returns models for project' do
+ is_expected.to match_array([model1, model2])
end
- it 'does not return older versions of a model' do
- is_expected.not_to include(model1_a)
- end
-
- it 'does not return models pending destruction' do
- is_expected.not_to include(model2)
+ it 'including the latest version', :aggregate_failures do
+ expect(models[0].association_cached?(:latest_version)).to be(true)
+ expect(models[1].association_cached?(:latest_version)).to be(true)
end
it 'does not return models belonging to a different project' do
is_expected.not_to include(model3)
end
-
- it 'does not return packages that are not ml_model' do
- is_expected.not_to include(model4)
- end
end
diff --git a/spec/models/ml/model_spec.rb b/spec/models/ml/model_spec.rb
index cfe39239e8c..42d8ed5c0c5 100644
--- a/spec/models/ml/model_spec.rb
+++ b/spec/models/ml/model_spec.rb
@@ -3,17 +3,18 @@
require 'spec_helper'
RSpec.describe Ml::Model, feature_category: :mlops do
- let_it_be(:base_project) { create(:project) }
- let_it_be(:existing_model) { create(:ml_models, name: 'an_existing_model', project: base_project) }
+ let_it_be(:project1) { create(:project) }
+ let_it_be(:project2) { create(:project) }
+ let_it_be(:existing_model) { create(:ml_models, name: 'an_existing_model', project: project1) }
+ let_it_be(:another_existing_model) { create(:ml_models, name: 'an_existing_model', project: project2) }
let_it_be(:valid_name) { 'a_valid_name' }
- let_it_be(:default_experiment) { create(:ml_experiments, name: valid_name, project: base_project) }
-
- let(:project) { base_project }
+ let_it_be(:default_experiment) { create(:ml_experiments, name: valid_name, project: project1) }
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_one(:default_experiment) }
it { is_expected.to have_many(:versions) }
+ it { is_expected.to have_one(:latest_version).class_name('Ml::ModelVersion').inverse_of(:model) }
end
describe '#valid?' do
@@ -22,7 +23,7 @@ RSpec.describe Ml::Model, feature_category: :mlops do
let(:name) { valid_name }
subject(:errors) do
- m = described_class.new(name: name, project: project, default_experiment: default_experiment)
+ m = described_class.new(name: name, project: project1, default_experiment: default_experiment)
m.validate
m.errors
end
@@ -54,12 +55,26 @@ RSpec.describe Ml::Model, feature_category: :mlops do
context 'when model version project is different than model project' do
before do
- allow(default_experiment).to receive(:project_id).and_return(project.id + 1)
+ allow(default_experiment).to receive(:project_id).and_return(project1.id + 1)
end
it { expect(errors).to include(:default_experiment) }
end
end
+
+ describe '.by_project' do
+ subject { described_class.by_project(project1) }
+
+ it { is_expected.to match_array([existing_model]) }
+ end
+
+ describe '.including_latest_version' do
+ subject { described_class.including_latest_version }
+
+ it 'loads latest version' do
+ expect(subject.first.association_cached?(:latest_version)).to be(true)
+ end
+ end
end
describe '.find_or_create' do
diff --git a/spec/models/ml/model_version_spec.rb b/spec/models/ml/model_version_spec.rb
index 7b8410f99a8..8a57a7bc378 100644
--- a/spec/models/ml/model_version_spec.rb
+++ b/spec/models/ml/model_version_spec.rb
@@ -6,7 +6,13 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
using RSpec::Parameterized::TableSyntax
let_it_be(:base_project) { create(:project) }
- let_it_be(:model) { create(:ml_models, project: base_project) }
+ let_it_be(:model1) { create(:ml_models, project: base_project) }
+ let_it_be(:model2) { create(:ml_models, project: base_project) }
+
+ let_it_be(:model_version1) { create(:ml_model_versions, model: model1) }
+ let_it_be(:model_version2) { create(:ml_model_versions, model: model_version1.model) }
+ let_it_be(:model_version3) { create(:ml_model_versions, model: model2) }
+ let_it_be(:model_version4) { create(:ml_model_versions, model: model_version3.model) }
describe 'associations' do
it { is_expected.to belong_to(:project) }
@@ -17,14 +23,14 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
describe 'validation' do
let_it_be(:valid_version) { 'valid_version' }
let_it_be(:valid_package) do
- build_stubbed(:ml_model_package, project: base_project, version: valid_version, name: model.name)
+ build_stubbed(:ml_model_package, project: base_project, version: valid_version, name: model1.name)
end
let(:package) { valid_package }
let(:version) { valid_version }
subject(:errors) do
- mv = described_class.new(version: version, model: model, package: package, project: model.project)
+ mv = described_class.new(version: version, model: model1, package: package, project: model1.project)
mv.validate
mv.errors
end
@@ -45,7 +51,7 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
context 'when version is not unique in project+name' do
let_it_be(:existing_model_version) do
- create(:ml_model_versions, model: model)
+ create(:ml_model_versions, model: model1)
end
let(:version) { existing_model_version.version }
@@ -57,7 +63,7 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
describe 'model' do
context 'when project is different' do
before do
- allow(model).to receive(:project_id).and_return(non_existing_record_id)
+ allow(model1).to receive(:project_id).and_return(non_existing_record_id)
end
it { expect(errors[:model]).to include('model project must be the same') }
@@ -80,7 +86,7 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
context 'when package is not ml_model' do
let(:package) do
- build_stubbed(:generic_package, project: base_project, name: model.name, version: valid_version)
+ build_stubbed(:generic_package, project: base_project, name: model1.name, version: valid_version)
end
it { expect(errors[:package]).to include('package must be ml_model') }
@@ -89,12 +95,12 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
end
describe '#find_or_create!' do
- let_it_be(:existing_model_version) { create(:ml_model_versions, model: model, version: 'abc') }
+ let_it_be(:existing_model_version) { create(:ml_model_versions, model: model1, version: 'abc') }
let(:version) { existing_model_version.version }
let(:package) { nil }
- subject(:find_or_create) { described_class.find_or_create!(model, version, package) }
+ subject(:find_or_create) { described_class.find_or_create!(model1, version, package) }
context 'if model version exists' do
it 'returns the model version', :aggregate_failures do
@@ -105,16 +111,32 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
context 'if model version does not exist' do
let(:version) { 'new_version' }
- let(:package) { create(:ml_model_package, project: model.project, name: model.name, version: version) }
+ let(:package) { create(:ml_model_package, project: model1.project, name: model1.name, version: version) }
it 'creates another model version', :aggregate_failures do
expect { find_or_create }.to change { Ml::ModelVersion.count }.by(1)
model_version = find_or_create
expect(model_version.version).to eq(version)
- expect(model_version.model).to eq(model)
+ expect(model_version.model).to eq(model1)
expect(model_version.package).to eq(package)
end
end
end
+
+ describe '.order_by_model_id_id_desc' do
+ subject { described_class.order_by_model_id_id_desc }
+
+ it 'orders by (model_id, id desc)' do
+ is_expected.to match_array([model_version2, model_version1, model_version4, model_version3])
+ end
+ end
+
+ describe '.latest_by_model' do
+ subject { described_class.latest_by_model }
+
+ it 'returns only the latest model version per model id' do
+ is_expected.to match_array([model_version4, model_version2])
+ end
+ end
end
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
index 120b7d72cd9..50c4a014298 100644
--- a/spec/models/packages/package_spec.rb
+++ b/spec/models/packages/package_spec.rb
@@ -1014,32 +1014,6 @@ RSpec.describe Packages::Package, type: :model, feature_category: :package_regis
end
end
- describe '.select_only_first_by_name' do
- let_it_be(:project) { create(:project) }
- let_it_be(:package1) { create(:package, name: 'p1', created_at: 1000, project: project) }
- let_it_be(:package2) { create(:package, name: 'p1', created_at: 1001, project: project) }
- let_it_be(:package3) { create(:package, name: 'p2', project: project) }
-
- subject { described_class.order_name_desc_version_desc.select_only_first_by_name }
-
- it 'returns only the most recent package by name' do
- is_expected.to eq([package3, package2])
- end
- end
-
- describe '.order_name_desc_version_desc' do
- let_it_be(:project) { create(:project) }
- let_it_be(:package1) { create(:package, name: 'p1', created_at: 1000, project: project) }
- let_it_be(:package2) { create(:package, name: 'p1', created_at: 1001, project: project) }
- let_it_be(:package3) { create(:package, name: 'p2', project: project) }
-
- subject { described_class.order_name_desc_version_desc }
-
- it 'sorts packages by name desc and created desc' do
- is_expected.to eq([package3, package2, package1])
- end
- end
-
context 'sorting' do
let_it_be(:project) { create(:project, name: 'aaa') }
let_it_be(:project2) { create(:project, name: 'bbb') }
diff --git a/spec/presenters/ml/models_index_presenter_spec.rb b/spec/presenters/ml/models_index_presenter_spec.rb
index 697b57a51c1..549700cdd84 100644
--- a/spec/presenters/ml/models_index_presenter_spec.rb
+++ b/spec/presenters/ml/models_index_presenter_spec.rb
@@ -4,8 +4,8 @@ require 'spec_helper'
RSpec.describe Ml::ModelsIndexPresenter, feature_category: :mlops do
let_it_be(:project) { build_stubbed(:project) }
- let_it_be(:model1) { build_stubbed(:ml_model_package, project: project) }
- let_it_be(:model2) { build_stubbed(:ml_model_package, project: project) }
+ let_it_be(:model1) { build_stubbed(:ml_models, :with_latest_version_and_package, project: project) }
+ let_it_be(:model2) { build_stubbed(:ml_models, project: project) }
let_it_be(:models) do
[model1, model2]
end
@@ -17,13 +17,13 @@ RSpec.describe Ml::ModelsIndexPresenter, feature_category: :mlops do
expected_models = [
{
'name' => model1.name,
- 'version' => model1.version,
- 'path' => "/#{project.full_path}/-/packages/#{model1.id}"
+ 'version' => model1.latest_version.version,
+ 'path' => "/#{project.full_path}/-/packages/#{model1.latest_version.package_id}"
},
{
'name' => model2.name,
- 'version' => model2.version,
- 'path' => "/#{project.full_path}/-/packages/#{model2.id}"
+ 'version' => nil,
+ 'path' => nil
}
]
diff --git a/spec/requests/projects/ml/models_controller_spec.rb b/spec/requests/projects/ml/models_controller_spec.rb
index d03748c8dff..b57f5db9b68 100644
--- a/spec/requests/projects/ml/models_controller_spec.rb
+++ b/spec/requests/projects/ml/models_controller_spec.rb
@@ -5,9 +5,8 @@ require 'spec_helper'
RSpec.describe Projects::Ml::ModelsController, feature_category: :mlops do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
- let_it_be(:model1_a) { create(:ml_model_package, project: project) }
- let_it_be(:model1_b) { create(:ml_model_package, project: project, name: model1_a.name) }
- let_it_be(:model2) { create(:ml_model_package, project: project) }
+ let_it_be(:model1) { create(:ml_models, :with_versions, project: project) }
+ let_it_be(:model2) { create(:ml_models, project: project) }
let(:model_registry_enabled) { true }
@@ -45,7 +44,8 @@ RSpec.describe Projects::Ml::ModelsController, feature_category: :mlops do
it 'does not perform N+1 sql queries' do
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { list_models }
- create_list(:ml_model_package, 4, project: project)
+ create_list(:ml_model_versions, 2, model: model1)
+ create_list(:ml_model_versions, 2, model: model2)
expect { list_models }.not_to exceed_all_query_limit(control_count)
end