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--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/finders/merge_requests_finder.rb2
-rw-r--r--app/graphql/mutations/concerns/mutations/authorizes_project.rb17
-rw-r--r--app/graphql/mutations/concerns/mutations/finds_project.rb11
-rw-r--r--app/graphql/types/project_type.rb7
-rw-r--r--app/models/concerns/packages/debian/distribution.rb109
-rw-r--r--app/models/group.rb3
-rw-r--r--app/models/merge_request.rb13
-rw-r--r--app/models/packages/debian/group_distribution.rb9
-rw-r--r--app/models/packages/debian/project_distribution.rb9
-rw-r--r--app/models/project.rb2
-rw-r--r--app/services/merge_requests/close_service.rb2
-rw-r--r--app/services/merge_requests/post_merge_service.rb2
-rw-r--r--app/services/merge_requests/reopen_service.rb2
-rw-r--r--app/uploaders/packages/debian/distribution_release_file_uploader.rb27
-rw-r--r--changelogs/unreleased/21686_show_merge_request_for_squashed_commits.yml5
-rw-r--r--changelogs/unreleased/296842-review-requests-counter-cache-invalidation.yml5
-rw-r--r--changelogs/unreleased/debian_distributions.yml5
-rw-r--r--changelogs/unreleased/jivanvl-remove-total-duration-ci-cd-analytics.yml5
-rw-r--r--db/migrate/20201204110700_create_packages_debian_project_distributions.rb62
-rw-r--r--db/migrate/20201204110800_create_packages_debian_group_distributions.rb62
-rw-r--r--db/migrate/20201216151616_add_squash_commit_sha_index.rb22
-rw-r--r--db/schema_migrations/202012041107001
-rw-r--r--db/schema_migrations/202012041108001
-rw-r--r--db/schema_migrations/202012161516161
-rw-r--r--db/structure.sql122
-rw-r--r--doc/administration/reference_architectures/1k_users.md3
-rw-r--r--doc/administration/reference_architectures/2k_users.md3
-rw-r--r--doc/administration/reference_architectures/3k_users.md11
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql5
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json14
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/ci/yaml/README.md7
-rw-r--r--doc/development/product_analytics/snowplow.md7
-rw-r--r--doc/gitlab-basics/create-project.md2
-rw-r--r--doc/user/group/index.md6
-rw-r--r--doc/user/group/saml_sso/index.md2
-rw-r--r--doc/user/project/issue_board.md2
-rw-r--r--doc/user/search/img/project_search_general_settings_v13_8.pngbin0 -> 47120 bytes
-rw-r--r--doc/user/search/index.md47
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/factories/packages/debian/distribution.rb25
-rw-r--r--spec/finders/merge_requests_finder_spec.rb39
-rw-r--r--spec/graphql/types/project_type_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/models/group_spec.rb20
-rw-r--r--spec/models/merge_request_spec.rb42
-rw-r--r--spec/models/packages/debian/group_distribution_spec.rb7
-rw-r--r--spec/models/packages/debian/project_distribution_spec.rb7
-rw-r--r--spec/models/project_spec.rb20
-rw-r--r--spec/services/merge_requests/close_service_spec.rb1
-rw-r--r--spec/services/merge_requests/post_merge_service_spec.rb1
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb1
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb204
-rw-r--r--spec/support/shared_examples/services/merge_request_shared_examples.rb15
-rw-r--r--spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb52
57 files changed, 989 insertions, 76 deletions
diff --git a/Gemfile b/Gemfile
index 32aea114d89..ebfa3796724 100644
--- a/Gemfile
+++ b/Gemfile
@@ -255,7 +255,7 @@ gem 'slack-messenger', '~> 2.3.4'
gem 'hangouts-chat', '~> 0.0.5'
# Asana integration
-gem 'asana', '0.10.2'
+gem 'asana', '~> 0.10.3'
# FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index c11b85c9032..427596e619c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -76,7 +76,7 @@ GEM
apollo_upload_server (2.0.2)
graphql (>= 1.8)
rails (>= 4.2)
- asana (0.10.2)
+ asana (0.10.3)
faraday (~> 1.0)
faraday_middleware (~> 1.0)
faraday_middleware-multi_json (~> 0.0)
@@ -1276,7 +1276,7 @@ DEPENDENCIES
addressable (~> 2.7)
akismet (~> 3.0)
apollo_upload_server (~> 2.0.2)
- asana (= 0.10.2)
+ asana (~> 0.10.3)
asciidoctor (~> 2.0.10)
asciidoctor-include-ext (~> 0.3.1)
asciidoctor-kroki (~> 0.2.2)
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 978550aedaf..87c190bdc45 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -84,7 +84,7 @@ class MergeRequestsFinder < IssuableFinder
def by_commit(items)
return items unless params[:commit_sha].presence
- items.by_commit_sha(params[:commit_sha])
+ items.by_related_commit_sha(params[:commit_sha])
end
def source_branch
diff --git a/app/graphql/mutations/concerns/mutations/authorizes_project.rb b/app/graphql/mutations/concerns/mutations/authorizes_project.rb
deleted file mode 100644
index 87341525d6c..00000000000
--- a/app/graphql/mutations/concerns/mutations/authorizes_project.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuthorizesProject
- include ResolvesProject
-
- def authorized_find_project!(full_path:)
- authorized_find!(full_path: full_path)
- end
-
- private
-
- def find_object(full_path:)
- resolve_project(full_path: full_path)
- end
- end
-end
diff --git a/app/graphql/mutations/concerns/mutations/finds_project.rb b/app/graphql/mutations/concerns/mutations/finds_project.rb
new file mode 100644
index 00000000000..577f9dc90f8
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/finds_project.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Mutations
+ module FindsProject
+ private
+
+ def find_object(full_path)
+ Project.find_by_full_path(full_path)
+ end
+ end
+end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index a7d9548610e..195da01f41f 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -315,9 +315,6 @@ module Types
description: 'Pipeline analytics',
resolver: Resolvers::ProjectPipelineStatisticsResolver
- field :total_pipeline_duration, GraphQL::INT_TYPE, null: true,
- description: 'Total pipeline duration for all of the pipelines in a project'
-
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args|
LabelsFinder
@@ -362,10 +359,6 @@ module Types
project.container_repositories.size
end
- def total_pipeline_duration
- object.all_pipelines.total_duration
- end
-
private
def project
diff --git a/app/models/concerns/packages/debian/distribution.rb b/app/models/concerns/packages/debian/distribution.rb
new file mode 100644
index 00000000000..2420f10f38a
--- /dev/null
+++ b/app/models/concerns/packages/debian/distribution.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+module Packages
+ module Debian
+ module Distribution
+ extend ActiveSupport::Concern
+
+ included do
+ include FileStoreMounter
+
+ def self.container_foreign_key
+ "#{container_type}_id".to_sym
+ end
+
+ alias_attribute :container, container_type
+ alias_attribute :container_id, "#{container_type}_id"
+
+ belongs_to container_type
+ belongs_to :creator, class_name: 'User'
+
+ validates :codename,
+ presence: true,
+ uniqueness: { scope: [container_foreign_key] },
+ format: { with: Gitlab::Regex.debian_distribution_regex }
+
+ validates :suite,
+ allow_nil: true,
+ format: { with: Gitlab::Regex.debian_distribution_regex }
+ validates :suite,
+ uniqueness: { scope: [container_foreign_key] },
+ if: :suite
+
+ validate :unique_codename_and_suite
+
+ validates :origin,
+ allow_nil: true,
+ format: { with: Gitlab::Regex.debian_distribution_regex }
+
+ validates :label,
+ allow_nil: true,
+ format: { with: Gitlab::Regex.debian_distribution_regex }
+
+ validates :version,
+ allow_nil: true,
+ format: { with: Gitlab::Regex.debian_version_regex }
+
+ # The Valid-Until field is a security measure to prevent malicious attackers to
+ # serve an outdated repository, with vulnerable packages
+ # (keeping in mind that most Debian repository are not using TLS but use GPG
+ # signatures instead).
+ # A minimum of 24 hours is simply to avoid generating indices too often
+ # (which generates load).
+ # Official Debian repositories are generated 4 times a day, and valid for 7 days.
+ # Full ref: https://wiki.debian.org/DebianRepository/Format#Date.2C_Valid-Until
+ validates :valid_time_duration_seconds,
+ allow_nil: true,
+ numericality: { greater_than_or_equal_to: 24.hours.to_i }
+
+ validates container_type, presence: true
+ validates :file_store, presence: true
+
+ validates :file_signature, absence: true
+ validates :signing_keys, absence: true
+
+ scope :with_container, ->(subject) { where(container_type => subject) }
+ scope :with_codename, ->(codename) { where(codename: codename) }
+ scope :with_suite, ->(suite) { where(suite: suite) }
+
+ attr_encrypted :signing_keys,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_truncated,
+ algorithm: 'aes-256-gcm',
+ encode: false,
+ encode_iv: false
+
+ mount_file_store_uploader Packages::Debian::DistributionReleaseFileUploader
+
+ def needs_update?
+ !file.exists? || time_duration_expired?
+ end
+
+ private
+
+ def time_duration_expired?
+ return false unless valid_time_duration_seconds.present?
+
+ updated_at + valid_time_duration_seconds.seconds + 6.hours < Time.current
+ end
+
+ def unique_codename_and_suite
+ errors.add(:codename, _('has already been taken as Suite')) if codename_exists_as_suite?
+ errors.add(:suite, _('has already been taken as Codename')) if suite_exists_as_codename?
+ end
+
+ def codename_exists_as_suite?
+ return false unless codename.present?
+
+ self.class.with_container(container).with_suite(codename).exists?
+ end
+
+ def suite_exists_as_codename?
+ return false unless suite.present?
+
+ self.class.with_container(container).with_codename(suite).exists?
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/group.rb b/app/models/group.rb
index 739135e82dd..903d0154969 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -75,6 +75,9 @@ class Group < Namespace
has_many :dependency_proxy_blobs, class_name: 'DependencyProxy::Blob'
has_many :dependency_proxy_manifests, class_name: 'DependencyProxy::Manifest'
+ # debian_distributions must be destroyed by ruby code in order to properly remove carrierwave uploads
+ has_many :debian_distributions, class_name: 'Packages::Debian::GroupDistribution', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+
accepts_nested_attributes_for :variables, allow_destroy: true
validate :visibility_level_allowed_by_projects
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index db066184e91..3918498f694 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -261,6 +261,19 @@ class MergeRequest < ApplicationRecord
scope :by_merge_commit_sha, -> (sha) do
where(merge_commit_sha: sha)
end
+ scope :by_squash_commit_sha, -> (sha) do
+ where(squash_commit_sha: sha)
+ end
+ scope :by_related_commit_sha, -> (sha) do
+ from_union(
+ [
+ by_commit_sha(sha),
+ by_squash_commit_sha(sha),
+ by_merge_commit_sha(sha)
+ ],
+ remove_duplicates: false
+ )
+ end
scope :by_cherry_pick_sha, -> (sha) do
joins(:notes).where(notes: { commit_id: sha })
end
diff --git a/app/models/packages/debian/group_distribution.rb b/app/models/packages/debian/group_distribution.rb
new file mode 100644
index 00000000000..eea7acacc96
--- /dev/null
+++ b/app/models/packages/debian/group_distribution.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Packages::Debian::GroupDistribution < ApplicationRecord
+ def self.container_type
+ :group
+ end
+
+ include Packages::Debian::Distribution
+end
diff --git a/app/models/packages/debian/project_distribution.rb b/app/models/packages/debian/project_distribution.rb
new file mode 100644
index 00000000000..a73c12d172d
--- /dev/null
+++ b/app/models/packages/debian/project_distribution.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Packages::Debian::ProjectDistribution < ApplicationRecord
+ def self.container_type
+ :project
+ end
+
+ include Packages::Debian::Distribution
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 74f37d7e67c..fdb16640cb4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -200,6 +200,8 @@ class Project < ApplicationRecord
# Packages
has_many :packages, class_name: 'Packages::Package'
has_many :package_files, through: :packages, class_name: 'Packages::PackageFile'
+ # debian_distributions must be destroyed by ruby code in order to properly remove carrierwave uploads
+ has_many :debian_distributions, class_name: 'Packages::Debian::ProjectDistribution', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project
has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb
index ab0e7e4ad06..f83b14c7269 100644
--- a/app/services/merge_requests/close_service.rb
+++ b/app/services/merge_requests/close_service.rb
@@ -18,7 +18,7 @@ module MergeRequests
notification_service.async.close_mr(merge_request, current_user)
todo_service.close_merge_request(merge_request, current_user)
execute_hooks(merge_request, 'close')
- invalidate_cache_counts(merge_request, users: merge_request.assignees)
+ invalidate_cache_counts(merge_request, users: merge_request.assignees | merge_request.reviewers)
merge_request.update_project_counter_caches
cleanup_environments(merge_request)
abort_auto_merge(merge_request, 'merge request was closed')
diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb
index 17afb04ac11..f04ec3c3e80 100644
--- a/app/services/merge_requests/post_merge_service.rb
+++ b/app/services/merge_requests/post_merge_service.rb
@@ -18,7 +18,7 @@ module MergeRequests
merge_request_activity_counter.track_merge_mr_action(user: current_user)
notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge')
- invalidate_cache_counts(merge_request, users: merge_request.assignees)
+ invalidate_cache_counts(merge_request, users: merge_request.assignees | merge_request.reviewers)
merge_request.update_project_counter_caches
delete_non_latest_diffs(merge_request)
cancel_review_app_jobs!(merge_request)
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index fde50bd959f..35c50d63da0 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -13,7 +13,7 @@ module MergeRequests
execute_hooks(merge_request, 'reopen')
merge_request.reload_diff(current_user)
merge_request.mark_as_unchecked
- invalidate_cache_counts(merge_request, users: merge_request.assignees)
+ invalidate_cache_counts(merge_request, users: merge_request.assignees | merge_request.reviewers)
merge_request.update_project_counter_caches
merge_request.cache_merge_request_closes_issues!(current_user)
merge_request.cleanup_schedule&.destroy
diff --git a/app/uploaders/packages/debian/distribution_release_file_uploader.rb b/app/uploaders/packages/debian/distribution_release_file_uploader.rb
new file mode 100644
index 00000000000..9a30aac6396
--- /dev/null
+++ b/app/uploaders/packages/debian/distribution_release_file_uploader.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+class Packages::Debian::DistributionReleaseFileUploader < GitlabUploader
+ extend Workhorse::UploadPath
+ include ObjectStorage::Concern
+
+ storage_options Gitlab.config.packages
+
+ after :store, :schedule_background_upload
+
+ alias_method :upload, :model
+
+ def filename
+ 'Release'
+ end
+
+ def store_dir
+ dynamic_segment
+ end
+
+ private
+
+ def dynamic_segment
+ raise ObjectNotReadyError, 'Package model not ready' unless model.id
+
+ Gitlab::HashedPath.new("debian_#{model.class.container_type}_distribution", model.id, root_hash: model.container_id)
+ end
+end
diff --git a/changelogs/unreleased/21686_show_merge_request_for_squashed_commits.yml b/changelogs/unreleased/21686_show_merge_request_for_squashed_commits.yml
new file mode 100644
index 00000000000..cc59ac92fd9
--- /dev/null
+++ b/changelogs/unreleased/21686_show_merge_request_for_squashed_commits.yml
@@ -0,0 +1,5 @@
+---
+title: Extend MergeRequestFinder to search by squash and merge commits
+merge_request: 49968
+author:
+type: added
diff --git a/changelogs/unreleased/296842-review-requests-counter-cache-invalidation.yml b/changelogs/unreleased/296842-review-requests-counter-cache-invalidation.yml
new file mode 100644
index 00000000000..a92acd20c91
--- /dev/null
+++ b/changelogs/unreleased/296842-review-requests-counter-cache-invalidation.yml
@@ -0,0 +1,5 @@
+---
+title: Invalidate reviews counter cache when MR gets closed/merged/reopened
+merge_request: 51055
+author:
+type: fixed
diff --git a/changelogs/unreleased/debian_distributions.yml b/changelogs/unreleased/debian_distributions.yml
new file mode 100644
index 00000000000..e552b33d83e
--- /dev/null
+++ b/changelogs/unreleased/debian_distributions.yml
@@ -0,0 +1,5 @@
+---
+title: Debian Group and Project Distributions
+merge_request: 49405
+author: Mathieu Parent
+type: added
diff --git a/changelogs/unreleased/jivanvl-remove-total-duration-ci-cd-analytics.yml b/changelogs/unreleased/jivanvl-remove-total-duration-ci-cd-analytics.yml
new file mode 100644
index 00000000000..0c3cb19a023
--- /dev/null
+++ b/changelogs/unreleased/jivanvl-remove-total-duration-ci-cd-analytics.yml
@@ -0,0 +1,5 @@
+---
+title: Remove total_pipeline_duration from project_type
+merge_request: 50093
+author:
+type: changed
diff --git a/db/migrate/20201204110700_create_packages_debian_project_distributions.rb b/db/migrate/20201204110700_create_packages_debian_project_distributions.rb
new file mode 100644
index 00000000000..5fe45c6236f
--- /dev/null
+++ b/db/migrate/20201204110700_create_packages_debian_project_distributions.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+class CreatePackagesDebianProjectDistributions < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ UNIQUE_CODENAME = 'uniq_pkgs_debian_project_distributions_project_id_and_codename'
+ UNIQUE_SUITE = 'uniq_pkgs_debian_project_distributions_project_id_and_suite'
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ unless table_exists?(:packages_debian_project_distributions)
+ create_table :packages_debian_project_distributions do |t|
+ t.timestamps_with_timezone
+ t.references :project, foreign_key: { to_table: :projects, on_delete: :restrict }, null: false
+ t.references :creator, foreign_key: { to_table: :users, on_delete: :nullify }
+ t.integer :valid_time_duration_seconds
+ t.integer :file_store, limit: 2, default: 1, null: false
+ t.boolean :automatic, default: true, null: false
+ t.boolean :automatic_upgrades, default: false, null: false
+ t.text :codename, null: false
+ t.text :suite
+ t.text :origin
+ t.text :label
+ t.text :version
+ t.text :description
+ t.text :encrypted_signing_keys
+ t.text :encrypted_signing_keys_iv
+ t.text :file
+ t.text :file_signature
+
+ t.index %w(project_id codename),
+ name: UNIQUE_CODENAME,
+ unique: true,
+ using: :btree
+ t.index %w(project_id suite),
+ name: UNIQUE_SUITE,
+ unique: true,
+ using: :btree
+ end
+ end
+ end
+
+ add_text_limit :packages_debian_project_distributions, :codename, 255
+ add_text_limit :packages_debian_project_distributions, :suite, 255
+ add_text_limit :packages_debian_project_distributions, :origin, 255
+ add_text_limit :packages_debian_project_distributions, :label, 255
+ add_text_limit :packages_debian_project_distributions, :version, 255
+ add_text_limit :packages_debian_project_distributions, :description, 255
+ add_text_limit :packages_debian_project_distributions, :encrypted_signing_keys, 2048
+ add_text_limit :packages_debian_project_distributions, :encrypted_signing_keys_iv, 255
+ add_text_limit :packages_debian_project_distributions, :file, 255
+ add_text_limit :packages_debian_project_distributions, :file_signature, 4096
+ end
+
+ def down
+ drop_table :packages_debian_project_distributions
+ end
+end
diff --git a/db/migrate/20201204110800_create_packages_debian_group_distributions.rb b/db/migrate/20201204110800_create_packages_debian_group_distributions.rb
new file mode 100644
index 00000000000..9ad9a59b3e9
--- /dev/null
+++ b/db/migrate/20201204110800_create_packages_debian_group_distributions.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+class CreatePackagesDebianGroupDistributions < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ UNIQUE_CODENAME = 'uniq_pkgs_debian_group_distributions_group_id_and_codename'
+ UNIQUE_SUITE = 'uniq_pkgs_debian_group_distributions_group_id_and_suite'
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ unless table_exists?(:packages_debian_group_distributions)
+ create_table :packages_debian_group_distributions do |t|
+ t.timestamps_with_timezone
+ t.references :group, foreign_key: { to_table: :namespaces, on_delete: :restrict }, null: false
+ t.references :creator, foreign_key: { to_table: :users, on_delete: :nullify }
+ t.integer :valid_time_duration_seconds
+ t.integer :file_store, limit: 2, default: 1, null: false
+ t.boolean :automatic, default: true, null: false
+ t.boolean :automatic_upgrades, default: false, null: false
+ t.text :codename, null: false
+ t.text :suite
+ t.text :origin
+ t.text :label
+ t.text :version
+ t.text :description
+ t.text :encrypted_signing_keys
+ t.text :encrypted_signing_keys_iv
+ t.text :file
+ t.text :file_signature
+
+ t.index %w(group_id codename),
+ name: UNIQUE_CODENAME,
+ unique: true,
+ using: :btree
+ t.index %w(group_id suite),
+ name: UNIQUE_SUITE,
+ unique: true,
+ using: :btree
+ end
+ end
+ end
+
+ add_text_limit :packages_debian_group_distributions, :codename, 255
+ add_text_limit :packages_debian_group_distributions, :suite, 255
+ add_text_limit :packages_debian_group_distributions, :origin, 255
+ add_text_limit :packages_debian_group_distributions, :label, 255
+ add_text_limit :packages_debian_group_distributions, :version, 255
+ add_text_limit :packages_debian_group_distributions, :description, 255
+ add_text_limit :packages_debian_group_distributions, :encrypted_signing_keys, 2048
+ add_text_limit :packages_debian_group_distributions, :encrypted_signing_keys_iv, 255
+ add_text_limit :packages_debian_group_distributions, :file, 255
+ add_text_limit :packages_debian_group_distributions, :file_signature, 4096
+ end
+
+ def down
+ drop_table :packages_debian_group_distributions
+ end
+end
diff --git a/db/migrate/20201216151616_add_squash_commit_sha_index.rb b/db/migrate/20201216151616_add_squash_commit_sha_index.rb
new file mode 100644
index 00000000000..ac6d3fda2d2
--- /dev/null
+++ b/db/migrate/20201216151616_add_squash_commit_sha_index.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddSquashCommitShaIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = "index_merge_requests_on_target_project_id_and_squash_commit_sha"
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :merge_requests,
+ [:target_project_id, :squash_commit_sha],
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index :merge_requests,
+ [:target_project_id, :squash_commit_sha],
+ name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20201204110700 b/db/schema_migrations/20201204110700
new file mode 100644
index 00000000000..b69e93149c2
--- /dev/null
+++ b/db/schema_migrations/20201204110700
@@ -0,0 +1 @@
+986ffa5e3e168ce9acf9b346c94bdee05d85c71abe238b8aa21f95cc472faabc \ No newline at end of file
diff --git a/db/schema_migrations/20201204110800 b/db/schema_migrations/20201204110800
new file mode 100644
index 00000000000..de1fdf7442a
--- /dev/null
+++ b/db/schema_migrations/20201204110800
@@ -0,0 +1 @@
+aecf517402d3decf8f7323e8f43fdfe7160cbe7542a474e392996abd75b2d70f \ No newline at end of file
diff --git a/db/schema_migrations/20201216151616 b/db/schema_migrations/20201216151616
new file mode 100644
index 00000000000..a8b27610716
--- /dev/null
+++ b/db/schema_migrations/20201216151616
@@ -0,0 +1 @@
+f1c6927431895c6ce03fe7e0be30fcd0a1f4ccfeac8277ee0662d7434b97d257 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index d768b885ad6..0a66608ba32 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -14721,6 +14721,88 @@ CREATE TABLE packages_debian_file_metadata (
CONSTRAINT check_e6e1fffcca CHECK ((char_length(architecture) <= 255))
);
+CREATE TABLE packages_debian_group_distributions (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ group_id bigint NOT NULL,
+ creator_id bigint,
+ valid_time_duration_seconds integer,
+ file_store smallint DEFAULT 1 NOT NULL,
+ automatic boolean DEFAULT true NOT NULL,
+ automatic_upgrades boolean DEFAULT false NOT NULL,
+ codename text NOT NULL,
+ suite text,
+ origin text,
+ label text,
+ version text,
+ description text,
+ encrypted_signing_keys text,
+ encrypted_signing_keys_iv text,
+ file text,
+ file_signature text,
+ CONSTRAINT check_310ac457b8 CHECK ((char_length(description) <= 255)),
+ CONSTRAINT check_3d6f87fc31 CHECK ((char_length(file_signature) <= 4096)),
+ CONSTRAINT check_3fdadf4a0c CHECK ((char_length(version) <= 255)),
+ CONSTRAINT check_590e18405a CHECK ((char_length(codename) <= 255)),
+ CONSTRAINT check_9b90bc0f07 CHECK ((char_length(encrypted_signing_keys_iv) <= 255)),
+ CONSTRAINT check_b057cd840a CHECK ((char_length(origin) <= 255)),
+ CONSTRAINT check_b811ec1218 CHECK ((char_length(encrypted_signing_keys) <= 2048)),
+ CONSTRAINT check_be5ed8d307 CHECK ((char_length(file) <= 255)),
+ CONSTRAINT check_d3244bfc0b CHECK ((char_length(label) <= 255)),
+ CONSTRAINT check_e7c928a24b CHECK ((char_length(suite) <= 255))
+);
+
+CREATE SEQUENCE packages_debian_group_distributions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE packages_debian_group_distributions_id_seq OWNED BY packages_debian_group_distributions.id;
+
+CREATE TABLE packages_debian_project_distributions (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ project_id bigint NOT NULL,
+ creator_id bigint,
+ valid_time_duration_seconds integer,
+ file_store smallint DEFAULT 1 NOT NULL,
+ automatic boolean DEFAULT true NOT NULL,
+ automatic_upgrades boolean DEFAULT false NOT NULL,
+ codename text NOT NULL,
+ suite text,
+ origin text,
+ label text,
+ version text,
+ description text,
+ encrypted_signing_keys text,
+ encrypted_signing_keys_iv text,
+ file text,
+ file_signature text,
+ CONSTRAINT check_6177ccd4a6 CHECK ((char_length(origin) <= 255)),
+ CONSTRAINT check_6f6b55a4c4 CHECK ((char_length(label) <= 255)),
+ CONSTRAINT check_834dabadb6 CHECK ((char_length(codename) <= 255)),
+ CONSTRAINT check_96965792c2 CHECK ((char_length(version) <= 255)),
+ CONSTRAINT check_a56ae58a17 CHECK ((char_length(suite) <= 255)),
+ CONSTRAINT check_a5a2ac6af2 CHECK ((char_length(file_signature) <= 4096)),
+ CONSTRAINT check_b93154339f CHECK ((char_length(description) <= 255)),
+ CONSTRAINT check_c25603a25b CHECK ((char_length(encrypted_signing_keys) <= 2048)),
+ CONSTRAINT check_cb4ac9599e CHECK ((char_length(file) <= 255)),
+ CONSTRAINT check_d488f8cce3 CHECK ((char_length(encrypted_signing_keys_iv) <= 255))
+);
+
+CREATE SEQUENCE packages_debian_project_distributions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE packages_debian_project_distributions_id_seq OWNED BY packages_debian_project_distributions.id;
+
CREATE TABLE packages_dependencies (
id bigint NOT NULL,
name character varying(255) NOT NULL,
@@ -18657,6 +18739,10 @@ ALTER TABLE ONLY packages_conan_file_metadata ALTER COLUMN id SET DEFAULT nextva
ALTER TABLE ONLY packages_conan_metadata ALTER COLUMN id SET DEFAULT nextval('packages_conan_metadata_id_seq'::regclass);
+ALTER TABLE ONLY packages_debian_group_distributions ALTER COLUMN id SET DEFAULT nextval('packages_debian_group_distributions_id_seq'::regclass);
+
+ALTER TABLE ONLY packages_debian_project_distributions ALTER COLUMN id SET DEFAULT nextval('packages_debian_project_distributions_id_seq'::regclass);
+
ALTER TABLE ONLY packages_dependencies ALTER COLUMN id SET DEFAULT nextval('packages_dependencies_id_seq'::regclass);
ALTER TABLE ONLY packages_dependency_links ALTER COLUMN id SET DEFAULT nextval('packages_dependency_links_id_seq'::regclass);
@@ -20008,6 +20094,12 @@ ALTER TABLE ONLY packages_conan_metadata
ALTER TABLE ONLY packages_debian_file_metadata
ADD CONSTRAINT packages_debian_file_metadata_pkey PRIMARY KEY (package_file_id);
+ALTER TABLE ONLY packages_debian_group_distributions
+ ADD CONSTRAINT packages_debian_group_distributions_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY packages_debian_project_distributions
+ ADD CONSTRAINT packages_debian_project_distributions_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY packages_dependencies
ADD CONSTRAINT packages_dependencies_pkey PRIMARY KEY (id);
@@ -21984,6 +22076,8 @@ CREATE INDEX index_merge_requests_on_target_project_id_and_iid_and_state_id ON m
CREATE INDEX index_merge_requests_on_target_project_id_and_iid_jira_title ON merge_requests USING btree (target_project_id, iid) WHERE ((title)::text ~ '[A-Z][A-Z_0-9]+-\d+'::text);
+CREATE INDEX index_merge_requests_on_target_project_id_and_squash_commit_sha ON merge_requests USING btree (target_project_id, squash_commit_sha);
+
CREATE INDEX index_merge_requests_on_target_project_id_and_target_branch ON merge_requests USING btree (target_project_id, target_branch) WHERE ((state_id = 1) AND (merge_when_pipeline_succeeds = true));
CREATE INDEX index_merge_requests_on_target_project_id_iid_jira_description ON merge_requests USING btree (target_project_id, iid) WHERE (description ~ '[A-Z][A-Z_0-9]+-\d+'::text);
@@ -22190,6 +22284,14 @@ CREATE UNIQUE INDEX index_packages_conan_file_metadata_on_package_file_id ON pac
CREATE UNIQUE INDEX index_packages_conan_metadata_on_package_id_username_channel ON packages_conan_metadata USING btree (package_id, package_username, package_channel);
+CREATE INDEX index_packages_debian_group_distributions_on_creator_id ON packages_debian_group_distributions USING btree (creator_id);
+
+CREATE INDEX index_packages_debian_group_distributions_on_group_id ON packages_debian_group_distributions USING btree (group_id);
+
+CREATE INDEX index_packages_debian_project_distributions_on_creator_id ON packages_debian_project_distributions USING btree (creator_id);
+
+CREATE INDEX index_packages_debian_project_distributions_on_project_id ON packages_debian_project_distributions USING btree (project_id);
+
CREATE UNIQUE INDEX index_packages_dependencies_on_name_and_version_pattern ON packages_dependencies USING btree (name, version_pattern);
CREATE INDEX index_packages_dependency_links_on_dependency_id ON packages_dependency_links USING btree (dependency_id);
@@ -23146,6 +23248,14 @@ CREATE INDEX tmp_index_oauth_applications_on_id_where_trusted ON oauth_applicati
CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING btree (id) WHERE (state <> 2);
+CREATE UNIQUE INDEX uniq_pkgs_debian_group_distributions_group_id_and_codename ON packages_debian_group_distributions USING btree (group_id, codename);
+
+CREATE UNIQUE INDEX uniq_pkgs_debian_group_distributions_group_id_and_suite ON packages_debian_group_distributions USING btree (group_id, suite);
+
+CREATE UNIQUE INDEX uniq_pkgs_debian_project_distributions_project_id_and_codename ON packages_debian_project_distributions USING btree (project_id, codename);
+
+CREATE UNIQUE INDEX uniq_pkgs_debian_project_distributions_project_id_and_suite ON packages_debian_project_distributions USING btree (project_id, suite);
+
CREATE UNIQUE INDEX unique_merge_request_metrics_by_merge_request_id ON merge_request_metrics USING btree (merge_request_id);
CREATE UNIQUE INDEX vulnerability_feedback_unique_idx ON vulnerability_feedback USING btree (project_id, category, feedback_type, project_fingerprint);
@@ -24148,6 +24258,9 @@ ALTER TABLE ONLY trending_projects
ALTER TABLE ONLY project_deploy_tokens
ADD CONSTRAINT fk_rails_0aca134388 FOREIGN KEY (deploy_token_id) REFERENCES deploy_tokens(id) ON DELETE CASCADE;
+ALTER TABLE ONLY packages_debian_group_distributions
+ ADD CONSTRAINT fk_rails_0adf75c347 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE RESTRICT;
+
ALTER TABLE ONLY packages_conan_file_metadata
ADD CONSTRAINT fk_rails_0afabd9328 FOREIGN KEY (package_file_id) REFERENCES packages_package_files(id) ON DELETE CASCADE;
@@ -24934,6 +25047,9 @@ ALTER TABLE ONLY alert_management_alert_assignees
ALTER TABLE ONLY scim_identities
ADD CONSTRAINT fk_rails_9421a0bffb FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+ALTER TABLE ONLY packages_debian_project_distributions
+ ADD CONSTRAINT fk_rails_94b95e1f84 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY packages_pypi_metadata
ADD CONSTRAINT fk_rails_9698717cdd FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
@@ -25294,6 +25410,9 @@ ALTER TABLE ONLY user_callouts
ALTER TABLE ONLY vulnerability_feedback
ADD CONSTRAINT fk_rails_debd54e456 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY packages_debian_project_distributions
+ ADD CONSTRAINT fk_rails_df44271a30 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE RESTRICT;
+
ALTER TABLE ONLY incident_management_oncall_shifts
ADD CONSTRAINT fk_rails_df4feb286a FOREIGN KEY (rotation_id) REFERENCES incident_management_oncall_rotations(id) ON DELETE CASCADE;
@@ -25369,6 +25488,9 @@ ALTER TABLE ONLY snippet_statistics
ALTER TABLE ONLY project_security_settings
ADD CONSTRAINT fk_rails_ed4abe1338 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY packages_debian_group_distributions
+ ADD CONSTRAINT fk_rails_ede0bb937f FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY experiment_subjects
ADD CONSTRAINT fk_rails_ede5754774 FOREIGN KEY (experiment_id) REFERENCES experiments(id) ON DELETE CASCADE;
diff --git a/doc/administration/reference_architectures/1k_users.md b/doc/administration/reference_architectures/1k_users.md
index 5b75c7ac9c4..161964353f5 100644
--- a/doc/administration/reference_architectures/1k_users.md
+++ b/doc/administration/reference_architectures/1k_users.md
@@ -16,7 +16,8 @@ requirements, a single-node solution with
many organizations .
> - **Supported users (approximate):** 1,000
-> - **High Availability:** No
+> - **High Availability:** No. For a highly-available environment, you can
+> follow the [3K reference architecture](3k_users.md).
| Users | Configuration | GCP | AWS | Azure |
|--------------|-------------------------|----------------|-----------------|----------------|
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index e664d55fa1b..3d38586fa62 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -12,7 +12,8 @@ For a full list of reference architectures, see
[Available reference architectures](index.md#available-reference-architectures).
> - **Supported users (approximate):** 2,000
-> - **High Availability:** No
+> - **High Availability:** No. For a highly-available environment, you can
+> follow the [3K reference architecture](3k_users.md).
> - **Test requests per second (RPS) rates:** API: 40 RPS, Web: 4 RPS, Git: 4 RPS
| Service | Nodes | Configuration | GCP | AWS | Azure |
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index e7b151452a5..648fac8025f 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -7,12 +7,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Reference architecture: up to 3,000 users **(PREMIUM ONLY)**
-This page describes GitLab reference architecture for up to 3,000 users. For a
-full list of reference architectures, see
-[Available reference architectures](index.md#available-reference-architectures).
-
-NOTE:
-This reference architecture is designed to help your organization achieve a
+This page describes GitLab reference architecture for up to 3,000 users.
+It is designed to help your organization achieve a
highly-available GitLab deployment. If you do not have the expertise or need to
maintain a highly-available environment, you can have a simpler and less
costly-to-operate environment by using the
@@ -20,6 +16,9 @@ costly-to-operate environment by using the
If you have fewer than 3,000 users and still want a highly-available GitLab deployment,
you should still use this reference architecture but scale down the node sizes.
+For a full list of reference architectures, see
+[Available reference architectures](index.md#available-reference-architectures).
+
> - **Supported users (approximate):** 3,000
> - **High Availability:** Yes
> - **Test requests per second (RPS) rates:** API: 60 RPS, Web: 6 RPS, Git: 6 RPS
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 71d75db6f23..302449a5e5b 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -18711,11 +18711,6 @@ type Project {
): TerraformStateConnection
"""
- Total pipeline duration for all of the pipelines in a project
- """
- totalPipelineDuration: Int
-
- """
Permissions for the current user on the resource
"""
userPermissions: ProjectPermissions!
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index bd646acfc5b..211f7b2e987 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -54461,20 +54461,6 @@
"deprecationReason": null
},
{
- "name": "totalPipelineDuration",
- "description": "Total pipeline duration for all of the pipelines in a project",
- "args": [
-
- ],
- "type": {
- "kind": "SCALAR",
- "name": "Int",
- "ofType": null
- },
- "isDeprecated": false,
- "deprecationReason": null
- },
- {
"name": "userPermissions",
"description": "Permissions for the current user on the resource",
"args": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 02db15bd603..90871111f8d 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -2690,7 +2690,6 @@ Autogenerated return type of PipelineRetry.
| `suggestionCommitMessage` | String | The commit message used to apply merge request suggestions |
| `tagList` | String | List of project topics (not Git tags) |
| `terraformStates` | TerraformStateConnection | Terraform states associated with the project |
-| `totalPipelineDuration` | Int | Total pipeline duration for all of the pipelines in a project |
| `userPermissions` | ProjectPermissions! | Permissions for the current user on the resource |
| `visibility` | String | Visibility of the project |
| `vulnerabilities` | VulnerabilityConnection | Vulnerabilities reported on the project |
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 539b20c3022..d6219a41660 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -412,9 +412,10 @@ include: '.gitlab-ci-production.yml'
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53903) in GitLab 11.7.
-To include files from another private project under the same GitLab instance,
-use `include:file`. This file is referenced with full paths relative to the
-root directory (`/`). For example:
+To include files from another private project on the same GitLab instance,
+use `include:file`. You can use `include:file` in combination with `include:project` only.
+
+The included file is referenced with a full path, relative to the root directory (`/`). For example:
```yaml
include:
diff --git a/doc/development/product_analytics/snowplow.md b/doc/development/product_analytics/snowplow.md
index 48b816f0b83..22da5a53c2f 100644
--- a/doc/development/product_analytics/snowplow.md
+++ b/doc/development/product_analytics/snowplow.md
@@ -478,6 +478,13 @@ For GitLab.com, we're setting up a [QA and Testing environment](https://gitlab.c
## Snowplow Schemas
+### [gitlab_standard](https://gitlab.com/gitlab-org/iglu/-/blob/master/public/schemas/com.gitlab/gitlab_standard/jsonschema/1-0-0) Schema
+
+| Field Name | Required | Type | Description |
+|--------------|---------------------|---------|--------------------------------|
+| project_id | **{dotted-circle}** | integer | ID of the associated project |
+| namespace_id | **{dotted-circle}** | integer | ID of the associated namespace |
+
### Default Schema
| Field Name | Required | Type | Description |
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index 30f467c2b12..c4fe522e6a2 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -145,6 +145,8 @@ git push --set-upstream git@gitlab.example.com:namespace/nonexistent-project.git
git push --set-upstream https://gitlab.example.com/namespace/nonexistent-project.git master
```
+You can pass the flag `--tags` to the `git push` command to export existing repository tags.
+
Once the push finishes successfully, a remote message indicates
the command to set the remote and the URL to the new project:
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index a0884461da1..eba1ed78d2b 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -664,6 +664,12 @@ request to add a new user to a project through API will not be possible.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1985) in [GitLab Ultimate and Gold](https://about.gitlab.com/pricing/) 12.0.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/215410) to [GitLab Premium and Silver](https://about.gitlab.com/pricing/) in 13.1.
+NOTE:
+IP Access Restrictions are currently not functioning as expected on GitLab.com. Some users
+may experience blocked Git operations or have difficulties accessing projects. Please
+review the [following bug report](https://gitlab.com/gitlab-org/gitlab/-/issues/271673) for
+more information.
+
To make sure only people from within your organization can access particular
resources, you have the option to restrict access to groups and their
underlying projects, issues, etc, by IP address. This can help ensure that
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 757aeaf2807..3f695e885f5 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -93,7 +93,7 @@ When SSO enforcement is enabled for a group, users can't share a project in the
## Providers
NOTE:
-GitLab is unable to provide full support for IdPs that are not listed here.
+GitLab is unable to provide full support for integrating identify providers that are not listed here.
| Provider | Documentation |
|----------|---------------|
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index a0de780eb19..7119970fca0 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -234,7 +234,7 @@ advanced functionality is present in [higher tiers only](https://about.gitlab.co
### Configurable issue boards **(STARTER)**
> - [Introduced](https://about.gitlab.com/releases/2017/11/22/gitlab-10-2-released/#issue-boards-configuration) in GitLab 10.2.
-> - Setting current iteration as scope [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196804) in GitLab 13.7.
+> - Setting current iteration as scope [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196804) in GitLab 13.8.
An issue board can be associated with a [milestone](milestones/index.md#milestones),
[labels](labels.md), assignee, weight, and current [iteration](../group/iterations/index.md),
diff --git a/doc/user/search/img/project_search_general_settings_v13_8.png b/doc/user/search/img/project_search_general_settings_v13_8.png
new file mode 100644
index 00000000000..08395e0d4f9
--- /dev/null
+++ b/doc/user/search/img/project_search_general_settings_v13_8.png
Binary files differ
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index 106306356bb..b6be21e9653 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -36,6 +36,10 @@ on the search field on the top-right of your screen:
### Filtering issue and merge request lists
+> - Filtering by Epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/195704) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
+> - Filtering by child Epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9029) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.0.
+> - Filtering by Iterations was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.6.
+
Follow these steps to filter the **Issues** and **Merge Requests** list pages within projects and
groups:
@@ -44,15 +48,12 @@ groups:
- Author
- Assignee
- [Milestone](../project/milestones/index.md)
- - [Iteration](../group/iterations/index.md) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.6)
+ - [Iteration](../group/iterations/index.md)
- Release
- [Label](../project/labels.md)
- My-reaction
- Confidential
- - Epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/195704) in GitLab 12.9),
- including [child epic](../group/epics/index.md#multi-level-child-epics)
- ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9029) in
- [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.0)
+ - [Epic and child Epic](../group/epics/index.md) (available only for the group the Epic was created, not for [higher group levels](https://gitlab.com/gitlab-org/gitlab/-/issues/233729)).
- Search for this text
1. Select or type the operator to use for filtering the attribute. The following operators are
available:
@@ -299,3 +300,39 @@ GitLab instance.
Use advanced queries for more targeted search results.
[Learn how to use the Advanced Search Syntax.](advanced_search_syntax.md)
+
+## Search project settings
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292941) in GitLab 13.8.
+> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default.
+> - It's disabled on GitLab.com.
+> - It's not recommended for production use.
+> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-search-project-settings). **(CORE ONLY)**
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+You can search inside the project’s settings sections by entering a search
+term in the search box located at the top of the page. The search results
+will appear highlighted in the sections that match the search term.
+
+![Search project settings](img/project_search_general_settings_v13_8.png)
+
+### Enable or disable Search project settings **(CORE ONLY)**
+
+Search project settings is under development and not ready for production use. It is
+deployed behind a feature flag that is **disabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
+can enable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:search_settings_in_page)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:search_settings_in_page)
+```
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 82f8874856b..d5e4f5e6200 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -33287,6 +33287,12 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has already been taken as Codename"
+msgstr ""
+
+msgid "has already been taken as Suite"
+msgstr ""
+
msgid "has been completed."
msgstr ""
diff --git a/spec/factories/packages/debian/distribution.rb b/spec/factories/packages/debian/distribution.rb
new file mode 100644
index 00000000000..2015f2923b8
--- /dev/null
+++ b/spec/factories/packages/debian/distribution.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :debian_project_distribution, class: 'Packages::Debian::ProjectDistribution' do
+ container { association(:project) }
+
+ sequence(:codename) { |n| "project-dist-#{n}" }
+
+ factory :debian_group_distribution, class: 'Packages::Debian::GroupDistribution' do
+ container { association(:group) }
+
+ sequence(:codename) { |n| "group-dist-#{n}" }
+ end
+
+ trait(:with_file) do
+ after(:build) do |distribution, evaluator|
+ distribution.file = fixture_file_upload('spec/fixtures/packages/debian/README.md')
+ end
+ end
+
+ trait(:object_storage) do
+ file_store { Packages::PackageFileUploader::Store::REMOTE }
+ end
+ end
+end
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 22d4362aaa9..6fdfe780463 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -76,13 +76,40 @@ RSpec.describe MergeRequestsFinder do
expect(merge_requests).to contain_exactly(merge_request3, merge_request4)
end
- it 'filters by commit sha' do
- merge_requests = described_class.new(
- user,
- commit_sha: merge_request5.merge_request_diff.last_commit_sha
- ).execute
+ context 'filters by commit sha' do
+ subject(:merge_requests) { described_class.new(user, commit_sha: commit_sha).execute }
+
+ context 'when commit belongs to the merge request' do
+ let(:commit_sha) { merge_request5.merge_request_diff.last_commit_sha }
+
+ it 'filters by commit sha' do
+ is_expected.to contain_exactly(merge_request5)
+ end
+ end
+
+ context 'when commit is a squash commit' do
+ before do
+ merge_request4.update!(squash_commit_sha: commit_sha)
+ end
+
+ let(:commit_sha) { '1234abcd' }
- expect(merge_requests).to contain_exactly(merge_request5)
+ it 'filters by commit sha' do
+ is_expected.to contain_exactly(merge_request4)
+ end
+ end
+
+ context 'when commit is a merge commit' do
+ before do
+ merge_request4.update!(merge_commit_sha: commit_sha)
+ end
+
+ let(:commit_sha) { '1234dcba' }
+
+ it 'filters by commit sha' do
+ is_expected.to contain_exactly(merge_request4)
+ end
+ end
end
context 'filters by merged_at date' do
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index b3028e034cc..9d0d7a3918a 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe GitlabSchema.types['Project'] do
container_expiration_policy service_desk_enabled service_desk_address
issue_status_counts terraform_states alert_management_integrations
container_repositories container_repositories_count
- pipeline_analytics total_pipeline_duration squash_read_only
+ pipeline_analytics squash_read_only
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 67768e2db8f..8798099f9f3 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -561,6 +561,7 @@ project:
- alert_management_http_integrations
- exported_protected_branches
- incident_management_oncall_schedules
+- debian_distributions
award_emoji:
- awardable
- user
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index cc8e744a15c..0acf2b96b74 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe Group do
it { is_expected.to have_one(:dependency_proxy_setting) }
it { is_expected.to have_many(:dependency_proxy_blobs) }
it { is_expected.to have_many(:dependency_proxy_manifests) }
+ it { is_expected.to have_many(:debian_distributions).class_name('Packages::Debian::GroupDistribution').dependent(:destroy) }
describe '#members & #requesters' do
let(:requester) { create(:user) }
@@ -1751,4 +1752,23 @@ RSpec.describe Group do
it { is_expected.to eq(false) }
end
end
+
+ describe 'with Debian Distributions' do
+ subject { create(:group) }
+
+ let!(:distributions) { create_list(:debian_group_distribution, 2, :with_file, container: subject) }
+
+ it 'removes distribution files on removal' do
+ distribution_file_paths = distributions.map do |distribution|
+ distribution.file.path
+ end
+
+ expect { subject.destroy }
+ .to change {
+ distribution_file_paths.select do |path|
+ File.exist? path
+ end.length
+ }.from(distribution_file_paths.length).to(0)
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 5c36622b1c6..d1f5a2c7077 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -411,6 +411,48 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
+ describe '.by_squash_commit_sha' do
+ subject { described_class.by_squash_commit_sha(sha) }
+
+ let(:sha) { '123abc' }
+ let(:merge_request) { create(:merge_request, :merged, squash_commit_sha: sha) }
+
+ it 'returns merge requests that match the given squash commit' do
+ is_expected.to eq([merge_request])
+ end
+ end
+
+ describe '.by_related_commit_sha' do
+ subject { described_class.by_related_commit_sha(sha) }
+
+ context 'when commit is a squash commit' do
+ let!(:merge_request) { create(:merge_request, :merged, squash_commit_sha: sha) }
+ let(:sha) { '123abc' }
+
+ it { is_expected.to eq([merge_request]) }
+ end
+
+ context 'when commit is a part of the merge request' do
+ let!(:merge_request) { create(:merge_request, :with_diffs) }
+ let(:sha) { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' }
+
+ it { is_expected.to eq([merge_request]) }
+ end
+
+ context 'when commit is a merge commit' do
+ let!(:merge_request) { create(:merge_request, :merged, merge_commit_sha: sha) }
+ let(:sha) { '123abc' }
+
+ it { is_expected.to eq([merge_request]) }
+ end
+
+ context 'when commit is not found' do
+ let(:sha) { '0000' }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe '.by_cherry_pick_sha' do
it 'returns merge requests that match the given merge commit' do
note = create(:track_mr_picking_note, commit_id: '456abc')
diff --git a/spec/models/packages/debian/group_distribution_spec.rb b/spec/models/packages/debian/group_distribution_spec.rb
new file mode 100644
index 00000000000..90fb0d0e7d8
--- /dev/null
+++ b/spec/models/packages/debian/group_distribution_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Debian::GroupDistribution do
+ it_behaves_like 'Debian Distribution', :debian_group_distribution, :group, false
+end
diff --git a/spec/models/packages/debian/project_distribution_spec.rb b/spec/models/packages/debian/project_distribution_spec.rb
new file mode 100644
index 00000000000..5f4041ad9fe
--- /dev/null
+++ b/spec/models/packages/debian/project_distribution_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Debian::ProjectDistribution do
+ it_behaves_like 'Debian Distribution', :debian_project_distribution, :project, true
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index d2b4bb9e95f..7c3eed14bcf 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -127,6 +127,7 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:reviews).inverse_of(:project) }
it { is_expected.to have_many(:packages).class_name('Packages::Package') }
it { is_expected.to have_many(:package_files).class_name('Packages::PackageFile') }
+ it { is_expected.to have_many(:debian_distributions).class_name('Packages::Debian::ProjectDistribution').dependent(:destroy) }
it { is_expected.to have_many(:pipeline_artifacts) }
it { is_expected.to have_many(:terraform_states).class_name('Terraform::State').inverse_of(:project) }
@@ -6489,6 +6490,25 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe 'with Debian Distributions' do
+ subject { create(:project) }
+
+ let!(:distributions) { create_list(:debian_project_distribution, 2, :with_file, container: subject) }
+
+ it 'removes distribution files on removal' do
+ distribution_file_paths = distributions.map do |distribution|
+ distribution.file.path
+ end
+
+ expect { subject.destroy }
+ .to change {
+ distribution_file_paths.select do |path|
+ File.exist? path
+ end.length
+ }.from(distribution_file_paths.length).to(0)
+ end
+ end
+
describe '#environments_for_scope' do
let_it_be(:project, reload: true) { create(:project) }
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index ca3225cab2c..48f56b3ec68 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe MergeRequests::CloseService do
describe '#execute' do
it_behaves_like 'cache counters invalidator'
+ it_behaves_like 'merge request reviewers cache counters invalidator'
context 'valid params' do
let(:service) { described_class.new(project, user, {}) }
diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb
index d7dba691da9..6523b5a158c 100644
--- a/spec/services/merge_requests/post_merge_service_spec.rb
+++ b/spec/services/merge_requests/post_merge_service_spec.rb
@@ -15,6 +15,7 @@ RSpec.describe MergeRequests::PostMergeService do
describe '#execute' do
it_behaves_like 'cache counters invalidator'
+ it_behaves_like 'merge request reviewers cache counters invalidator'
it 'refreshes the number of open merge requests for a valid MR', :use_clean_rails_memory_store_caching do
# Cache the counter before the MR changed state.
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index 9879e940747..8541d597581 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe MergeRequests::ReopenService do
describe '#execute' do
it_behaves_like 'cache counters invalidator'
+ it_behaves_like 'merge request reviewers cache counters invalidator'
context 'valid params' do
let(:service) { described_class.new(project, user, {}) }
diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
new file mode 100644
index 00000000000..f3dd0b1e374
--- /dev/null
+++ b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
@@ -0,0 +1,204 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
+ let_it_be(:distribution_with_suite, freeze: can_freeze) { create(factory, suite: 'mysuite') }
+ let_it_be(:distribution_with_same_container, freeze: can_freeze) { create(factory, container: distribution_with_suite.container ) }
+ let_it_be(:distribution_with_same_codename, freeze: can_freeze) { create(factory, codename: distribution_with_suite.codename ) }
+ let_it_be(:distribution_with_same_suite, freeze: can_freeze) { create(factory, suite: distribution_with_suite.suite ) }
+
+ let_it_be_with_refind(:distribution) { create(factory, container: distribution_with_suite.container ) }
+
+ subject { distribution }
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(container) }
+ it { is_expected.to belong_to(:creator).class_name('User') }
+ end
+
+ describe 'validations' do
+ describe "##{container}" do
+ it { is_expected.to validate_presence_of(container) }
+ end
+
+ describe "#creator" do
+ it { is_expected.not_to validate_presence_of(:creator) }
+ end
+
+ describe '#codename' do
+ it { is_expected.to validate_presence_of(:codename) }
+
+ it { is_expected.to allow_value('buster').for(:codename) }
+ it { is_expected.to allow_value('buster-updates').for(:codename) }
+ it { is_expected.to allow_value('Debian10.5').for(:codename) }
+ it { is_expected.not_to allow_value('jessie/updates').for(:codename) }
+ it { is_expected.not_to allow_value('hé').for(:codename) }
+ end
+
+ describe '#suite' do
+ it { is_expected.to allow_value(nil).for(:suite) }
+ it { is_expected.to allow_value('testing').for(:suite) }
+ it { is_expected.not_to allow_value('hé').for(:suite) }
+ end
+
+ describe '#unique_debian_suite_and_codename' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:with_existing_suite, :suite, :codename, :errors) do
+ false | nil | :keep | nil
+ false | 'testing' | :keep | nil
+ false | nil | :codename | ["Codename has already been taken"]
+ false | :codename | :keep | ["Suite has already been taken as Codename"]
+ false | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
+ true | nil | :keep | nil
+ true | 'testing' | :keep | nil
+ true | nil | :codename | ["Codename has already been taken"]
+ true | :codename | :keep | ["Suite has already been taken as Codename"]
+ true | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
+ true | nil | :suite | ["Codename has already been taken as Suite"]
+ true | :suite | :keep | ["Suite has already been taken"]
+ true | :suite | :suite | ["Suite has already been taken", "Codename has already been taken as Suite"]
+ end
+
+ with_them do
+ context factory do
+ let(:new_distribution) { build(factory, container: distribution.container) }
+
+ before do
+ distribution.update_column(:suite, 'suite-' + distribution.codename) if with_existing_suite
+
+ if suite.is_a?(Symbol)
+ new_distribution.suite = distribution.send suite unless suite == :keep
+ else
+ new_distribution.suite = suite
+ end
+
+ if codename.is_a?(Symbol)
+ new_distribution.codename = distribution.send codename unless codename == :keep
+ else
+ new_distribution.codename = codename
+ end
+ end
+
+ it do
+ if errors
+ expect(new_distribution).not_to be_valid
+ expect(new_distribution.errors.to_a).to eq(errors)
+ else
+ expect(new_distribution).to be_valid
+ end
+ end
+ end
+ end
+ end
+
+ describe '#origin' do
+ it { is_expected.to allow_value(nil).for(:origin) }
+ it { is_expected.to allow_value('Debian').for(:origin) }
+ it { is_expected.not_to allow_value('hé').for(:origin) }
+ end
+
+ describe '#label' do
+ it { is_expected.to allow_value(nil).for(:label) }
+ it { is_expected.to allow_value('Debian').for(:label) }
+ it { is_expected.not_to allow_value('hé').for(:label) }
+ end
+
+ describe '#version' do
+ it { is_expected.to allow_value(nil).for(:version) }
+ it { is_expected.to allow_value('10.6').for(:version) }
+ it { is_expected.not_to allow_value('hé').for(:version) }
+ end
+
+ describe '#description' do
+ it { is_expected.to allow_value(nil).for(:description) }
+ it { is_expected.to allow_value('Debian 10.6 Released 26 September 2020').for(:description) }
+ it { is_expected.to allow_value('Hé !').for(:description) }
+ end
+
+ describe '#valid_time_duration_seconds' do
+ it { is_expected.to allow_value(nil).for(:valid_time_duration_seconds) }
+ it { is_expected.to allow_value(24.hours.to_i).for(:valid_time_duration_seconds) }
+ it { is_expected.not_to allow_value(12.hours.to_i).for(:valid_time_duration_seconds) }
+ end
+
+ describe '#signing_keys' do
+ it { is_expected.to validate_absence_of(:signing_keys) }
+ end
+
+ describe '#file' do
+ it { is_expected.not_to validate_presence_of(:file) }
+ end
+
+ describe '#file_store' do
+ it { is_expected.to validate_presence_of(:file_store) }
+ end
+
+ describe '#file_signature' do
+ it { is_expected.to validate_absence_of(:file_signature) }
+ end
+ end
+
+ describe 'scopes' do
+ describe '.with_container' do
+ subject { described_class.with_container(distribution_with_suite.container) }
+
+ it 'does not return other distributions' do
+ expect(subject.to_a).to eq([distribution_with_suite, distribution, distribution_with_same_container])
+ end
+ end
+
+ describe '.with_codename' do
+ subject { described_class.with_codename(distribution_with_suite.codename) }
+
+ it 'does not return other distributions' do
+ expect(subject.to_a).to eq([distribution_with_suite, distribution_with_same_codename])
+ end
+ end
+
+ describe '.with_suite' do
+ subject { described_class.with_suite(distribution_with_suite.suite) }
+
+ it 'does not return other distributions' do
+ expect(subject.to_a).to eq([distribution_with_suite, distribution_with_same_suite])
+ end
+ end
+ end
+
+ describe '#needs_update?' do
+ subject { distribution.needs_update? }
+
+ context 'with new distribution' do
+ let(:distribution) { create(factory, container: distribution_with_suite.container) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'with file' do
+ context 'without valid_time_duration_seconds' do
+ let(:distribution) { create(factory, :with_file, container: distribution_with_suite.container) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'with valid_time_duration_seconds' do
+ let(:distribution) { create(factory, :with_file, container: distribution_with_suite.container, valid_time_duration_seconds: 2.days.to_i) }
+
+ context 'when not yet expired' do
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when expired' do
+ it do
+ distribution
+
+ travel_to(4.days.from_now) do
+ is_expected.to be_truthy
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/merge_request_shared_examples.rb b/spec/support/shared_examples/services/merge_request_shared_examples.rb
index 2bd06ac3e9c..56179b6cd00 100644
--- a/spec/support/shared_examples/services/merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/services/merge_request_shared_examples.rb
@@ -58,3 +58,18 @@ RSpec.shared_examples 'reviewer_ids filter' do
end
end
end
+
+RSpec.shared_examples 'merge request reviewers cache counters invalidator' do
+ let(:reviewer_1) { create(:user) }
+ let(:reviewer_2) { create(:user) }
+
+ before do
+ merge_request.update!(reviewers: [reviewer_1, reviewer_2])
+ end
+
+ it 'invalidates counter cache for reviewers' do
+ expect(merge_request.reviewers).to all(receive(:invalidate_merge_request_cache_counts))
+
+ described_class.new(project, user, {}).execute(merge_request)
+ end
+end
diff --git a/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb b/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb
new file mode 100644
index 00000000000..d36bfac4de8
--- /dev/null
+++ b/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Debian::DistributionReleaseFileUploader do
+ [:project, :group].each do |container_type|
+ context "Packages::Debian::#{container_type.capitalize}Distribution" do
+ let(:factory) { "debian_#{container_type}_distribution" }
+ let(:distribution) { create(factory, :with_file) }
+ let(:uploader) { described_class.new(distribution, :file) }
+ let(:path) { Gitlab.config.packages.storage_path }
+
+ subject { uploader }
+
+ it_behaves_like "builds correct paths",
+ store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_distribution/\d+$],
+ cache_dir: %r[/packages/tmp/cache$],
+ work_dir: %r[/packages/tmp/work$]
+
+ context 'object store is remote' do
+ before do
+ stub_package_file_object_storage
+ end
+
+ include_context 'with storage', described_class::Store::REMOTE
+
+ it_behaves_like "builds correct paths",
+ store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_distribution/\d+$],
+ cache_dir: %r[/packages/tmp/cache$],
+ work_dir: %r[/packages/tmp/work$]
+ end
+
+ describe 'remote file' do
+ let(:distribution) { create(factory, :with_file, :object_storage) }
+
+ context 'with object storage enabled' do
+ before do
+ stub_package_file_object_storage
+ end
+
+ it 'can store file remotely' do
+ allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
+
+ distribution
+
+ expect(distribution.file_store).to eq(described_class::Store::REMOTE)
+ expect(distribution.file.path).not_to be_blank
+ end
+ end
+ end
+ end
+ end
+end