summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_todo/lint/no_return_in_begin_end_blocks.yml1
-rw-r--r--.rubocop_todo/rails/negate_include.yml1
-rw-r--r--.rubocop_todo/style/format_string.yml1
-rw-r--r--.rubocop_todo/style/if_unless_modifier.yml1
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/controllers/profiles/chat_names_controller.rb7
-rw-r--r--app/controllers/projects/blob_controller.rb1
-rw-r--r--app/graphql/types/relative_position_type_enum.rb11
-rw-r--r--app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb15
-rw-r--r--app/models/integrations/mattermost_slash_commands.rb4
-rw-r--r--app/models/work_items/parent_link.rb4
-rw-r--r--app/services/git/branch_hooks_service.rb2
-rw-r--r--app/services/work_items/parent_links/base_service.rb53
-rw-r--r--app/services/work_items/parent_links/create_service.rb46
-rw-r--r--app/services/work_items/parent_links/reorder_service.rb39
-rw-r--r--app/services/work_items/widgets/hierarchy_service/update_service.rb60
-rw-r--r--app/views/profiles/chat_names/new.html.haml4
-rw-r--r--app/workers/jira_connect/sync_merge_request_worker.rb7
-rw-r--r--config/feature_flags/development/db_load_balance_audit_event_streaming_worker.yml8
-rw-r--r--config/feature_flags/development/explain_code.yml8
-rw-r--r--config/feature_flags/development/jira_include_keys_from_associated_mr_for_branch.yml8
-rw-r--r--data/removals/15_0/15-0-remove-background-upload-object-storage.yml4
-rw-r--r--db/post_migrate/20230403023828_swap_note_diff_files_note_id_to_bigint_for_gitlab_dot_com.rb68
-rw-r--r--db/post_migrate/20230404044338_drop_async_index_ci_job_artifacts_on_expire_at_for_removal.rb14
-rw-r--r--db/schema_migrations/202304030238281
-rw-r--r--db/schema_migrations/202304040443381
-rw-r--r--db/structure.sql9
-rw-r--r--doc/administration/job_artifacts.md4
-rw-r--r--doc/administration/lfs/index.md4
-rw-r--r--doc/administration/merge_request_diffs.md2
-rw-r--r--doc/administration/object_storage.md175
-rw-r--r--doc/administration/packages/dependency_proxy.md2
-rw-r--r--doc/administration/packages/index.md2
-rw-r--r--doc/administration/pages/index.md2
-rw-r--r--doc/administration/reference_architectures/10k_users.md6
-rw-r--r--doc/administration/reference_architectures/25k_users.md6
-rw-r--r--doc/administration/reference_architectures/2k_users.md6
-rw-r--r--doc/administration/reference_architectures/3k_users.md6
-rw-r--r--doc/administration/reference_architectures/50k_users.md6
-rw-r--r--doc/administration/reference_architectures/5k_users.md6
-rw-r--r--doc/administration/terraform_state.md2
-rw-r--r--doc/administration/uploads.md2
-rw-r--r--doc/api/graphql/reference/index.md11
-rw-r--r--doc/architecture/blueprints/object_storage/index.md2
-rw-r--r--doc/development/experiment_guide/index.md2
-rw-r--r--doc/integration/jira/configure.md2
-rw-r--r--doc/integration/saml.md4
-rw-r--r--doc/topics/git/index.md2
-rw-r--r--doc/topics/git/terminology.md4
-rw-r--r--doc/update/removals.md4
-rw-r--r--doc/user/analytics/index.md2
-rw-r--r--doc/user/application_security/terminology/index.md70
-rw-r--r--doc/user/gitlab_com/index.md2
-rw-r--r--doc/user/group/saml_sso/index.md2
-rw-r--r--doc/user/packages/maven_repository/index.md7
-rw-r--r--doc/user/project/import/index.md1
-rw-r--r--doc/user/project/settings/import_export.md103
-rw-r--r--lib/atlassian/jira_connect/serializers/branch_entity.rb5
-rw-r--r--lib/atlassian/jira_issue_key_extractors/branch.rb46
-rw-r--r--locale/gitlab.pot18
-rw-r--r--spec/features/profiles/chat_names_spec.rb16
-rw-r--r--spec/graphql/mutations/work_items/update_spec.rb21
-rw-r--r--spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb8
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb49
-rw-r--r--spec/lib/atlassian/jira_issue_key_extractors/branch_spec.rb65
-rw-r--r--spec/migrations/swap_note_diff_files_note_id_to_bigint_for_gitlab_dot_com_spec.rb66
-rw-r--r--spec/requests/api/graphql/mutations/work_items/update_spec.rb123
-rw-r--r--spec/services/work_items/parent_links/base_service_spec.rb31
-rw-r--r--spec/services/work_items/parent_links/create_service_spec.rb6
-rw-r--r--spec/services/work_items/parent_links/reorder_service_spec.rb176
-rw-r--r--spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb96
-rw-r--r--spec/workers/jira_connect/sync_merge_request_worker_spec.rb17
72 files changed, 1245 insertions, 327 deletions
diff --git a/.rubocop_todo/lint/no_return_in_begin_end_blocks.yml b/.rubocop_todo/lint/no_return_in_begin_end_blocks.yml
index 04115b12600..1d724ccc783 100644
--- a/.rubocop_todo/lint/no_return_in_begin_end_blocks.yml
+++ b/.rubocop_todo/lint/no_return_in_begin_end_blocks.yml
@@ -5,7 +5,6 @@ Lint/NoReturnInBeginEndBlocks:
- 'app/models/concerns/metric_image_uploading.rb'
- 'app/models/merge_request.rb'
- 'app/services/security/ci_configuration/sast_parser_service.rb'
- - 'app/services/work_items/parent_links/create_service.rb'
- 'ee/app/services/epic_issues/create_service.rb'
- 'ee/app/services/gitlab_subscriptions/preview_billable_user_change_service.rb'
- 'ee/app/services/security/token_revocation_service.rb'
diff --git a/.rubocop_todo/rails/negate_include.yml b/.rubocop_todo/rails/negate_include.yml
index 48a1e4483b9..c99f15f98e3 100644
--- a/.rubocop_todo/rails/negate_include.yml
+++ b/.rubocop_todo/rails/negate_include.yml
@@ -13,7 +13,6 @@ Rails/NegateInclude:
- 'app/models/merge_request.rb'
- 'app/models/milestone.rb'
- 'app/services/todo_service.rb'
- - 'app/services/work_items/parent_links/create_service.rb'
- 'config/application.rb'
- 'config/initializers/1_settings.rb'
- 'ee/app/finders/security/pipeline_vulnerabilities_finder.rb'
diff --git a/.rubocop_todo/style/format_string.yml b/.rubocop_todo/style/format_string.yml
index cb9ae6e9bd9..fc74f8fe075 100644
--- a/.rubocop_todo/style/format_string.yml
+++ b/.rubocop_todo/style/format_string.yml
@@ -145,7 +145,6 @@ Style/FormatString:
- 'app/services/system_notes/design_management_service.rb'
- 'app/services/timelogs/create_service.rb'
- 'app/services/users/banned_user_base_service.rb'
- - 'app/services/work_items/parent_links/create_service.rb'
- 'app/services/work_items/widgets/hierarchy_service/base_service.rb'
- 'app/validators/addressable_url_validator.rb'
- 'app/validators/any_field_validator.rb'
diff --git a/.rubocop_todo/style/if_unless_modifier.yml b/.rubocop_todo/style/if_unless_modifier.yml
index 8afc3f97aee..74c9637d68f 100644
--- a/.rubocop_todo/style/if_unless_modifier.yml
+++ b/.rubocop_todo/style/if_unless_modifier.yml
@@ -330,7 +330,6 @@ Style/IfUnlessModifier:
- 'app/services/users/build_service.rb'
- 'app/services/users/respond_to_terms_service.rb'
- 'app/services/wikis/create_attachment_service.rb'
- - 'app/services/work_items/parent_links/create_service.rb'
- 'app/services/work_items/task_list_reference_removal_service.rb'
- 'app/services/work_items/task_list_reference_replacement_service.rb'
- 'app/uploaders/file_mover.rb'
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 4f42e4f877a..3a8a46adab2 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-d642d1ef77564b21d24a0be42c1c12ed62d03b2b
+ccdfef925ac6fd2264d456f438faa0ca7adaffc2
diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb
index ae757c30d1c..564a84a0829 100644
--- a/app/controllers/profiles/chat_names_controller.rb
+++ b/app/controllers/profiles/chat_names_controller.rb
@@ -11,6 +11,7 @@ class Profiles::ChatNamesController < Profiles::ApplicationController
end
def new
+ @integration_name = integration_name
end
def create
@@ -65,4 +66,10 @@ class Profiles::ChatNamesController < Profiles::ApplicationController
def chat_names
@chat_names ||= current_user.chat_names
end
+
+ def integration_name
+ s_('Integrations|Mattermost slash commands')
+ end
end
+
+Profiles::ChatNamesController.prepend_mod
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index a5d18af993d..1be5363b464 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -49,6 +49,7 @@ class Projects::BlobController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:highlight_js, @project)
+ push_frontend_feature_flag(:explain_code, current_user)
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
end
diff --git a/app/graphql/types/relative_position_type_enum.rb b/app/graphql/types/relative_position_type_enum.rb
new file mode 100644
index 00000000000..e0d28bea648
--- /dev/null
+++ b/app/graphql/types/relative_position_type_enum.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ class RelativePositionTypeEnum < BaseEnum
+ graphql_name 'RelativePositionType'
+ description 'The position to which the object should be moved'
+
+ value 'BEFORE', 'Object is moved before an adjacent object.'
+ value 'AFTER', 'Object is moved after an adjacent object.'
+ end
+end
diff --git a/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb b/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb
index e1a9ebb76e9..297b06a8fab 100644
--- a/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb
+++ b/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb
@@ -6,16 +6,27 @@ module Types
class HierarchyUpdateInputType < BaseInputObject
graphql_name 'WorkItemWidgetHierarchyUpdateInput'
- argument :parent_id, ::Types::GlobalIDType[::WorkItem],
+ argument :adjacent_work_item_id,
+ ::Types::GlobalIDType[::WorkItem],
required: false,
loads: ::Types::WorkItemType,
- description: 'Global ID of the parent work item. Use `null` to remove the association.'
+ description: 'ID of the work item to be switched with.'
argument :children_ids, [::Types::GlobalIDType[::WorkItem]],
required: false,
description: 'Global IDs of children work items.',
loads: ::Types::WorkItemType,
as: :children
+
+ argument :parent_id, ::Types::GlobalIDType[::WorkItem],
+ required: false,
+ loads: ::Types::WorkItemType,
+ description: 'Global ID of the parent work item. Use `null` to remove the association.'
+
+ argument :relative_position,
+ Types::RelativePositionTypeEnum,
+ required: false,
+ description: 'Type of switch. Valid values are `BEFORE` or `AFTER`.'
end
end
end
diff --git a/app/models/integrations/mattermost_slash_commands.rb b/app/models/integrations/mattermost_slash_commands.rb
index f5079b9b907..e075400d9b5 100644
--- a/app/models/integrations/mattermost_slash_commands.rb
+++ b/app/models/integrations/mattermost_slash_commands.rb
@@ -15,11 +15,11 @@ module Integrations
end
def title
- 'Mattermost slash commands'
+ s_('Integrations|Mattermost slash commands')
end
def description
- "Perform common tasks with slash commands."
+ s_('Integrations|Perform common tasks with slash commands.')
end
def self.to_param
diff --git a/app/models/work_items/parent_link.rb b/app/models/work_items/parent_link.rb
index 21e31980fda..5dff9e8e8d5 100644
--- a/app/models/work_items/parent_link.rb
+++ b/app/models/work_items/parent_link.rb
@@ -41,6 +41,10 @@ module WorkItems
def relative_positioning_parent_column
:work_item_parent_id
end
+
+ def for_work_item(work_item)
+ find_or_initialize_by(work_item: work_item)
+ end
end
private
diff --git a/app/services/git/branch_hooks_service.rb b/app/services/git/branch_hooks_service.rb
index 6087efce9fd..2ead2e2a113 100644
--- a/app/services/git/branch_hooks_service.rb
+++ b/app/services/git/branch_hooks_service.rb
@@ -156,7 +156,7 @@ module Git
def enqueue_jira_connect_sync_messages
return unless project.jira_subscription_exists?
- branch_to_sync = branch_name if Atlassian::JiraIssueKeyExtractor.has_keys?(branch_name)
+ branch_to_sync = branch_name if Atlassian::JiraIssueKeyExtractors::Branch.has_keys?(project, branch_name)
commits_to_sync = limited_commits.select { |commit| Atlassian::JiraIssueKeyExtractor.has_keys?(commit.safe_message) }.map(&:sha)
if branch_to_sync || commits_to_sync.any?
diff --git a/app/services/work_items/parent_links/base_service.rb b/app/services/work_items/parent_links/base_service.rb
new file mode 100644
index 00000000000..6f22e09a3fc
--- /dev/null
+++ b/app/services/work_items/parent_links/base_service.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module WorkItems
+ module ParentLinks
+ class BaseService < IssuableLinks::CreateService
+ extend ::Gitlab::Utils::Override
+
+ private
+
+ def set_parent(issuable, work_item)
+ link = WorkItems::ParentLink.for_work_item(work_item)
+ link.work_item_parent = issuable
+ link
+ end
+
+ def create_notes(work_item)
+ SystemNoteService.relate_work_item(issuable, work_item, current_user)
+ end
+
+ def linkable_issuables(work_items)
+ @linkable_issuables ||= if can_admin_link?(issuable)
+ work_items.select { |work_item| linkable?(work_item) }
+ else
+ []
+ end
+ end
+
+ def linkable?(work_item)
+ can_admin_link?(work_item) && previous_related_issuables.exclude?(work_item)
+ end
+
+ def can_admin_link?(work_item)
+ can?(current_user, :admin_parent_link, work_item)
+ end
+
+ override :previous_related_issuables
+ def previous_related_issuables
+ @previous_related_issuables ||= issuable.work_item_children.to_a
+ end
+
+ override :target_issuable_type
+ def target_issuable_type
+ 'work item'
+ end
+
+ override :issuables_not_found_message
+ def issuables_not_found_message
+ format(_('No matching %{issuable} found. Make sure that you are adding a valid %{issuable} ID.'),
+ issuable: target_issuable_type)
+ end
+ end
+ end
+end
diff --git a/app/services/work_items/parent_links/create_service.rb b/app/services/work_items/parent_links/create_service.rb
index 85b470c47ca..60747daa5f8 100644
--- a/app/services/work_items/parent_links/create_service.rb
+++ b/app/services/work_items/parent_links/create_service.rb
@@ -2,59 +2,23 @@
module WorkItems
module ParentLinks
- class CreateService < IssuableLinks::CreateService
+ class CreateService < WorkItems::ParentLinks::BaseService
private
- # rubocop: disable CodeReuse/ActiveRecord
+ override :relate_issuables
def relate_issuables(work_item)
- link = WorkItems::ParentLink.find_or_initialize_by(work_item: work_item)
- link.work_item_parent = issuable
+ link = set_parent(issuable, work_item)
link.move_to_end
-
- if link.changed? && link.save
- create_notes(work_item)
- end
+ create_notes(work_item) if link.changed? && link.save
link
end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def linkable_issuables(work_items)
- @linkable_issuables ||= begin
- return [] unless can?(current_user, :admin_parent_link, issuable)
-
- work_items.select do |work_item|
- linkable?(work_item)
- end
- end
- end
-
- def linkable?(work_item)
- can?(current_user, :admin_parent_link, work_item) &&
- !previous_related_issuables.include?(work_item)
- end
-
- def previous_related_issuables
- @related_issues ||= issuable.work_item_children.to_a
- end
+ override :extract_references
def extract_references
params[:issuable_references]
end
-
- def create_notes(work_item)
- SystemNoteService.relate_work_item(issuable, work_item, current_user)
- end
-
- def target_issuable_type
- 'work item'
- end
-
- def issuables_not_found_message
- _('No matching %{issuable} found. Make sure that you are adding a valid %{issuable} ID.' %
- { issuable: target_issuable_type })
- end
end
end
end
diff --git a/app/services/work_items/parent_links/reorder_service.rb b/app/services/work_items/parent_links/reorder_service.rb
new file mode 100644
index 00000000000..0ee650bd8ab
--- /dev/null
+++ b/app/services/work_items/parent_links/reorder_service.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module WorkItems
+ module ParentLinks
+ class ReorderService < WorkItems::ParentLinks::BaseService
+ private
+
+ override :relate_issuables
+ def relate_issuables(work_item)
+ notes_are_expected = work_item.work_item_parent != issuable
+ link = set_parent(issuable, work_item)
+ reorder(link, params[:adjacent_work_item], params[:relative_position])
+
+ create_notes(work_item) if link.save && notes_are_expected
+
+ link
+ end
+
+ def reorder(link, adjacent_work_item, relative_position)
+ WorkItems::ParentLink.move_nulls_to_end(RelativePositioning.mover.context(link).relative_siblings)
+
+ link.move_before(adjacent_work_item.parent_link) if relative_position == 'BEFORE'
+ link.move_after(adjacent_work_item.parent_link) if relative_position == 'AFTER'
+ end
+
+ override :render_conflict_error?
+ def render_conflict_error?
+ return false if params[:adjacent_work_item] && params[:relative_position]
+
+ super
+ end
+
+ override :linkable?
+ def linkable?(work_item)
+ can_admin_link?(work_item)
+ end
+ end
+ end
+end
diff --git a/app/services/work_items/widgets/hierarchy_service/update_service.rb b/app/services/work_items/widgets/hierarchy_service/update_service.rb
index 48b540f919e..00b45c04ffa 100644
--- a/app/services/work_items/widgets/hierarchy_service/update_service.rb
+++ b/app/services/work_items/widgets/hierarchy_service/update_service.rb
@@ -4,10 +4,68 @@ module WorkItems
module Widgets
module HierarchyService
class UpdateService < WorkItems::Widgets::HierarchyService::BaseService
+ INVALID_RELATIVE_POSITION_ERROR = 'Relative position is not valid.'
+ CHILDREN_REORDERING_ERROR = 'Relative position cannot be combined with childrenIds.'
+ UNRELATED_ADJACENT_HIERARCHY_ERROR = 'The adjacent work item\'s parent must match the new parent work item.'
+ INVALID_ADJACENT_PARENT_ERROR = 'The adjacent work item\'s parent must match the current parent work item.'
+
def before_update_in_transaction(params:)
return unless params.present?
- service_response!(handle_hierarchy_changes(params))
+ if positioning?(params)
+ service_response!(handle_positioning(params))
+ else
+ service_response!(handle_hierarchy_changes(params))
+ end
+ end
+
+ private
+
+ def handle_positioning(params)
+ validate_positioning!(params)
+
+ arguments = {
+ target_issuable: work_item,
+ adjacent_work_item: params.delete(:adjacent_work_item),
+ relative_position: params.delete(:relative_position)
+ }
+ work_item_parent = params.delete(:parent) || work_item.work_item_parent
+ ::WorkItems::ParentLinks::ReorderService.new(work_item_parent, current_user, arguments).execute
+ end
+
+ def positioning?(params)
+ params[:relative_position].present? || params[:adjacent_work_item].present?
+ end
+
+ def error!(message)
+ service_response!(error(_(message)))
+ end
+
+ def validate_positioning!(params)
+ error!(INVALID_RELATIVE_POSITION_ERROR) if incomplete_relative_position?(params)
+ error!(CHILDREN_REORDERING_ERROR) if positioning_children?(params)
+ error!(UNRELATED_ADJACENT_HIERARCHY_ERROR) if unrelated_adjacent_hierarchy?(params)
+ error!(INVALID_ADJACENT_PARENT_ERROR) if invalid_adjacent_parent?(params)
+ end
+
+ def positioning_children?(params)
+ params.key?(:children)
+ end
+
+ def incomplete_relative_position?(params)
+ params[:adjacent_work_item].blank? || params[:relative_position].blank?
+ end
+
+ def unrelated_adjacent_hierarchy?(params)
+ return false if params[:parent].blank?
+
+ params[:parent] != params[:adjacent_work_item].work_item_parent
+ end
+
+ def invalid_adjacent_parent?(params)
+ return false if params[:parent].present?
+
+ work_item.work_item_parent != params[:adjacent_work_item].work_item_parent
end
end
end
diff --git a/app/views/profiles/chat_names/new.html.haml b/app/views/profiles/chat_names/new.html.haml
index 8ff2e6f34a0..bc30ccc5821 100644
--- a/app/views/profiles/chat_names/new.html.haml
+++ b/app/views/profiles/chat_names/new.html.haml
@@ -4,10 +4,10 @@
.gl-max-w-80.gl-mx-auto.gl-mt-6
= render Pajamas::CardComponent.new do |c|
- c.header do
- %h4.gl-m-0= s_('SlackIntegration|Authorize GitLab for Slack app (%{user}) to use your account?').html_safe % { user: @chat_name_params[:chat_name] }
+ %h4.gl-m-0= sprintf(s_('Integrations|Authorize %{integration_name} (%{user}) to use your account?'), { user: @chat_name_params[:chat_name], integration_name: @integration_name })
- c.body do
%p
- = s_('SlackIntegration|An application called GitLab for Slack app is requesting access to your GitLab account. This application was created by GitLab Inc.')
+ = sprintf(s_('Integrations|An application called %{integration_name} is requesting access to your GitLab account. This application was created by GitLab Inc.'), { integration_name: @integration_name })
%p
= _('This application will be able to:')
%ul
diff --git a/app/workers/jira_connect/sync_merge_request_worker.rb b/app/workers/jira_connect/sync_merge_request_worker.rb
index 6576aa9fdf4..76f62e55ab1 100644
--- a/app/workers/jira_connect/sync_merge_request_worker.rb
+++ b/app/workers/jira_connect/sync_merge_request_worker.rb
@@ -14,10 +14,13 @@ module JiraConnect
def perform(merge_request_id, update_sequence_id)
merge_request = MergeRequest.find_by_id(merge_request_id)
+ project = merge_request&.project
- return unless merge_request && merge_request.project
+ return unless merge_request && project
- JiraConnect::SyncService.new(merge_request.project).execute(merge_requests: [merge_request], update_sequence_id: update_sequence_id)
+ branches = [project.repository.find_branch(merge_request.source_branch)].compact
+
+ JiraConnect::SyncService.new(project).execute(merge_requests: [merge_request], branches: branches, update_sequence_id: update_sequence_id)
end
end
end
diff --git a/config/feature_flags/development/db_load_balance_audit_event_streaming_worker.yml b/config/feature_flags/development/db_load_balance_audit_event_streaming_worker.yml
deleted file mode 100644
index a7b60acefdf..00000000000
--- a/config/feature_flags/development/db_load_balance_audit_event_streaming_worker.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: db_load_balance_audit_event_streaming_worker
-introduced_by_url: https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1811
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/402493
-milestone: '15.11'
-type: development
-group: group::scalability
-default_enabled: false
diff --git a/config/feature_flags/development/explain_code.yml b/config/feature_flags/development/explain_code.yml
new file mode 100644
index 00000000000..5e64f842018
--- /dev/null
+++ b/config/feature_flags/development/explain_code.yml
@@ -0,0 +1,8 @@
+---
+name: explain_code
+introduced_by_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/404631
+milestone: '15.11'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/jira_include_keys_from_associated_mr_for_branch.yml b/config/feature_flags/development/jira_include_keys_from_associated_mr_for_branch.yml
new file mode 100644
index 00000000000..4fe66d8a220
--- /dev/null
+++ b/config/feature_flags/development/jira_include_keys_from_associated_mr_for_branch.yml
@@ -0,0 +1,8 @@
+---
+name: jira_include_keys_from_associated_mr_for_branch
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115448
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/398773
+milestone: '15.11'
+type: development
+group: group::integrations
+default_enabled: false
diff --git a/data/removals/15_0/15-0-remove-background-upload-object-storage.yml b/data/removals/15_0/15-0-remove-background-upload-object-storage.yml
index ca9fcc408ba..223e967073b 100644
--- a/data/removals/15_0/15-0-remove-background-upload-object-storage.yml
+++ b/data/removals/15_0/15-0-remove-background-upload-object-storage.yml
@@ -18,7 +18,7 @@
#### Encrypted S3 buckets
- Additionally, this also breaks the use of [encrypted S3 buckets](https://docs.gitlab.com/ee/administration/object_storage.html#encrypted-s3-buckets) with [storage-specific configuration form](https://docs.gitlab.com/ee/administration/object_storage.html#storage-specific-configuration).
+ Additionally, this also breaks the use of [encrypted S3 buckets](https://docs.gitlab.com/ee/administration/object_storage.html#encrypted-s3-buckets) with [storage-specific configuration form](https://docs.gitlab.com/ee/administration/object_storage.html#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form).
If your S3 buckets have [SSE-S3 or SSE-KMS encryption enabled](https://docs.aws.amazon.com/kms/latest/developerguide/services-s3.html), please [migrate your configuration to use consolidated object storage form](https://docs.gitlab.com/ee/administration/object_storage.html#transition-to-consolidated-form) before upgrading to GitLab 15.0. Otherwise, you may start getting `ETag mismatch` errors during objects upload.
@@ -36,7 +36,7 @@
If you have set a prefix, you can use a workaround to revert to background uploads:
- 1. Continue to use [storage-specific configuration](https://docs.gitlab.com/ee/administration/object_storage.html#storage-specific-configuration).
+ 1. Continue to use [storage-specific configuration](https://docs.gitlab.com/ee/administration/object_storage.html#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form).
1. In Omnibus GitLab, set the `GITLAB_LEGACY_BACKGROUND_UPLOADS` to re-enable background uploads:
```ruby
diff --git a/db/post_migrate/20230403023828_swap_note_diff_files_note_id_to_bigint_for_gitlab_dot_com.rb b/db/post_migrate/20230403023828_swap_note_diff_files_note_id_to_bigint_for_gitlab_dot_com.rb
new file mode 100644
index 00000000000..6a453722de9
--- /dev/null
+++ b/db/post_migrate/20230403023828_swap_note_diff_files_note_id_to_bigint_for_gitlab_dot_com.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+class SwapNoteDiffFilesNoteIdToBigintForGitlabDotCom < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::MigrationHelpers::ConvertToBigint
+
+ disable_ddl_transaction!
+
+ TABLE_NAME = 'note_diff_files'
+
+ def up
+ return unless should_run?
+
+ swap
+ end
+
+ def down
+ return unless should_run?
+
+ swap
+
+ add_concurrent_index TABLE_NAME, :diff_note_id_convert_to_bigint, unique: true,
+ name: 'index_note_diff_files_on_diff_note_id_convert_to_bigint'
+
+ add_concurrent_foreign_key TABLE_NAME, :notes, column: :diff_note_id_convert_to_bigint,
+ name: 'fk_note_diff_files_diff_note_id_convert_to_bigint',
+ on_delete: :cascade,
+ validate: false
+ end
+
+ def swap
+ # This will replace the existing index_note_diff_files_on_diff_note_id
+ add_concurrent_index TABLE_NAME, :diff_note_id_convert_to_bigint, unique: true,
+ name: 'index_note_diff_files_on_diff_note_id_convert_to_bigint'
+
+ # This will replace the existing fk_rails_3d66047aeb
+ add_concurrent_foreign_key TABLE_NAME, :notes, column: :diff_note_id_convert_to_bigint,
+ name: 'fk_note_diff_files_diff_note_id_convert_to_bigint',
+ on_delete: :cascade
+
+ with_lock_retries(raise_on_exhaustion: true) do
+ execute "LOCK TABLE notes, #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE"
+
+ execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN diff_note_id TO diff_note_id_tmp"
+ execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN diff_note_id_convert_to_bigint TO diff_note_id"
+ execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN diff_note_id_tmp TO diff_note_id_convert_to_bigint"
+
+ function_name = Gitlab::Database::UnidirectionalCopyTrigger
+ .on_table(TABLE_NAME, connection: connection)
+ .name(:diff_note_id, :diff_note_id_convert_to_bigint)
+ execute "ALTER FUNCTION #{quote_table_name(function_name)} RESET ALL"
+
+ # Swap defaults
+ change_column_default TABLE_NAME, :diff_note_id, nil
+ change_column_default TABLE_NAME, :diff_note_id_convert_to_bigint, 0
+
+ execute 'DROP INDEX IF EXISTS index_note_diff_files_on_diff_note_id'
+ rename_index TABLE_NAME, 'index_note_diff_files_on_diff_note_id_convert_to_bigint',
+ 'index_note_diff_files_on_diff_note_id'
+
+ execute "ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT IF EXISTS fk_rails_3d66047aeb"
+ rename_constraint(TABLE_NAME, 'fk_note_diff_files_diff_note_id_convert_to_bigint', 'fk_rails_3d66047aeb')
+ end
+ end
+
+ def should_run?
+ com_or_dev_or_test_but_not_jh?
+ end
+end
diff --git a/db/post_migrate/20230404044338_drop_async_index_ci_job_artifacts_on_expire_at_for_removal.rb b/db/post_migrate/20230404044338_drop_async_index_ci_job_artifacts_on_expire_at_for_removal.rb
new file mode 100644
index 00000000000..7c3cb65c884
--- /dev/null
+++ b/db/post_migrate/20230404044338_drop_async_index_ci_job_artifacts_on_expire_at_for_removal.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class DropAsyncIndexCiJobArtifactsOnExpireAtForRemoval < Gitlab::Database::Migration[2.1]
+ INDEX_NAME = 'index_ci_job_artifacts_on_expire_at_for_removal'
+
+ # TODO: Index to be destroyed synchronously in https://gitlab.com/gitlab-org/gitlab/-/issues/393913
+ def up
+ prepare_async_index_removal :ci_job_artifacts, :expire_at, name: INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index :ci_job_artifacts, :expire_at, name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20230403023828 b/db/schema_migrations/20230403023828
new file mode 100644
index 00000000000..60cc18c8eec
--- /dev/null
+++ b/db/schema_migrations/20230403023828
@@ -0,0 +1 @@
+1056fb290b3f5acf11dca0848258494d42c075dc7dc18ca89873d6237fc7104b \ No newline at end of file
diff --git a/db/schema_migrations/20230404044338 b/db/schema_migrations/20230404044338
new file mode 100644
index 00000000000..caa04406a17
--- /dev/null
+++ b/db/schema_migrations/20230404044338
@@ -0,0 +1 @@
+42d91918d6669bb02501657aeac062114ce6ce907baf690ab346e788c57a3324 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 89de87610b7..d26046b2788 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -18777,7 +18777,7 @@ ALTER SEQUENCE namespaces_sync_events_id_seq OWNED BY namespaces_sync_events.id;
CREATE TABLE note_diff_files (
id integer NOT NULL,
- diff_note_id integer NOT NULL,
+ diff_note_id_convert_to_bigint integer DEFAULT 0 NOT NULL,
diff text NOT NULL,
new_file boolean NOT NULL,
renamed_file boolean NOT NULL,
@@ -18786,7 +18786,7 @@ CREATE TABLE note_diff_files (
b_mode character varying NOT NULL,
new_path text NOT NULL,
old_path text NOT NULL,
- diff_note_id_convert_to_bigint bigint DEFAULT 0 NOT NULL
+ diff_note_id bigint NOT NULL
);
CREATE SEQUENCE note_diff_files_id_seq
@@ -31240,8 +31240,6 @@ CREATE INDEX index_non_requested_project_members_on_source_id_and_type ON member
CREATE UNIQUE INDEX index_note_diff_files_on_diff_note_id ON note_diff_files USING btree (diff_note_id);
-CREATE UNIQUE INDEX index_note_diff_files_on_diff_note_id_convert_to_bigint ON note_diff_files USING btree (diff_note_id_convert_to_bigint);
-
CREATE INDEX index_notes_for_cherry_picked_merge_requests ON notes USING btree (project_id, commit_id) WHERE ((noteable_type)::text = 'MergeRequest'::text);
CREATE INDEX index_notes_on_author_id_and_created_at_and_id ON notes USING btree (author_id, created_at, id);
@@ -35238,9 +35236,6 @@ ALTER TABLE ONLY ml_candidate_metrics
ALTER TABLE ONLY ml_candidate_params
ADD CONSTRAINT fk_ml_candidate_params_on_candidate_id FOREIGN KEY (candidate_id) REFERENCES ml_candidates(id) ON DELETE CASCADE;
-ALTER TABLE ONLY note_diff_files
- ADD CONSTRAINT fk_note_diff_files_diff_note_id_convert_to_bigint FOREIGN KEY (diff_note_id_convert_to_bigint) REFERENCES notes(id) ON DELETE CASCADE NOT VALID;
-
ALTER TABLE ONLY path_locks
ADD CONSTRAINT fk_path_locks_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 6547cb53b7c..55b033dd3e1 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -174,7 +174,7 @@ In a multi-server setup you must use one of the options to
[eliminate local disk usage for job logs](job_logs.md#prevent-local-disk-usage), or job logs could be lost.
In GitLab 13.2 and later, you should use the
-[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
+[consolidated object storage settings](object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
### Migrating to object storage
@@ -781,7 +781,7 @@ In both cases, you might need to add `region` to the job artifact [object storag
### Job artifact upload fails with `500 Internal Server Error (Missing file)`
-Bucket names that include folder paths are not supported with [consolidated object storage](object_storage.md#consolidated-object-storage-configuration).
+Bucket names that include folder paths are not supported with [consolidated object storage](object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
For example, `bucket/path`. If a bucket name has a path in it, you might receive an error similar to:
```plaintext
diff --git a/doc/administration/lfs/index.md b/doc/administration/lfs/index.md
index 3a4a7cad3b2..ee5cd04f3d6 100644
--- a/doc/administration/lfs/index.md
+++ b/doc/administration/lfs/index.md
@@ -156,14 +156,14 @@ You can store LFS objects in remote object storage. This allows you
to reduce reads and writes to the local disk, and free up disk space significantly.
In GitLab 13.2 and later, you should use the
-[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
+[consolidated object storage settings](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
### Migrating to object storage
You can migrate the LFS objects from local storage to object storage. The
processing is done in the background and requires **no downtime**.
-1. [Configure the object storage](../object_storage.md#consolidated-object-storage-configuration).
+1. [Configure the object storage](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
1. Migrate the LFS objects:
::Tabs
diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md
index 85677512860..3fbcdafd886 100644
--- a/doc/administration/merge_request_diffs.md
+++ b/doc/administration/merge_request_diffs.md
@@ -105,7 +105,7 @@ be configured already.
### Object Storage Settings
In GitLab 13.2 and later, you should use the
-[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
+[consolidated object storage settings](object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
This section describes the earlier configuration format.
For source installations, these settings are nested under `external_diffs:` and
diff --git a/doc/administration/object_storage.md b/doc/administration/object_storage.md
index 6f50f085927..848ee8de951 100644
--- a/doc/administration/object_storage.md
+++ b/doc/administration/object_storage.md
@@ -13,10 +13,10 @@ typically much more performant, reliable, and scalable.
To configure the object storage, you have two options:
-- Recommended. [Consolidated configuration](#consolidated-object-storage-configuration):
- A single credential is shared by all supported object types.
-- [Storage-specific configuration](#storage-specific-configuration): Every
- object defines its own object storage connection and configuration.
+- Recommended. [Configure a single storage connection for all object types](#configure-a-single-storage-connection-for-all-object-types-consolidated-form):
+ A single credential is shared by all supported object types. This is called the consolidated form.
+- [Configure each object type to define its own storage connection](#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form):
+ Every object defines its own object storage connection and configuration. This is called the storage-specific form.
If you already use the storage-specific form, see how to
[transition to the consolidated form](#transition-to-consolidated-form).
@@ -43,34 +43,37 @@ Specifically, GitLab has been tested by vendors and customers on a number of obj
- [MinIO](https://min.io/) (S3 compatible)
- On-premises hardware and appliances from various storage vendors, whose list is not officially established.
-## Consolidated object storage configuration
+## Configure a single storage connection for all object types (consolidated form)
> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4368) in GitLab 13.2.
-Using the consolidated object storage configuration has a number of advantages:
+Most types of objects, such as CI artifacts, LFS files, and upload attachments
+can be saved in object storage by specifying a single credential for object
+storage with multiple buckets.
+
+Configuring the object storage using the consolidated form has a number of advantages:
- It can simplify your GitLab configuration since the connection details are shared
across object types.
- It enables the use of [encrypted S3 buckets](#encrypted-s3-buckets).
- It [uploads files to S3 with proper `Content-MD5` headers](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/222).
-Because [direct upload mode](../development/uploads/index.md#direct-upload)
-must be enabled, only the following providers can be used:
+When the consolidated form is used,
+[direct upload](../development/uploads/index.md#direct-upload) is enabled
+automatically. Thus, only the following providers can be used:
- [Amazon S3-compatible providers](#amazon-s3)
- [Google Cloud Storage](#google-cloud-storage-gcs)
- [Azure Blob storage](#azure-blob-storage)
-When consolidated object storage is used, direct upload is enabled
-automatically. For storage-specific
-configuration, [direct upload may become the default](https://gitlab.com/gitlab-org/gitlab/-/issues/27331)
-because it does not require a shared folder.
-
-Consolidated object storage configuration can't be used for backups or
-Mattermost. See the [full table for a complete list](#storage-specific-configuration).
-However, backups can be configured with [server side encryption](../raketasks/backup_gitlab.md#s3-encrypted-buckets) separately.
+The consolidated form configuration can't be used for backups or
+Mattermost. Backups can be configured with
+[server side encryption](../raketasks/backup_gitlab.md#s3-encrypted-buckets)
+separately. See the
+[table for a complete list](#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form)
+of supported object storage types.
-Enabling consolidated object storage enables object storage for all object
+Enabling the consolidated form enables object storage for all object
types. If not all buckets are specified, you may see an error like:
```plaintext
@@ -80,24 +83,21 @@ Object storage for <object type> must have a bucket specified
If you want to use local storage for specific object types, you can
[disable object storage for specific features](#disable-object-storage-for-specific-features).
-Most types of objects, such as CI artifacts, LFS files, and upload
-attachments can be saved in object storage by specifying a single
-credential for object storage with multiple buckets.
+### Configure the common parameters
-When the consolidated form is:
+In the consolidated form, the `object_store` section defines a
+common set of parameters.
-- Used with an S3-compatible object storage, Workhorse uses its internal S3 client to
- upload files.
-- Not used with an S3-compatible object storage, Workhorse falls back to using
- pre-signed URLs.
-
-See the section on [ETag mismatch errors](#etag-mismatch) for more details.
-
-### Common parameters
+| Setting | Description |
+|-------------------|-----------------------------------|
+| `enabled` | Enable or disable object storage. |
+| `proxy_download` | Set to `true` to [enable proxying all files served](#proxy-download). Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data. |
+| `connection` | Various [connection options](#connection-settings) described below. |
+| `storage_options` | Options to use when saving new objects, such as [server side encryption](#server-side-encryption-headers). Introduced in GitLab 13.3. |
+| `objects` | [Object-specific configuration](#configure-the-parameters-of-each-object). |
-In the consolidated configuration, the `object_store` section defines a
-common set of parameters. Here we use the YAML from the source
-installation because it's easier to see the inheritance:
+The following YAML is from the source
+installation, to help you see the inheritance:
```yaml
object_store:
@@ -123,15 +123,30 @@ gitlab_rails['object_store']['connection'] = {
}
```
-| Setting | Description |
-|-------------------|-----------------------------------|
-| `enabled` | Enable or disable object storage. |
-| `proxy_download` | Set to `true` to [enable proxying all files served](#proxy-download). Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data. |
-| `connection` | Various [connection options](#connection-settings) described below. |
-| `storage_options` | Options to use when saving new objects, such as [server side encryption](#server-side-encryption-headers). Introduced in GitLab 13.3. |
-| `objects` | [Object-specific configuration](#object-specific-configuration). |
+### Configure the parameters of each object
+
+Each object type must at least define the bucket name where it will be stored.
+
+The following table lists the valid `objects` that can be used:
+
+| Type | Description |
+|--------------------|----------------------------------------------------------------------------|
+| `artifacts` | [CI artifacts](job_artifacts.md) |
+| `external_diffs` | [Merge request diffs](merge_request_diffs.md) |
+| `uploads` | [User uploads](uploads.md) |
+| `lfs` | [Git Large File Storage objects](lfs/index.md) |
+| `packages` | [Project packages (for example, PyPI, Maven, or NuGet)](packages/index.md) |
+| `dependency_proxy` | [Dependency Proxy](packages/dependency_proxy.md) |
+| `terraform_state` | [Terraform state files](terraform_state.md) |
+| `pages` | [Pages](pages/index.md) |
+
+Within each object type, three parameters can be defined:
-### Object-specific configuration
+| Setting | Required? | Description |
+|------------------|------------------------|-------------------------------------|
+| `bucket` | **{check-circle}** Yes\* | Bucket name for the object type. Not required if `enabled` is set to `false`. |
+| `enabled` | **{dotted-circle}** No | Overrides the [common parameter](#configure-the-common-parameters). |
+| `proxy_download` | **{dotted-circle}** No | Overrides the [common parameter](#configure-the-common-parameters). |
The following YAML shows how the `object_store` section defines
object-specific configuration block and how the `enabled` and
@@ -178,27 +193,6 @@ gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'terrafor
gitlab_rails['object_store']['objects']['pages']['bucket'] = 'pages'
```
-This is the list of valid `objects` that can be used:
-
-| Type | Description |
-|--------------------|----------------------------------------------------------------------------|
-| `artifacts` | [CI artifacts](job_artifacts.md) |
-| `external_diffs` | [Merge request diffs](merge_request_diffs.md) |
-| `uploads` | [User uploads](uploads.md) |
-| `lfs` | [Git Large File Storage objects](lfs/index.md) |
-| `packages` | [Project packages (for example, PyPI, Maven, or NuGet)](packages/index.md) |
-| `dependency_proxy` | [Dependency Proxy](packages/dependency_proxy.md) |
-| `terraform_state` | [Terraform state files](terraform_state.md) |
-| `pages` | [Pages](pages/index.md) |
-
-Within each object type, three parameters can be defined:
-
-| Setting | Required? | Description |
-|------------------|------------------------|-------------------------------------|
-| `bucket` | **{check-circle}** Yes | Bucket name for the object storage. |
-| `enabled` | **{dotted-circle}** No | Overrides the common parameter. |
-| `proxy_download` | **{dotted-circle}** No | Overrides the common parameter. |
-
#### Disable object storage for specific features
As seen above, object storage can be disabled for specific types by
@@ -216,33 +210,41 @@ no bucket is needed if CI artifacts are disabled with this setting:
gitlab_rails['artifacts_enabled'] = false
```
-## Storage-specific configuration
+## Configure each object type to define its own storage connection (storage-specific form)
+
+With the storage-specific form, every object defines its own object
+storage connection and configuration. If you're using GitLab 13.2 and later,
+you should [transition to the consolidated form](#transition-to-consolidated-form).
+
+The use of [encrypted S3 buckets](#encrypted-s3-buckets) with non-consolidated form is not supported.
+You may get [ETag mismatch errors](#etag-mismatch) if you use it.
+
+NOTE:
+For the storage-specific form,
+[direct upload may become the default](https://gitlab.com/gitlab-org/gitlab/-/issues/27331)
+because it does not require a shared folder.
For configuring object storage in GitLab 13.1 and earlier, or for storage types not
-supported by consolidated configuration form, refer to the following guides:
+supported by consolidated form, refer to the following guides:
-| Object storage type | Supported by consolidated configuration? |
+| Object storage type | Supported by consolidated form? |
|---------------------|------------------------------------------|
| [Backups](../raketasks/backup_gitlab.md#upload-backups-to-a-remote-cloud-storage) | **{dotted-circle}** No |
+| [Container Registry](packages/container_registry.md#use-object-storage) (optional feature) | **{dotted-circle}** No |
+| [Mattermost](https://docs.mattermost.com/configure/file-storage-configuration-settings.html)| **{dotted-circle}** No |
+| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | **{dotted-circle}** No |
| [Job artifacts](job_artifacts.md#using-object-storage) including archived job logs | **{check-circle}** Yes |
| [LFS objects](lfs/index.md#storing-lfs-objects-in-remote-object-storage) | **{check-circle}** Yes |
| [Uploads](uploads.md#using-object-storage) | **{check-circle}** Yes |
-| [Container Registry](packages/container_registry.md#use-object-storage) (optional feature) | **{dotted-circle}** No |
| [Merge request diffs](merge_request_diffs.md#using-object-storage) | **{check-circle}** Yes |
-| [Mattermost](https://docs.mattermost.com/configure/file-storage-configuration-settings.html)| **{dotted-circle}** No |
| [Packages](packages/index.md#use-object-storage) (optional feature) | **{check-circle}** Yes |
| [Dependency Proxy](packages/dependency_proxy.md#using-object-storage) (optional feature) | **{check-circle}** Yes |
-| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | **{dotted-circle}** No |
| [Terraform state files](terraform_state.md#using-object-storage) | **{check-circle}** Yes |
| [Pages content](pages/index.md#using-object-storage) | **{check-circle}** Yes |
-WARNING:
-The use of [encrypted S3 buckets](#encrypted-s3-buckets) with non-consolidated configuration is not supported.
-You may start getting [ETag mismatch errors](#etag-mismatch) if you use it.
-
## Connection settings
-Both consolidated configuration form and storage-specific configuration form must configure a connection. The following sections describe parameters that can be used
+Both consolidated and storage-specific form must configure a connection. The following sections describe parameters that can be used
in the `connection` setting.
### Amazon S3
@@ -348,9 +350,9 @@ Although Azure uses the word `container` to denote a collection of
blobs, GitLab standardizes on the term `bucket`. Be sure to configure
Azure container names in the `bucket` settings.
-Azure Blob storage can only be used with the [consolidated form](#consolidated-object-storage-configuration)
+Azure Blob storage can only be used with the [consolidated form](#configure-a-single-storage-connection-for-all-object-types-consolidated-form)
because a single set of credentials are used to access multiple
-containers. The [storage-specific form](#storage-specific-configuration)
+containers. The [storage-specific form](#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form)
is not supported. For more details, see [how to transition to consolidated form](#transition-to-consolidated-form).
The following are the valid connection parameters for Azure. For more information, see the
@@ -419,7 +421,7 @@ gitlab_rails['object_store']['connection'] = {
The signature version must be `2`. Using v4 results in a HTTP 411 Length Required error.
For more information, see [issue #4419](https://gitlab.com/gitlab-org/gitlab/-/issues/4419).
-## Full example using the consolidated object storage and Amazon S3
+## Full example using the consolidated form and Amazon S3
The following example uses AWS S3 to enable object storage for all supported services:
@@ -747,7 +749,7 @@ additional complexity and unnecessary redundancy. Since both GitLab
Rails and Workhorse components need access to object storage, the
consolidated form avoids excessive duplication of credentials.
-The consolidated object storage configuration is used _only_ if all lines from
+The consolidated form is used _only_ if all lines from
the original form is omitted. To move to the consolidated form, remove the
original configuration (for example, `artifacts_object_store_enabled`, or
`uploads_object_store_connection`)
@@ -769,10 +771,10 @@ address must be added to the `no_proxy` list.
### Encrypted S3 buckets
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/466) in GitLab 13.1 for instance profiles only and [S3 default encryption](https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encryption.html).
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34460) in GitLab 13.2 for static credentials when [consolidated object storage configuration](#consolidated-object-storage-configuration) and [S3 default encryption](https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encryption.html) are used.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34460) in GitLab 13.2 for static credentials when the [consolidated form](#configure-a-single-storage-connection-for-all-object-types-consolidated-form) and [S3 default encryption](https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encryption.html) is used.
When configured either with an instance profile or with the consolidated
-object configuration, GitLab Workhorse properly uploads files to S3
+form, GitLab Workhorse properly uploads files to S3
buckets that have [SSE-S3 or SSE-KMS encryption enabled by default](https://docs.aws.amazon.com/kms/latest/developerguide/services-s3.html).
Customer master keys (CMKs) and SSE-C encryption are
[not supported since this requires sending the encryption keys in every request](https://gitlab.com/gitlab-org/gitlab/-/issues/226006).
@@ -797,7 +799,7 @@ the Workhorse S3 client is enabled. One of the following two conditions
must be fulfilled:
- `use_iam_profile` is `true` in the connection settings.
-- Consolidated object storage settings are in use.
+- Consolidated form is in use.
[ETag mismatch errors](#etag-mismatch) occur if server side
encryption headers are used without enabling the Workhorse S3 client.
@@ -990,14 +992,14 @@ If you are seeing this ETag mismatch error with Amazon Web Services S3,
it's likely this is due to [encryption settings on your bucket](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html).
To fix this issue, you have two options:
-- [Use the consolidated object configuration](#consolidated-object-storage-configuration).
+- [Use the consolidated form](#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
- [Use Amazon instance profiles](#use-amazon-instance-profiles).
The first option is recommended for MinIO. Otherwise, the
[workaround for MinIO](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/1564#note_244497658)
is to use the `--compat` parameter on the server.
-Without consolidated object store configuration or instance profiles enabled,
+Without the consolidated form or instance profiles enabled,
GitLab Workhorse uploads files to S3 using pre-signed URLs that do
not have a `Content-MD5` HTTP header computed for them. To ensure data
is not corrupted, Workhorse checks that the MD5 hash of the data sent
@@ -1005,9 +1007,14 @@ equals the ETag header returned from the S3 server. When encryption is
enabled, this is not the case, which causes Workhorse to report an `ETag
mismatch` error during an upload.
-With the consolidated object configuration and instance profile, Workhorse has
-S3 credentials so that it can compute the `Content-MD5` header. This
-eliminates the need to compare ETag headers returned from the S3 server.
+When the consolidated form is:
+
+- Used with an S3-compatible object storage or an istance profile, Workhorse
+ uses its internal S3 client which has S3 credentials so that it can compute
+ the `Content-MD5` header. This eliminates the need to compare ETag headers
+ returned from the S3 server.
+- Not used with an S3-compatible object storage, Workhorse falls back to using
+ pre-signed URLs.
Encrypting buckets with the GCS [Cloud Key Management Service (KMS)](https://cloud.google.com/kms/docs) is not supported and results in ETag mismatch errors.
diff --git a/doc/administration/packages/dependency_proxy.md b/doc/administration/packages/dependency_proxy.md
index 78efd75bbe3..a29d6d93051 100644
--- a/doc/administration/packages/dependency_proxy.md
+++ b/doc/administration/packages/dependency_proxy.md
@@ -122,7 +122,7 @@ To change the local storage path:
Instead of relying on the local storage, you can use an object storage to
store the blobs of the Dependency Proxy. In GitLab 13.2 and later, you should use the
-[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
+[consolidated object storage settings](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
This section describes the earlier configuration format. [Migration steps still apply](#migrate-local-dependency-proxy-blobs-and-manifests-to-object-storage).
[Read more about using object storage with GitLab](../object_storage.md).
diff --git a/doc/administration/packages/index.md b/doc/administration/packages/index.md
index 6e53d77109f..f0f238aa5ba 100644
--- a/doc/administration/packages/index.md
+++ b/doc/administration/packages/index.md
@@ -124,7 +124,7 @@ Instead of relying on the local storage, you can use an object storage to store
packages.
For more information, see how to use the
-[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
+[consolidated object storage settings](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
### Migrate local packages to object storage
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 7ce588a6cb0..b4a786c7b33 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -916,7 +916,7 @@ If you want to stop using and disconnect the NFS server, you need to
#### S3-compatible connection settings
In GitLab 13.2 and later, you should use the
-[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
+[consolidated object storage settings](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
This section describes the earlier configuration format.
See [the available connection settings for different providers](../object_storage.md#connection-settings).
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index 09ba9838cf8..e70d012507f 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -2198,15 +2198,15 @@ and scalable.
There are two ways of specifying object storage configuration in GitLab:
-- [Consolidated form](../object_storage.md#consolidated-object-storage-configuration): A single credential is
+- [Consolidated form](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form): A single credential is
shared by all supported object types.
-- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
+- [Storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
The consolidated form is used in the following examples when available.
NOTE:
-When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
+When using the [storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
which was deprecated in 14.9, requires shared storage such as NFS.
diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index f55740bdf9d..19a79657218 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -2216,15 +2216,15 @@ and scalable.
There are two ways of specifying object storage configuration in GitLab:
-- [Consolidated form](../object_storage.md#consolidated-object-storage-configuration): A single credential is
+- [Consolidated form](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form): A single credential is
shared by all supported object types.
-- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
+- [Storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
The consolidated form is used in the following examples when available.
NOTE:
-When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
+When using the [storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
which was deprecated in 14.9, requires shared storage such as NFS.
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index 893d9eff21c..f31cf6f4ab6 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -902,15 +902,15 @@ and scalable.
There are two ways of specifying object storage configuration in GitLab:
-- [Consolidated form](../object_storage.md#consolidated-object-storage-configuration): A single credential is
+- [Consolidated form](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form): A single credential is
shared by all supported object types.
-- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
+- [Storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
The consolidated form is used in the following examples when available.
NOTE:
-When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
+When using the [storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
which was deprecated in 14.9, requires shared storage such as NFS.
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index 47d49ffb437..a01eaee10d3 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -2186,15 +2186,15 @@ and scalable.
There are two ways of specifying object storage configuration in GitLab:
-- [Consolidated form](../object_storage.md#consolidated-object-storage-configuration): A single credential is
+- [Consolidated form](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form): A single credential is
shared by all supported object types.
-- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
+- [Storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
The consolidated form is used in the following examples when available.
NOTE:
-When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
+When using the [storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
which was deprecated in 14.9, requires shared storage such as NFS.
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index b89effb730a..d4423ef4f9f 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -2215,15 +2215,15 @@ and scalable.
There are two ways of specifying object storage configuration in GitLab:
-- [Consolidated form](../object_storage.md#consolidated-object-storage-configuration): A single credential is
+- [Consolidated form](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form): A single credential is
shared by all supported object types.
-- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
+- [Storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
The consolidated form is used in the following examples when available.
NOTE:
-When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
+When using the [storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
which was deprecated in 14.9, requires shared storage such as NFS.
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index 5d6c650fe50..627812f5647 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -2181,15 +2181,15 @@ and scalable.
There are two ways of specifying object storage configuration in GitLab:
-- [Consolidated form](../object_storage.md#consolidated-object-storage-configuration): A single credential is
+- [Consolidated form](../object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form): A single credential is
shared by all supported object types.
-- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
+- [Storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
The consolidated form is used in the following examples when available.
NOTE:
-When using the [storage-specific form](../object_storage.md#storage-specific-configuration)
+When using the [storage-specific form](../object_storage.md#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form)
in GitLab 14.x and earlier, you should enable [direct upload mode](../../development/uploads/index.md#direct-upload).
The previous [background upload](../../development/uploads/index.md#direct-upload) mode,
which was deprecated in 14.9, requires shared storage such as NFS.
diff --git a/doc/administration/terraform_state.md b/doc/administration/terraform_state.md
index 7ab4359638d..9a54eb86eeb 100644
--- a/doc/administration/terraform_state.md
+++ b/doc/administration/terraform_state.md
@@ -175,7 +175,7 @@ sudo find /var/opt/gitlab/gitlab-rails/shared/terraform_state -type f | grep -v
### S3-compatible connection settings
In GitLab 13.2 and later, you should use the
-[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
+[consolidated object storage settings](object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
This section describes the earlier configuration format.
See [the available connection settings for different providers](object_storage.md#connection-settings).
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index c4e2c352e9b..e614f0d38e3 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -65,7 +65,7 @@ This configuration relies on valid AWS credentials to be configured already.
### Object Storage Settings
In GitLab 13.2 and later, you should use the
-[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
+[consolidated object storage settings](object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form).
This section describes the earlier configuration format.
For source installations the following settings are nested under `uploads:` and then `object_store:`. On Omnibus GitLab installs they are prefixed by `uploads_object_store_`.
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 5e81e08a961..b6a8cbae0da 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -24213,6 +24213,15 @@ State of a Geo registry.
| <a id="registrystatestarted"></a>`STARTED` | Registry currently syncing. |
| <a id="registrystatesynced"></a>`SYNCED` | Registry that is synced. |
+### `RelativePositionType`
+
+The position to which the object should be moved.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="relativepositiontypeafter"></a>`AFTER` | Object is moved after an adjacent object. |
+| <a id="relativepositiontypebefore"></a>`BEFORE` | Object is moved before an adjacent object. |
+
### `ReleaseAssetLinkType`
Type of the link: `other`, `runbook`, `image`, `package`.
@@ -26878,8 +26887,10 @@ A time-frame defined as a closed inclusive range of two dates.
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="workitemwidgethierarchyupdateinputadjacentworkitemid"></a>`adjacentWorkItemId` | [`WorkItemID`](#workitemid) | ID of the work item to be switched with. |
| <a id="workitemwidgethierarchyupdateinputchildrenids"></a>`childrenIds` | [`[WorkItemID!]`](#workitemid) | Global IDs of children work items. |
| <a id="workitemwidgethierarchyupdateinputparentid"></a>`parentId` | [`WorkItemID`](#workitemid) | Global ID of the parent work item. Use `null` to remove the association. |
+| <a id="workitemwidgethierarchyupdateinputrelativeposition"></a>`relativePosition` | [`RelativePositionType`](#relativepositiontype) | Type of switch. Valid values are `BEFORE` or `AFTER`. |
### `WorkItemWidgetIterationInput`
diff --git a/doc/architecture/blueprints/object_storage/index.md b/doc/architecture/blueprints/object_storage/index.md
index 5d83d5058a0..3f649960554 100644
--- a/doc/architecture/blueprints/object_storage/index.md
+++ b/doc/architecture/blueprints/object_storage/index.md
@@ -126,7 +126,7 @@ infrastructure. It also makes the initial installation more complex
feature after feature.
Implementing a direct upload by default, with a
-[consolidated object storage configuration](../../../administration/object_storage.md#consolidated-object-storage-configuration)
+[consolidated object storage configuration](../../../administration/object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form)
will reduce the number of merge requests needed to ship a new feature
from four to only one. It will also remove the need for SRE
intervention as the bucket will always be the same.
diff --git a/doc/development/experiment_guide/index.md b/doc/development/experiment_guide/index.md
index fd5b0ea15e6..5e68921510e 100644
--- a/doc/development/experiment_guide/index.md
+++ b/doc/development/experiment_guide/index.md
@@ -29,7 +29,7 @@ the documentation on that project if you want to dig into more advanced topics o
aware that the documentation there reflects what's in the main branch and may not be the same as
the version being used in GitLab.
-## Glossary of terms
+## Glossary
To ensure a shared language, you should understand these fundamental terms we use
when communicating about experiments:
diff --git a/doc/integration/jira/configure.md b/doc/integration/jira/configure.md
index 3ef4dfac3f4..c825b584b57 100644
--- a/doc/integration/jira/configure.md
+++ b/doc/integration/jira/configure.md
@@ -72,3 +72,5 @@ To migrate from Jira Server to Jira Cloud in GitLab and maintain your Jira integ
1. In **Password or API token**, paste the API token value.
1. Optional. Select **Test settings** to check if the connection is working.
1. Select **Save changes**.
+
+To update existing Jira issue references in GitLab to use the new Jira site URL, you must [invalidate the Markdown cache](../../administration/invalidate_markdown_cache.md#invalidate-the-cache).
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index 16c69a65a8c..2fdace32553 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -19,7 +19,7 @@ To set up SAML on GitLab.com, see [SAML SSO for GitLab.com groups](../user/group
For more information on:
- OmniAuth provider settings, see the [OmniAuth documentation](omniauth.md).
-- Commonly-used terms, see the [glossary of common terms](#glossary-of-common-terms).
+- Commonly-used terms, see the [glossary](#glossary).
## Configure SAML support in GitLab
@@ -3125,7 +3125,7 @@ such as the following:
For example configurations, see the [notes on specific providers](#set-up-identity-providers).
-## Glossary of common terms
+## Glossary
| Term | Description |
|--------------------------------|-------------|
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index fa802952b32..91c3f1971f1 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -30,7 +30,7 @@ The following resources can help you get started with Git:
- [Git Basics](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics)
- [Git on the Server - GitLab](https://git-scm.com/book/en/v2/Git-on-the-Server-GitLab)
- [How to install Git](how_to_install_git/index.md)
-- [Git terminology](terminology.md)
+- [Git concepts](terminology.md)
- [Start using Git on the command line](../../gitlab-basics/start-using-git.md)
- [GitLab Git Cheat Sheet (download)](https://about.gitlab.com/images/press/git-cheat-sheet.pdf)
- Commits:
diff --git a/doc/topics/git/terminology.md b/doc/topics/git/terminology.md
index ac097396bef..8b1db5b4416 100644
--- a/doc/topics/git/terminology.md
+++ b/doc/topics/git/terminology.md
@@ -4,9 +4,9 @@ group: Source Code
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
---
-# Git terminology
+# Git concepts
-The following are commonly-used Git terms.
+The following are commonly-used Git concepts.
## Repository
diff --git a/doc/update/removals.md b/doc/update/removals.md
index 6874ab9fe2e..843604b8a18 100644
--- a/doc/update/removals.md
+++ b/doc/update/removals.md
@@ -247,7 +247,7 @@ If your object storage provider does not support `background_upload`, please [mi
#### Encrypted S3 buckets
-Additionally, this also breaks the use of [encrypted S3 buckets](https://docs.gitlab.com/ee/administration/object_storage.html#encrypted-s3-buckets) with [storage-specific configuration form](https://docs.gitlab.com/ee/administration/object_storage.html#storage-specific-configuration).
+Additionally, this also breaks the use of [encrypted S3 buckets](https://docs.gitlab.com/ee/administration/object_storage.html#encrypted-s3-buckets) with [storage-specific configuration form](https://docs.gitlab.com/ee/administration/object_storage.html#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form).
If your S3 buckets have [SSE-S3 or SSE-KMS encryption enabled](https://docs.aws.amazon.com/kms/latest/developerguide/services-s3.html), please [migrate your configuration to use consolidated object storage form](https://docs.gitlab.com/ee/administration/object_storage.html#transition-to-consolidated-form) before upgrading to GitLab 15.0. Otherwise, you may start getting `ETag mismatch` errors during objects upload.
@@ -265,7 +265,7 @@ Some users found that they could specify a path prefix to the bucket. In direct
If you have set a prefix, you can use a workaround to revert to background uploads:
-1. Continue to use [storage-specific configuration](https://docs.gitlab.com/ee/administration/object_storage.html#storage-specific-configuration).
+1. Continue to use [storage-specific configuration](https://docs.gitlab.com/ee/administration/object_storage.html#configure-each-object-type-to-define-its-own-storage-connection-storage-specific-form).
1. In Omnibus GitLab, set the `GITLAB_LEGACY_BACKGROUND_UPLOADS` to re-enable background uploads:
```ruby
diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md
index 774063810f3..44af9dc9dea 100644
--- a/doc/user/analytics/index.md
+++ b/doc/user/analytics/index.md
@@ -59,7 +59,7 @@ You can use the following analytics features to analyze and visualize the perfor
- [Value stream analytics for groups](../group/value_stream_analytics/index.md)
- [Value streams dashboard](value_streams_dashboard.md)
-## Definitions
+## Glossary
We use the following terms to describe GitLab analytics:
diff --git a/doc/user/application_security/terminology/index.md b/doc/user/application_security/terminology/index.md
index 772a7d17e1e..df103976901 100644
--- a/doc/user/application_security/terminology/index.md
+++ b/doc/user/application_security/terminology/index.md
@@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference
---
-# Secure and Govern terminology **(FREE)**
+# Secure and Govern glossary **(FREE)**
The glossary of terms aims to achieve the following:
@@ -17,9 +17,7 @@ The glossary of terms aims to achieve the following:
The definitions of the terms outlined in this document are in the context of the GitLab
products. Therefore, a term may have a different meaning to users outside of GitLab.
-## Terms
-
-### Analyzer
+## Analyzer
Software that performs a scan. The scan analyzes an attack surface for vulnerabilities and produces
a report containing findings. Reports adhere to the [Secure report format](#secure-report-format).
@@ -31,35 +29,35 @@ manage found vulnerabilities. For more information, see [Security Scanner Integr
Many GitLab analyzers follow a standard approach using Docker to run a wrapped scanner. For example,
the image `semgrep` is an analyzer that wraps the scanner `Semgrep`.
-### Attack surface
+## Attack surface
The different places in an application that are vulnerable to attack. Secure products discover and
search the attack surface during scans. Each product defines the attack surface differently. For
example, SAST uses files and line numbers, and DAST uses URLs.
-### Corpus
+## Corpus
The set of meaningful test cases that are generated while the fuzzer is running. Each meaningful
test case produces new coverage in the tested program. It's advised to re-use the corpus and pass it
to subsequent runs.
-### CNA
+## CNA
[CVE](#cve) Numbering Authorities (CNAs) are organizations from around the world that are authorized by
the [Mitre Corporation](https://cve.mitre.org/) to assign [CVE](#cve)s to vulnerabilities in products or
services within their respective scope. [GitLab is a CNA](https://about.gitlab.com/security/cve/).
-### CVE
+## CVE
Common Vulnerabilities and Exposures (CVE®) is a list of common identifiers for publicly known
cybersecurity vulnerabilities. The list is managed by the [Mitre Corporation](https://cve.mitre.org/).
-### CVSS
+## CVSS
The Common Vulnerability Scoring System (CVSS) is a free and open industry standard for assessing
the severity of computer system security vulnerabilities.
-### CWE
+## CWE
Common Weakness Enumeration (CWEâ„¢) is a community-developed list of common software and hardware
weakness types that have security ramifications. Weaknesses are flaws, faults, bugs,
@@ -68,22 +66,22 @@ architecture. If left unaddressed, weaknesses could result in systems, networks,
vulnerable to attack. The CWE List and associated classification taxonomy serve as a language that
you can use to identify and describe these weaknesses in terms of CWEs.
-### Deduplication
+## Deduplication
When a category's process deems findings to be the same, or if they are similar enough that a noise reduction is
required, only one finding is kept and the others are eliminated. Read more about the [deduplication process](../vulnerability_report/pipeline.md#deduplication-process).
-### Duplicate finding
+## Duplicate finding
A legitimate finding that is reported multiple times. This can occur when different scanners
discover the same finding, or when a single scan inadvertently reports the same finding more than
once.
-### False positive
+## False positive
A finding that doesn't exist but is incorrectly reported as existing.
-### Finding
+## Finding
An asset that has the potential to be vulnerable, identified in a project by an analyzer. Assets
include but are not restricted to source code, binary packages, containers, dependencies, networks,
@@ -96,32 +94,32 @@ You can interact with vulnerability findings in two ways.
1. You can open an issue or merge request for the vulnerability finding.
1. You can dismiss the vulnerability finding. Dismissing the finding hides it from the default views.
-### Grouping
+## Grouping
A flexible and non-destructive way to visually organize vulnerabilities in groups when there are multiple findings
that are likely related but do not qualify for deduplication. For example, you can include findings that should be
evaluated together, would be fixed by the same action, or come from the same source. Grouping behavior for vulnerabilities is
under development and tracked in issue [267588](https://gitlab.com/gitlab-org/gitlab/-/issues/267588).
-### Insignificant finding
+## Insignificant finding
A legitimate finding that a particular customer doesn't care about.
-### Location fingerprint
+## Location fingerprint
A finding's location fingerprint is a text value that's unique for each location on the attack
surface. Each security product defines this according to its type of attack surface. For example, SAST
incorporates file path and line number.
-### Package managers and package types
+## Package managers and package types
-#### Package managers
+### Package managers
A package manager is a system that manages your project dependencies.
The package manager provides a method to install new dependencies (also referred to as "packages"), manage where packages are stored on your file system, and offer capabilities for you to publish your own packages.
-#### Package types
+### Package types
Each package manager, platform, type, or ecosystem has its own conventions and protocols to identify, locate, and provision software packages.
@@ -215,11 +213,11 @@ table.package-managers-and-types ul {
</tbody>
</table>
-### Pipeline Security tab
+## Pipeline Security tab
A page that displays findings discovered in the associated CI pipeline.
-### Post-filter
+## Post-filter
Post-filters help reduce noise in the scanner results and automate manual tasks. You can specify criteria that updates
or modifies vulnerability data based on scanner results. For example, you can flag findings as likely False Positives
@@ -228,7 +226,7 @@ and automatically resolve vulnerabilities that are no longer detected. These are
Support for automatically resolving findings is tracked in epic [7478](https://gitlab.com/groups/gitlab-org/-/epics/7478) and
support for cheap scan is proposed in issue [349926](https://gitlab.com/gitlab-org/gitlab/-/issues/349926).
-### Pre-filter
+## Pre-filter
An irreversible action that is done to filter out targets before analysis occurs. This is usually provided to allow
the user to reduce scope and noise as well as speed up the analysis. This should not be done if a record is needed as
@@ -236,7 +234,7 @@ we currently do not store anything related to the skipped/excluded code or asset
Examples: `DS_EXCLUDED_PATHS` should `Exclude files and directories from the scan based on the paths provided.`
-### Primary identifier
+## Primary identifier
A finding's primary identifier is a value that is unique to each finding. The external type and external ID
of the finding's [first identifier](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/v2.4.0-rc1/dist/sast-report-format.json#L228)
@@ -246,13 +244,13 @@ Examples of primary identifiers include `PluginID` for OWASP Zed Attack Proxy (Z
Trivy. The identifier must be stable. Subsequent scans must return the same value for the
same finding, even if the location has slightly changed.
-### Report finding
+## Report finding
A [finding](#finding) that only exists in a report produced by an analyzer, and is yet to be
persisted to the database. The report finding becomes a [vulnerability finding](#vulnerability-finding)
once it's imported into the database.
-### Scan type (report type)
+## Scan type (report type)
Describes the type of scan. This must be one of the following:
@@ -266,12 +264,12 @@ Describes the type of scan. This must be one of the following:
This list is subject to change as scanners are added.
-### Scanner
+## Scanner
Software that can scan for vulnerabilities. The resulting scan report is typically not in the
[Secure report format](#secure-report-format). Examples include ESLint, Trivy, and ZAP.
-### Secure product
+## Secure product
A group of features related to a specific area of application security with first-class support by
GitLab.
@@ -281,23 +279,23 @@ Testing (DAST), Secret Detection, Static Application Security Testing (SAST), an
Each of these products typically include one or more analyzers.
-### Secure report format
+## Secure report format
A standard report format that Secure products comply with when creating JSON reports. The format is described by a
[JSON schema](https://gitlab.com/gitlab-org/security-products/security-report-schemas).
-### Security Dashboard
+## Security Dashboard
Provides an overview of all the vulnerabilities for a project, group, or GitLab instance.
Vulnerabilities are only created from findings discovered on the project's default branch.
-### Seed corpus
+## Seed corpus
The set of test cases given as initial input to the fuzz target. This usually speeds up the fuzz
target substantially. This can be either manually created test cases or auto-generated with the fuzz
target itself from previous runs.
-### Vendor
+## Vendor
The party maintaining an analyzer. As such, a vendor is responsible for integrating a scanner into
GitLab and keeping it compatible as they evolve. A vendor isn't necessarily the author or maintainer
@@ -305,7 +303,7 @@ of the scanner, as in the case of using an open core or OSS project as a base so
offering. For scanners included as part of a GitLab distribution or GitLab subscription, the vendor
is listed as GitLab.
-### Vulnerability
+## Vulnerability
A flaw that has a negative impact on the security of its environment. Vulnerabilities describe the
error or weakness, and don't describe where the error is located (see [finding](#finding)).
@@ -314,12 +312,12 @@ Each vulnerability maps to a unique finding.
Vulnerabilities exist in the default branch. Findings (see [finding](#finding)) are all potential vulnerability items scanners identify in MRs/feature branches. Only after merging to default does a finding become a vulnerability.
-### Vulnerability finding
+## Vulnerability finding
When a [report finding](#report-finding) is stored to the database, it becomes a vulnerability
[finding](#finding).
-### Vulnerability tracking
+## Vulnerability tracking
Deals with the responsibility of matching findings across scans so that a finding's life cycle can
be understood. Engineers and security teams use this information to decide whether to merge code
@@ -327,6 +325,6 @@ changes, and to see unresolved findings and when they were introduced.
Vulnerabilities are tracked by comparing the location fingerprint, primary identifier, and report type.
-### Vulnerability occurrence
+## Vulnerability occurrence
Deprecated, see [finding](#finding).
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index fe7fff648d8..b36d92f1682 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -189,7 +189,7 @@ the default value [is the same as for self-managed instances](../admin_area/sett
| Setting | GitLab.com default |
|-------------------------------|--------------------|
| [Repository size including LFS](../admin_area/settings/account_and_limit_settings.md#repository-size-limit) | 10 GB |
-| [Maximum import size](../project/settings/import_export.md#maximum-import-file-size) | 5 GB |
+| [Maximum import size](../project/settings/import_export.md#import-a-project-and-its-data) | 5 GB |
| Maximum attachment size | 100 MB |
If you are near or over the repository size limit, you can either
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index d1f8722b9dc..a76eca4ffac 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -515,7 +515,7 @@ immediately. If the user:
## Related topics
- [SAML SSO for self-managed GitLab instances](../../../integration/saml.md)
-- [Glossary of common terms](../../../integration/saml.md#glossary-of-common-terms)
+- [Glossary](../../../integration/saml.md#glossary)
- [Authentication comparison between SaaS and self-managed](../../../administration/auth/index.md#saas-vs-self-managed-comparison)
- [Passwords for users created through integrated authentication](../../../security/passwords_for_integrated_authentication_methods.md)
diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md
index 29de1940b63..7a451ca283f 100644
--- a/doc/user/packages/maven_repository/index.md
+++ b/doc/user/packages/maven_repository/index.md
@@ -66,6 +66,13 @@ The three endpoints are:
- **Group-level**: Use when you want to install packages from many different projects in the same GitLab group. GitLab does not guarantee the uniqueness of package names within the group. You can have two projects with the same package name and package version. As a result, GitLab serves whichever one is more recent.
- **Instance-level**: Use when you have many packages in different GitLab groups or in their own namespace.
+For the instance-level endpoint, ensure the relevant section of your `pom.xml` in Maven looks like this:
+
+```xml
+ <groupId>group-slug.subgroup-slug</groupId>
+ <artifactId>project-slug</artifactId>
+```
+
**Only packages that have the same path as the project** are exposed by the instance-level endpoint.
| Project | Package | Instance-level endpoint available |
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index f0d75895e6e..5be7c49b5c2 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -167,6 +167,7 @@ GitLab from:
For more information, see:
+- Information on paid GitLab [migration services](https://about.gitlab.com/services/migration/).
- [Quick Start](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/blob/master/docs/using-congregate.md#quick-start).
- [Frequently Asked Migration Questions](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/blob/master/customer/famq.md),
including settings that need checking afterwards and other limitations.
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index eeeeace6dd2..85ef3dc8405 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -7,22 +7,56 @@ info: "To determine the technical writer assigned to the Stage/Group associated
# Migrating projects using file exports **(FREE)**
Existing projects on any self-managed GitLab instance or GitLab.com can be exported to a file and
-then imported into a new GitLab instance.
+then imported into another GitLab instance. You can also copy GitLab projects to another location with more automation by
+[migrating groups by direct transfer](../../group/import/index.md#migrate-groups-by-direct-transfer-recommended).
-GitLab maps user contributions correctly when an admin access token is used to perform the import.
+## Preserving user contributions
-Consequently, migrating projects using file exports does not map user contributions correctly when you are importing
-projects from a self-managed instance to GitLab.com.
+Preserving user contribution depends on meeting the following requirements:
-Instead, all GitLab user associations (such as comment author) are changed to the user importing the project. For more
-information, see the prerequisites and important notes in these sections:
+### Migrating from GitLab self-managed to GitLab.com
-- [Export a project and its data](../settings/import_export.md#export-a-project-and-its-data).
-- [Import the project](../settings/import_export.md#import-a-project-and-its-data).
+When migrating projects by using file exports, an administrator's access token is required for user contributions to map correctly.
-To preserve contribution history, [migrate using direct transfer](../../group/import/index.md#migrate-groups-by-direct-transfer-recommended).
+Therefore, user contributions never map correctly when importing file exports from a self-managed instance to GitLab.com. Instead, all GitLab user associations (such as
+comment author) are changed to the user importing the project. To preserve contribution history, do one of the following:
-If you migrate from GitLab.com to self-managed GitLab, an administrator can create users on the self-managed GitLab instance.
+- [Migrate by direct transfer](../../group/import/index.md#migrate-groups-by-direct-transfer-recommended).
+- Consider paid GitLab [migration services](https://about.gitlab.com/services/migration/).
+
+### Migrating to GitLab self-managed
+
+To ensure GitLab maps users and their contributions correctly:
+
+- The owner of the project's top-level group should export the project so that the information of all members (direct and inherited) with access to the project can be included in the exported file. Project maintainers and owners can initiate the project export. However, only direct members of a project are then exported.
+- An administrator must perform the import with an administrator access token.
+- Required users must exist on the destination GitLab instance. An administrator can create confirmed users either in bulk in a Rails console or one by one in the UI.
+- Users must [set a public email in their profiles](../../profile/index.md#set-your-public-email) on the source GitLab instance that matches their primary email
+ address on the destination GitLab instance. You can also manually add users' public emails by
+ [editing project export files](#edit-project-export-files).
+
+When the email of an existing user matches the email of an imported user, that user is added as a [direct member](../members/index.md) to the imported project.
+
+If any of the previous conditions are not met, user contributions are not mapped correctly. Instead, all GitLab user associations are changed to the user who performed the import.
+That user becomes an author of merge requests created by other users. Supplementary comments mentioning original authors are:
+
+- Added for comments, merge request approvals, linked tasks, and items.
+- Not added for the merge request or issue creator, added or removed labels, and merged-by information.
+
+## Edit project export files
+
+You can add or remove data from export files. For example, you can:
+
+- Manually add users public emails to the `project_members.ndjson` file.
+- Trim CI pipelines by removing lines from the `ci_pipelines.ndjson` file.
+
+To edit a project export file:
+
+1. Extract the exported `.tar.gz` file.
+1. Edit the appropriate file . For example, `tree/project/project_members.ndjson`.
+1. Compress the files back to a `.tar.gz` file.
+
+You can also make sure that all members were exported by checking the `project_members.ndjson` file.
## Compatibility
@@ -75,8 +109,6 @@ Prerequisites:
- Review the list of [items that are exported](#items-that-are-exported). Not all items are exported.
- You must have at least the Maintainer role for the project.
-- For the user mapping to work correctly, users must [set a public email](../../profile/index.md#set-your-public-email)
- in the source GitLab instance that matches their verified primary email in the target GitLab instance.
To export a project and its data, follow these steps:
@@ -94,15 +126,13 @@ moved to your configured `uploads_directory`. Every 24 hours, a worker deletes t
### Items that are exported
-The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml)
-file for projects lists many of the items exported and imported when migrating projects using file exports. View this file in the branch
-for your version of GitLab to see the list of items relevant to you. For example,
-[`import_export.yml` on the `14-10-stable-ee` branch](https://gitlab.com/gitlab-org/gitlab/-/blob/14-10-stable-ee/lib/gitlab/import_export/project/import_export.yml).
+Exported project items depend on the version of GitLab you use. To determine if a
+specific project item is exported:
-Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../../group/custom_project_templates.md) and
-[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same.
+1. Check the [`exporters` array](https://gitlab.com/gitlab-org/gitlab/blob/0a60d6dcfa7b809cf4fe0c2e239f406014d92e34/app/services/projects/import_export/export_service.rb#L25-28).
+1. Check the [`project/import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml) file for projects for your GitLab version (for example, `<https://gitlab.com/gitlab-org/gitlab/-/blob/15-9-stable-ee/lib/gitlab/import_export/project/import_export.yml>` for GitLab 15.9).
-Items that are exported include:
+For a quick overview, items that are exported include:
- Project and wiki repositories
- Project uploads
@@ -139,22 +169,25 @@ Items that are exported include:
Items that are **not** exported include:
- [Child pipeline history](https://gitlab.com/gitlab-org/gitlab/-/issues/221088)
+- Pipeline triggers
- Build traces and artifacts
- Package and container registry images
- CI/CD variables
-- Pipeline triggers
- Webhooks
- Any encrypted tokens
- [Number of required approvals](https://gitlab.com/gitlab-org/gitlab/-/issues/221087)
- Repository size limits
- Deploy keys allowed to push to protected branches
-- Secure Files
+- Secure files
- [Activity logs for Git-related events](https://gitlab.com/gitlab-org/gitlab/-/issues/214700) (for example, pushing and creating tags)
- Security policies associated with your project
+Migrating projects with file exports uses the same export and import mechanisms as creating projects from templates at the [group](../../group/custom_project_templates.md) and
+[instance](../../admin_area/custom_project_templates.md) levels. Therefore, the list of exported items is the same.
+
## Import a project and its data
-> Default maximum import file size [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50 MB to unlimited in GitLab 13.8.
+> Default maximum import file size [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50 MB to unlimited in GitLab 13.8. Administrators of self-managed instances can [set maximum import file size](#set-maximum-import-file-size). On GitLab.com, the value is [set to 5 GB](../../gitlab_com/index.md#account-and-limit-settings).
WARNING:
Only import projects from sources you trust. If you import a project from an untrusted source, it
@@ -197,12 +230,7 @@ Deploy keys aren't imported. To use deploy keys, you must enable them in your im
If you have a larger project, consider [using a Rake task](../../../administration/raketasks/project_import_export.md#import-large-projects).
-## Automate group and project import **(PREMIUM)**
-
-For information on automating user, group, and project import API calls, see
-[Automate group and project import](../import/index.md#automate-group-and-project-import).
-
-## Maximum import file size
+## Set maximum import file size **(FREE SELF)**
Administrators can set the maximum import file size one of two ways:
@@ -211,25 +239,6 @@ Administrators can set the maximum import file size one of two ways:
The default is `0` (unlimited).
-For the GitLab.com setting, see the [Account and limit settings](../../gitlab_com/index.md#account-and-limit-settings)
-section of the GitLab.com settings page.
-
-## Map users for import
-
-Imported users can be mapped by their public email addresses on self-managed instances, if an administrator (not an owner) does the import.
-
-- The project must be exported by a project or group member with the Owner role.
-- Public email addresses are not set by default. Users must [set it in their profiles](../../profile/index.md#set-your-public-email)
- for mapping to work correctly.
-- For contributions to be mapped correctly, users must be an existing member of the namespace,
- or they can be added as a member of the project. Otherwise, a supplementary comment is left to mention that the original
- author and the merge requests, notes, or issues that are owned by the importer.
-- Imported users are set as [direct members](../members/index.md)
- in the imported project.
-
-For project migration imports performed over GitLab.com groups, preserving author information is
-possible through a [professional services engagement](https://about.gitlab.com/services/migration/).
-
## Rate limits
To help avoid abuse, by default, users are rate limited to:
diff --git a/lib/atlassian/jira_connect/serializers/branch_entity.rb b/lib/atlassian/jira_connect/serializers/branch_entity.rb
index c663575b7a8..682b2d77102 100644
--- a/lib/atlassian/jira_connect/serializers/branch_entity.rb
+++ b/lib/atlassian/jira_connect/serializers/branch_entity.rb
@@ -7,14 +7,13 @@ module Atlassian
expose :id do |branch|
Digest::SHA256.hexdigest(branch.name)
end
- expose :issueKeys do |branch|
- JiraIssueKeyExtractor.new(branch.name).issue_keys
+ expose :issueKeys do |branch, options|
+ JiraIssueKeyExtractors::Branch.new(options[:project], branch.name).issue_keys
end
expose :name
expose :lastCommit, using: JiraConnect::Serializers::CommitEntity do |branch, options|
options[:project].commit(branch.dereferenced_target)
end
-
expose :url do |branch, options|
project_commits_url(options[:project], branch.name)
end
diff --git a/lib/atlassian/jira_issue_key_extractors/branch.rb b/lib/atlassian/jira_issue_key_extractors/branch.rb
new file mode 100644
index 00000000000..67105cd093b
--- /dev/null
+++ b/lib/atlassian/jira_issue_key_extractors/branch.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Atlassian
+ module JiraIssueKeyExtractors
+ class Branch
+ def self.has_keys?(project, branch_name)
+ new(project, branch_name).issue_keys.any?
+ end
+
+ def initialize(project, branch_name)
+ @project = project
+ @branch_name = branch_name
+ end
+
+ # Extract Jira issue keys from the branch name and associated open merge request.
+ # Use BatchLoader to load this data without N+1 queries when serializing multiple branches
+ # in `Atlassian::JiraConnect::Serializers::BranchEntity`.
+ def issue_keys
+ unless Feature.enabled?(:jira_include_keys_from_associated_mr_for_branch, project)
+ return JiraIssueKeyExtractor.new(branch_name).issue_keys
+ end
+
+ BatchLoader.for(branch_name).batch do |branch_names, loader|
+ merge_requests = MergeRequest
+ .select(:description, :source_branch, :title)
+ .from_project(project)
+ .from_source_branches(branch_names)
+ .opened
+
+ branch_names.each do |branch_name|
+ related_merge_request = merge_requests.find { |mr| mr.source_branch == branch_name }
+
+ key_sources = [branch_name, related_merge_request&.title, related_merge_request&.description].compact
+ issue_keys = JiraIssueKeyExtractor.new(key_sources).issue_keys
+
+ loader.call(branch_name, issue_keys)
+ end
+ end
+ end
+
+ private
+
+ attr_reader :branch_name, :project
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e0692a37116..8ae1865dbff 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -23209,12 +23209,18 @@ msgstr ""
msgid "Integrations|All projects inheriting these settings will also be reset."
msgstr ""
+msgid "Integrations|An application called %{integration_name} is requesting access to your GitLab account. This application was created by GitLab Inc."
+msgstr ""
+
msgid "Integrations|An error occurred while loading projects using custom settings."
msgstr ""
msgid "Integrations|An event will be triggered when one of the following items happen."
msgstr ""
+msgid "Integrations|Authorize %{integration_name} (%{user}) to use your account?"
+msgstr ""
+
msgid "Integrations|Branches for which notifications are to be sent"
msgstr ""
@@ -23338,6 +23344,9 @@ msgstr ""
msgid "Integrations|Linked namespaces"
msgstr ""
+msgid "Integrations|Mattermost slash commands"
+msgstr ""
+
msgid "Integrations|Namespace successfully linked"
msgstr ""
@@ -23353,6 +23362,9 @@ msgstr ""
msgid "Integrations|Notification settings"
msgstr ""
+msgid "Integrations|Perform common tasks with slash commands."
+msgstr ""
+
msgid "Integrations|Projects using custom settings"
msgstr ""
@@ -41166,15 +41178,9 @@ msgstr ""
msgid "Slack notifications will be deprecated"
msgstr ""
-msgid "SlackIntegration|An application called GitLab for Slack app is requesting access to your GitLab account. This application was created by GitLab Inc."
-msgstr ""
-
msgid "SlackIntegration|Are you sure you want to remove this project from the GitLab for Slack app?"
msgstr ""
-msgid "SlackIntegration|Authorize GitLab for Slack app (%{user}) to use your account?"
-msgstr ""
-
msgid "SlackIntegration|Client ID"
msgstr ""
diff --git a/spec/features/profiles/chat_names_spec.rb b/spec/features/profiles/chat_names_spec.rb
index 9e1bd69a239..105d7d4ec16 100644
--- a/spec/features/profiles/chat_names_spec.rb
+++ b/spec/features/profiles/chat_names_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Profile > Chat', feature_category: :user_profile do
+RSpec.describe 'Profile > Chat', feature_category: :integrations do
let_it_be(:user) { create(:user) }
before do
@@ -11,7 +11,12 @@ RSpec.describe 'Profile > Chat', feature_category: :user_profile do
describe 'uses authorization link' do
let(:params) do
- { team_id: 'T00', team_domain: 'my_chat_team', user_id: 'U01', user_name: 'my_chat_user' }
+ {
+ team_id: 'f1924a8db44ff3bb41c96424cdc20676',
+ team_domain: 'my_chat_team',
+ user_id: 'ay5sq51sebfh58ktrce5ijtcwy',
+ user_name: 'my_chat_user'
+ }
end
let!(:authorize_url) { ChatNames::AuthorizeUserService.new(params).execute }
@@ -21,6 +26,13 @@ RSpec.describe 'Profile > Chat', feature_category: :user_profile do
visit authorize_path
end
+ it 'names the integration correctly' do
+ expect(page).to have_content(
+ 'An application called Mattermost slash commands is requesting access to your GitLab account'
+ )
+ expect(page).to have_content('Authorize Mattermost slash commands')
+ end
+
context 'clicks authorize' do
before do
click_button 'Authorize'
diff --git a/spec/graphql/mutations/work_items/update_spec.rb b/spec/graphql/mutations/work_items/update_spec.rb
new file mode 100644
index 00000000000..3acb06346a4
--- /dev/null
+++ b/spec/graphql/mutations/work_items/update_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::WorkItems::Update, feature_category: :portfolio_management do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } }
+ let_it_be(:current_work_item) { create(:work_item, :task, project: project) }
+ let_it_be(:parent_work_item) { create(:work_item, project: project) }
+
+ subject(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
+
+ describe '#ready?' do
+ let(:current_user) { developer }
+ let(:current_gid) { current_work_item.to_gid.to_s }
+ let(:parent_gid) { parent_work_item.to_gid.to_s }
+ let(:valid_arguments) { { id: current_gid, parent_id: parent_gid } }
+
+ it { is_expected.to be_ready(**valid_arguments) }
+ end
+end
diff --git a/spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb b/spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb
index 6221580605e..0d4d31faee1 100644
--- a/spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb
+++ b/spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb
@@ -5,5 +5,11 @@ require 'spec_helper'
RSpec.describe ::Types::WorkItems::Widgets::HierarchyUpdateInputType do
it { expect(described_class.graphql_name).to eq('WorkItemWidgetHierarchyUpdateInput') }
- it { expect(described_class.arguments.keys).to match_array(%w[parentId childrenIds]) }
+ it 'accepts documented arguments' do
+ expect(described_class.arguments.keys).to match_array(%w[parentId childrenIds adjacentWorkItemId relativePosition])
+ end
+
+ it 'sets the type of relative_position argument to RelativePositionTypeEnum' do
+ expect(described_class.arguments['relativePosition'].type).to eq(Types::RelativePositionTypeEnum)
+ end
end
diff --git a/spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb
index 86e48a4a0fd..230908ccea1 100644
--- a/spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb
+++ b/spec/lib/atlassian/jira_connect/serializers/branch_entity_spec.rb
@@ -3,7 +3,10 @@
require 'spec_helper'
RSpec.describe Atlassian::JiraConnect::Serializers::BranchEntity, feature_category: :integrations do
- let(:project) { create(:project, :repository) }
+ include AfterNextHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+
let(:branch) { project.repository.find_branch('improve/awesome') }
subject { described_class.represent(branch, project: project).as_json }
@@ -11,4 +14,48 @@ RSpec.describe Atlassian::JiraConnect::Serializers::BranchEntity, feature_catego
it 'sets the hash of the branch name as the id' do
expect(subject[:id]).to eq('bbfba9b197ace5da93d03382a7ce50081ae89d99faac1f2326566941288871ce')
end
+
+ describe '#issue_keys' do
+ it 'calls Atlassian::JiraIssueKeyExtractors::Branch#issue_keys' do
+ expect_next(Atlassian::JiraIssueKeyExtractors::Branch) do |extractor|
+ expect(extractor).to receive(:issue_keys)
+ end
+
+ subject
+ end
+
+ it 'avoids N+1 queries when fetching merge requests for multiple branches' do
+ master_branch = project.repository.find_branch('master')
+
+ create(
+ :merge_request,
+ source_project: project,
+ source_branch: 'improve/awesome',
+ title: 'OPEN_MR_TITLE-1',
+ description: 'OPEN_MR_DESC-1'
+ )
+
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) { subject }
+
+ create(
+ :merge_request,
+ source_project: project,
+ source_branch: 'master',
+ title: 'MASTER_MR_TITLE-1',
+ description: 'MASTER_MR_DESC-1'
+ )
+
+ expect(subject).to include(
+ name: 'improve/awesome',
+ issueKeys: match_array(%w[OPEN_MR_TITLE-1 OPEN_MR_DESC-1])
+ )
+
+ expect do
+ expect(described_class.represent([branch, master_branch], project: project).as_json).to contain_exactly(
+ hash_including(name: 'improve/awesome', issueKeys: match_array(%w[BRANCH-1 OPEN_MR_TITLE-1 OPEN_MR_DESC-1])),
+ hash_including(name: 'master', issueKeys: match_array(%w[MASTER_MR_TITLE-1 MASTER_MR_DESC-1]))
+ )
+ end.not_to exceed_query_limit(control)
+ end
+ end
end
diff --git a/spec/lib/atlassian/jira_issue_key_extractors/branch_spec.rb b/spec/lib/atlassian/jira_issue_key_extractors/branch_spec.rb
new file mode 100644
index 00000000000..1d5a87f770a
--- /dev/null
+++ b/spec/lib/atlassian/jira_issue_key_extractors/branch_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Atlassian::JiraIssueKeyExtractors::Branch, feature_category: :integrations do
+ include AfterNextHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:branch) { project.repository.find_branch('improve/awesome') }
+
+ describe '.has_keys?' do
+ it 'delegates to `#issue_keys?`' do
+ expect_next(described_class) do |instance|
+ expect(instance).to receive_message_chain(:issue_keys, :any?)
+ end
+
+ described_class.has_keys?(project, branch.name)
+ end
+ end
+
+ describe '#issue_keys' do
+ subject { described_class.new(project, branch.name).issue_keys }
+
+ context 'when branch name does not refer to an issue' do
+ it { is_expected.to eq([]) }
+ end
+
+ context 'when branch name refers to an issue' do
+ before do
+ allow(branch).to receive(:name).and_return('BRANCH-1')
+ end
+
+ it { is_expected.to eq(['BRANCH-1']) }
+
+ context 'when there is a related open merge request, and related closed merge request' do
+ before_all do
+ create(:merge_request,
+ source_project: project,
+ source_branch: 'BRANCH-1',
+ title: 'OPEN_MR_TITLE-1',
+ description: 'OPEN_MR_DESC-1'
+ )
+
+ create(:merge_request, :closed,
+ source_project: project,
+ source_branch: 'BRANCH-1',
+ title: 'CLOSED_MR_TITLE-2',
+ description: 'CLOSED_MR_DESC-2'
+ )
+ end
+
+ it { is_expected.to eq(%w[BRANCH-1 OPEN_MR_TITLE-1 OPEN_MR_DESC-1]) }
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(jira_include_keys_from_associated_mr_for_branch: false)
+ end
+
+ it { is_expected.to eq(['BRANCH-1']) }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/swap_note_diff_files_note_id_to_bigint_for_gitlab_dot_com_spec.rb b/spec/migrations/swap_note_diff_files_note_id_to_bigint_for_gitlab_dot_com_spec.rb
new file mode 100644
index 00000000000..b0147f3ef58
--- /dev/null
+++ b/spec/migrations/swap_note_diff_files_note_id_to_bigint_for_gitlab_dot_com_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapNoteDiffFilesNoteIdToBigintForGitlabDotCom, feature_category: :database do
+ describe '#up' do
+ before do
+ # A we call `schema_migrate_down!` before each example, and for this migration
+ # `#down` is same as `#up`, we need to ensure we start from the expected state.
+ connection = described_class.new.connection
+ connection.execute('ALTER TABLE note_diff_files ALTER COLUMN diff_note_id TYPE integer')
+ connection.execute('ALTER TABLE note_diff_files ALTER COLUMN diff_note_id_convert_to_bigint TYPE bigint')
+ end
+
+ # rubocop: disable RSpec/AnyInstanceOf
+ it 'swaps the integer and bigint columns for GitLab.com, dev, or test' do
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
+
+ ndf = table(:note_diff_files)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ ndf.reset_column_information
+
+ expect(ndf.columns.find { |c| c.name == 'diff_note_id' }.sql_type).to eq('integer')
+ expect(ndf.columns.find { |c| c.name == 'diff_note_id_convert_to_bigint' }.sql_type).to eq('bigint')
+ }
+
+ migration.after -> {
+ ndf.reset_column_information
+
+ expect(ndf.columns.find { |c| c.name == 'diff_note_id' }.sql_type).to eq('bigint')
+ expect(ndf.columns.find { |c| c.name == 'diff_note_id_convert_to_bigint' }.sql_type).to eq('integer')
+ }
+ end
+ end
+ end
+
+ it 'is a no-op for other instances' do
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
+
+ ndf = table(:note_diff_files)
+
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ ndf.reset_column_information
+
+ expect(ndf.columns.find { |c| c.name == 'diff_note_id' }.sql_type).to eq('integer')
+ expect(ndf.columns.find { |c| c.name == 'diff_note_id_convert_to_bigint' }.sql_type).to eq('bigint')
+ }
+
+ migration.after -> {
+ ndf.reset_column_information
+
+ expect(ndf.columns.find { |c| c.name == 'diff_note_id' }.sql_type).to eq('integer')
+ expect(ndf.columns.find { |c| c.name == 'diff_note_id_convert_to_bigint' }.sql_type).to eq('bigint')
+ }
+ end
+ end
+ end
+ # rubocop: enable RSpec/AnyInstanceOf
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
index 733d6a789a2..4cc6bbe01b6 100644
--- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
@@ -468,9 +468,62 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
FIELDS
end
+ let_it_be(:valid_parent) { create(:work_item, project: project) }
+ let_it_be(:valid_child1) { create(:work_item, :task, project: project, created_at: 5.minutes.ago) }
+ let_it_be(:valid_child2) { create(:work_item, :task, project: project, created_at: 5.minutes.from_now) }
+ let(:input_base) { { parentId: valid_parent.to_gid.to_s } }
+ let(:child1_ref) { { adjacentWorkItemId: valid_child1.to_global_id.to_s } }
+ let(:child2_ref) { { adjacentWorkItemId: valid_child2.to_global_id.to_s } }
+ let(:relative_range) { [valid_child1, valid_child2].map(&:parent_link).map(&:relative_position) }
+
+ let(:invalid_relative_position_error) do
+ WorkItems::Widgets::HierarchyService::UpdateService::INVALID_RELATIVE_POSITION_ERROR
+ end
+
+ shared_examples 'updates work item parent and sets the relative position' do
+ it do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ work_item.reload
+ end.to change(work_item, :work_item_parent).from(nil).to(valid_parent)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(widgets_response).to include({ 'type' => 'HIERARCHY', 'children' => { 'edges' => [] },
+ 'parent' => { 'id' => valid_parent.to_global_id.to_s } })
+
+ expect(work_item.parent_link.relative_position).to be_between(*relative_range)
+ end
+ end
+
+ shared_examples 'sets the relative position and does not update work item parent' do
+ it do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ work_item.reload
+ end.to not_change(work_item, :work_item_parent)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(widgets_response).to include({ 'type' => 'HIERARCHY', 'children' => { 'edges' => [] },
+ 'parent' => { 'id' => valid_parent.to_global_id.to_s } })
+
+ expect(work_item.parent_link.relative_position).to be_between(*relative_range)
+ end
+ end
+
+ shared_examples 'returns "relative position is not valid" error message' do
+ it do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ work_item.reload
+ end.to not_change(work_item, :work_item_parent)
+
+ expect(mutation_response['workItem']).to be_nil
+ expect(mutation_response['errors']).to match_array([invalid_relative_position_error])
+ end
+ end
+
context 'when updating parent' do
let_it_be(:work_item, reload: true) { create(:work_item, :task, project: project) }
- let_it_be(:valid_parent) { create(:work_item, project: project) }
let_it_be(:invalid_parent) { create(:work_item, :task, project: project) }
context 'when parent work item type is invalid' do
@@ -493,20 +546,15 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
context 'when parent work item has a valid type' do
let(:input) { { 'hierarchyWidget' => { 'parentId' => valid_parent.to_global_id.to_s } } }
- it 'sets the parent for the work item' do
+ it 'updates work item parent' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
work_item.reload
end.to change(work_item, :work_item_parent).from(nil).to(valid_parent)
expect(response).to have_gitlab_http_status(:success)
- expect(widgets_response).to include(
- {
- 'children' => { 'edges' => [] },
- 'parent' => { 'id' => valid_parent.to_global_id.to_s },
- 'type' => 'HIERARCHY'
- }
- )
+ expect(widgets_response).to include({ 'type' => 'HIERARCHY', 'children' => { 'edges' => [] },
+ 'parent' => { 'id' => valid_parent.to_global_id.to_s } })
end
context 'when a parent is already present' do
@@ -523,6 +571,31 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
end.to change(work_item, :work_item_parent).from(existing_parent).to(valid_parent)
end
end
+
+ context 'when updating relative position' do
+ before(:all) do
+ create(:parent_link, work_item_parent: valid_parent, work_item: valid_child1)
+ create(:parent_link, work_item_parent: valid_parent, work_item: valid_child2)
+ end
+
+ context "when incomplete positioning arguments are given" do
+ let(:input) { { hierarchyWidget: input_base.merge(child1_ref) } }
+
+ it_behaves_like 'returns "relative position is not valid" error message'
+ end
+
+ context 'when moving after adjacent' do
+ let(:input) { { hierarchyWidget: input_base.merge(child1_ref).merge(relativePosition: 'AFTER') } }
+
+ it_behaves_like 'updates work item parent and sets the relative position'
+ end
+
+ context 'when moving before adjacent' do
+ let(:input) { { hierarchyWidget: input_base.merge(child2_ref).merge(relativePosition: 'BEFORE') } }
+
+ it_behaves_like 'updates work item parent and sets the relative position'
+ end
+ end
end
context 'when parentId is null' do
@@ -578,9 +651,37 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
end
end
+ context 'when reordering existing child' do
+ let_it_be(:work_item, reload: true) { create(:work_item, :task, project: project) }
+
+ context "when parent is already assigned" do
+ before(:all) do
+ create(:parent_link, work_item_parent: valid_parent, work_item: work_item)
+ create(:parent_link, work_item_parent: valid_parent, work_item: valid_child1)
+ create(:parent_link, work_item_parent: valid_parent, work_item: valid_child2)
+ end
+
+ context "when incomplete positioning arguments are given" do
+ let(:input) { { hierarchyWidget: child1_ref } }
+
+ it_behaves_like 'returns "relative position is not valid" error message'
+ end
+
+ context 'when moving after adjacent' do
+ let(:input) { { hierarchyWidget: child1_ref.merge(relativePosition: 'AFTER') } }
+
+ it_behaves_like 'sets the relative position and does not update work item parent'
+ end
+
+ context 'when moving before adjacent' do
+ let(:input) { { hierarchyWidget: child2_ref.merge(relativePosition: 'BEFORE') } }
+
+ it_behaves_like 'sets the relative position and does not update work item parent'
+ end
+ end
+ end
+
context 'when updating children' do
- let_it_be(:valid_child1) { create(:work_item, :task, project: project) }
- let_it_be(:valid_child2) { create(:work_item, :task, project: project) }
let_it_be(:invalid_child) { create(:work_item, project: project) }
let(:input) { { 'hierarchyWidget' => { 'childrenIds' => children_ids } } }
diff --git a/spec/services/work_items/parent_links/base_service_spec.rb b/spec/services/work_items/parent_links/base_service_spec.rb
new file mode 100644
index 00000000000..dbdbc774d3c
--- /dev/null
+++ b/spec/services/work_items/parent_links/base_service_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+module WorkItems
+ class ParentLinksService < WorkItems::ParentLinks::BaseService; end
+end
+
+RSpec.describe WorkItems::ParentLinks::BaseService, feature_category: :portfolio_management do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:work_item) { create(:work_item, :objective, project: project) }
+ let_it_be(:target_work_item) { create(:work_item, :objective, project: project) }
+
+ let(:params) { { target_issuable: target_work_item } }
+ let(:described_class_descendant) { WorkItems::ParentLinksService }
+
+ before do
+ project.add_reporter(user)
+ end
+
+ describe '#execute' do
+ subject { described_class_descendant.new(work_item, user, params).execute }
+
+ context 'when user has sufficient permissions' do
+ it 'raises NotImplementedError' do
+ expect { subject }.to raise_error(NotImplementedError)
+ end
+ end
+ end
+end
diff --git a/spec/services/work_items/parent_links/create_service_spec.rb b/spec/services/work_items/parent_links/create_service_spec.rb
index a989ecf9c07..4f2be7111ac 100644
--- a/spec/services/work_items/parent_links/create_service_spec.rb
+++ b/spec/services/work_items/parent_links/create_service_spec.rb
@@ -9,8 +9,8 @@ RSpec.describe WorkItems::ParentLinks::CreateService, feature_category: :portfol
let_it_be(:project) { create(:project) }
let_it_be(:work_item) { create(:work_item, project: project) }
let_it_be(:task) { create(:work_item, :task, project: project) }
- let_it_be(:task1) { create(:work_item, :task, project: project) }
- let_it_be(:task2) { create(:work_item, :task, project: project) }
+ let_it_be_with_reload(:task1) { create(:work_item, :task, project: project) }
+ let_it_be_with_reload(:task2) { create(:work_item, :task, project: project) }
let_it_be(:guest_task) { create(:work_item, :task) }
let_it_be(:invalid_task) { build_stubbed(:work_item, :task, id: non_existing_record_id) }
let_it_be(:another_project) { (create :project) }
@@ -194,7 +194,7 @@ RSpec.describe WorkItems::ParentLinks::CreateService, feature_category: :portfol
end
context 'when params include invalid ids' do
- let(:params) { { issuable_references: [task1, invalid_task] } }
+ let(:params) { { issuable_references: [task1, guest_task] } }
it 'creates links only for valid IDs' do
expect { subject }.to change(parent_link_class, :count).by(1)
diff --git a/spec/services/work_items/parent_links/reorder_service_spec.rb b/spec/services/work_items/parent_links/reorder_service_spec.rb
new file mode 100644
index 00000000000..0448429d2bb
--- /dev/null
+++ b/spec/services/work_items/parent_links/reorder_service_spec.rb
@@ -0,0 +1,176 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::ParentLinks::ReorderService, feature_category: :portfolio_management do
+ describe '#execute' do
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be_with_reload(:parent) { create(:work_item, :objective, project: project) }
+ let_it_be_with_reload(:work_item) { create(:work_item, :objective, project: project) }
+ let_it_be_with_reload(:top_adjacent) { create(:work_item, :objective, project: project) }
+ let_it_be_with_reload(:last_adjacent) { create(:work_item, :objective, project: project) }
+
+ let(:parent_link_class) { WorkItems::ParentLink }
+ let(:user) { reporter }
+ let(:params) { { target_issuable: work_item } }
+ let(:relative_range) { [top_adjacent, last_adjacent].map(&:parent_link).map(&:relative_position) }
+
+ subject { described_class.new(parent, user, params).execute }
+
+ before do
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+
+ create(:parent_link, work_item: top_adjacent, work_item_parent: parent)
+ create(:parent_link, work_item: last_adjacent, work_item_parent: parent)
+ end
+
+ shared_examples 'raises a service error' do |message, status = 409|
+ it { is_expected.to eq(service_error(message, http_status: status)) }
+ end
+
+ shared_examples 'returns not found error' do
+ it 'returns error' do
+ error = "No matching work item found. Make sure that you are adding a valid work item ID."
+
+ is_expected.to eq(service_error(error))
+ end
+
+ it 'creates no relationship' do
+ expect { subject }.not_to change { parent_link_class.count }
+ end
+ end
+
+ shared_examples 'returns conflict error' do
+ it_behaves_like 'raises a service error', 'Work item(s) already assigned'
+
+ it 'creates no relationship' do
+ expect { subject }.to not_change { parent_link_class.count }
+ end
+ end
+
+ shared_examples 'processes ordered hierarchy' do
+ it 'returns success status and processed links', :aggregate_failures do
+ expect(subject.keys).to match_array([:status, :created_references])
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:created_references].map(&:work_item_id)).to match_array([work_item.id])
+ end
+
+ it 'orders hierarchy' do
+ subject
+
+ expect(last_adjacent.parent_link.relative_position).to be_between(*relative_range)
+ end
+ end
+
+ context 'when user has insufficient permissions' do
+ let(:user) { guest }
+
+ it_behaves_like 'returns not found error'
+
+ context 'when user is a guest assigned to the work item' do
+ before do
+ work_item.assignees = [guest]
+ end
+
+ it_behaves_like 'returns not found error'
+ end
+ end
+
+ context 'when child and parent are already linked' do
+ before do
+ create(:parent_link, work_item: work_item, work_item_parent: parent)
+ end
+
+ it_behaves_like 'returns conflict error'
+
+ context 'when adjacents are already in place and the user has sufficient permissions' do
+ let(:base_param) { { target_issuable: work_item } }
+
+ shared_examples 'updates hierarchy order without notes' do
+ it_behaves_like 'processes ordered hierarchy'
+
+ it 'keeps relationships', :aggregate_failures do
+ expect { subject }.to not_change { parent_link_class.count }
+
+ expect(parent_link_class.where(work_item: work_item).last.work_item_parent).to eq(parent)
+ end
+
+ it 'does not create notes', :aggregate_failures do
+ expect { subject }.to not_change { work_item.notes.count }.and(not_change { work_item.notes.count })
+ end
+ end
+
+ context 'when moving before adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: last_adjacent, relative_position: 'BEFORE' }) }
+
+ it_behaves_like 'updates hierarchy order without notes'
+ end
+
+ context 'when moving after adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: top_adjacent, relative_position: 'AFTER' }) }
+
+ it_behaves_like 'updates hierarchy order without notes'
+ end
+ end
+ end
+
+ context 'when new parent is assigned' do
+ shared_examples 'updates hierarchy order and creates notes' do
+ it_behaves_like 'processes ordered hierarchy'
+
+ it 'creates notes', :aggregate_failures do
+ subject
+
+ expect(parent.notes.last.note).to eq("added #{work_item.to_reference} as child objective")
+ expect(work_item.notes.last.note).to eq("added #{parent.to_reference} as parent objective")
+ end
+ end
+
+ context 'when adjacents are already in place and the user has sufficient permissions' do
+ let(:base_param) { { target_issuable: work_item } }
+
+ context 'when moving before adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: last_adjacent, relative_position: 'BEFORE' }) }
+
+ it_behaves_like 'updates hierarchy order and creates notes'
+ end
+
+ context 'when moving after adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: top_adjacent, relative_position: 'AFTER' }) }
+
+ it_behaves_like 'updates hierarchy order and creates notes'
+ end
+
+ context 'when previous parent was in place' do
+ before do
+ create(:parent_link, work_item: work_item,
+ work_item_parent: create(:work_item, :objective, project: project))
+ end
+
+ context 'when moving before adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: last_adjacent, relative_position: 'BEFORE' }) }
+
+ it_behaves_like 'updates hierarchy order and creates notes'
+ end
+
+ context 'when moving after adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: top_adjacent, relative_position: 'AFTER' }) }
+
+ it_behaves_like 'updates hierarchy order and creates notes'
+ end
+ end
+ end
+ end
+ end
+
+ def service_error(message, http_status: 404)
+ {
+ message: message,
+ status: :error,
+ http_status: http_status
+ }
+ end
+end
diff --git a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
index 160a7fc1045..229ba81d676 100644
--- a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
+++ b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
@@ -14,7 +14,13 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Hierarchy) } }
let(:not_found_error) { 'No matching work item found. Make sure that you are adding a valid work item ID.' }
- shared_examples 'raises a WidgetError' do
+ shared_examples 'raises a WidgetError' do |message|
+ it { expect { subject }.to raise_error(described_class::WidgetError, message) }
+ end
+
+ shared_examples 'raises a WidgetError with message' do
+ let(:message) { not_found_error }
+
it { expect { subject }.to raise_error(described_class::WidgetError, message) }
end
@@ -24,16 +30,30 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
context 'when parent and children params are present' do
let(:params) { { parent: parent_work_item, children: [child_work_item] } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) { 'A Work Item can be a parent or a child, but not both.' }
- end
+ it_behaves_like 'raises a WidgetError', 'A Work Item can be a parent or a child, but not both.'
end
context 'when invalid params are present' do
let(:params) { { other_parent: parent_work_item } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) { 'One or more arguments are invalid: other_parent.' }
+ it_behaves_like 'raises a WidgetError', 'One or more arguments are invalid: other_parent.'
+ end
+
+ context 'when relative position params are incomplete' do
+ context 'when only adjacent_work_item is present' do
+ let(:params) do
+ { parent: parent_work_item, adjacent_work_item: child_work_item }
+ end
+
+ it_behaves_like 'raises a WidgetError', described_class::INVALID_RELATIVE_POSITION_ERROR
+ end
+
+ context 'when only relative_position is present' do
+ let(:params) do
+ { parent: parent_work_item, relative_position: 'AFTER' }
+ end
+
+ it_behaves_like 'raises a WidgetError', described_class::INVALID_RELATIVE_POSITION_ERROR
end
end
@@ -45,7 +65,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
context 'when user has insufficient permissions to link work items' do
let(:params) { { children: [child_work_item4] } }
- it_behaves_like 'raises a WidgetError' do
+ it_behaves_like 'raises a WidgetError with message' do
let(:message) { not_found_error }
end
end
@@ -55,7 +75,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
project.add_developer(user)
end
- context 'with valid params' do
+ context 'with valid children params' do
let(:params) { { children: [child_work_item2, child_work_item3] } }
it 'correctly sets work item parent' do
@@ -64,14 +84,30 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
expect(work_item.reload.work_item_children)
.to contain_exactly(child_work_item, child_work_item2, child_work_item3)
end
+
+ context 'when relative_position and adjacent_work_item are given' do
+ context 'with BEFORE value' do
+ let(:params) do
+ { children: [child_work_item3], relative_position: 'BEFORE', adjacent_work_item: child_work_item }
+ end
+
+ it_behaves_like 'raises a WidgetError', described_class::CHILDREN_REORDERING_ERROR
+ end
+
+ context 'with AFTER value' do
+ let(:params) do
+ { children: [child_work_item2], relative_position: 'AFTER', adjacent_work_item: child_work_item }
+ end
+
+ it_behaves_like 'raises a WidgetError', described_class::CHILDREN_REORDERING_ERROR
+ end
+ end
end
context 'when child is already assigned' do
let(:params) { { children: [child_work_item] } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) { 'Work item(s) already assigned' }
- end
+ it_behaves_like 'raises a WidgetError', 'Work item(s) already assigned'
end
context 'when child type is invalid' do
@@ -79,10 +115,8 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
let(:params) { { children: [child_issue] } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) do
- "#{child_issue.to_reference} cannot be added: is not allowed to add this type of parent"
- end
+ it_behaves_like 'raises a WidgetError with message' do
+ let(:message) { "#{child_issue.to_reference} cannot be added: is not allowed to add this type of parent" }
end
end
end
@@ -94,7 +128,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
let(:params) { { parent: parent_work_item } }
context 'when user has insufficient permissions to link work items' do
- it_behaves_like 'raises a WidgetError' do
+ it_behaves_like 'raises a WidgetError with message' do
let(:message) { not_found_error }
end
end
@@ -134,10 +168,34 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
let(:params) { { parent: parent_task } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) do
- "#{work_item.to_reference} cannot be added: is not allowed to add this type of parent"
+ it_behaves_like 'raises a WidgetError with message' do
+ let(:message) { "#{work_item.to_reference} cannot be added: is not allowed to add this type of parent" }
+ end
+ end
+
+ context 'with positioning arguments' do
+ let_it_be_with_reload(:adjacent) { create(:work_item, :task, project: project) }
+
+ let_it_be_with_reload(:adjacent_link) do
+ create(:parent_link, work_item: adjacent, work_item_parent: parent_work_item)
+ end
+
+ let(:params) { { parent: parent_work_item, adjacent_work_item: adjacent, relative_position: 'AFTER' } }
+
+ it 'correctly sets new parent and position' do
+ expect(subject[:status]).to eq(:success)
+ expect(work_item.work_item_parent).to eq(parent_work_item)
+ expect(work_item.parent_link.relative_position).to be > adjacent_link.relative_position
+ end
+
+ context 'when other hierarchy adjacent is provided' do
+ let_it_be(:other_hierarchy_adjacent) { create(:parent_link).work_item }
+
+ let(:params) do
+ { parent: parent_work_item, adjacent_work_item: other_hierarchy_adjacent, relative_position: 'AFTER' }
end
+
+ it_behaves_like 'raises a WidgetError', described_class::UNRELATED_ADJACENT_HIERARCHY_ERROR
end
end
end
diff --git a/spec/workers/jira_connect/sync_merge_request_worker_spec.rb b/spec/workers/jira_connect/sync_merge_request_worker_spec.rb
index 117dfe49b62..bbedbf6a53b 100644
--- a/spec/workers/jira_connect/sync_merge_request_worker_spec.rb
+++ b/spec/workers/jira_connect/sync_merge_request_worker_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe JiraConnect::SyncMergeRequestWorker, feature_category: :integrati
it 'calls JiraConnect::SyncService#execute' do
expect_next(JiraConnect::SyncService).to receive(:execute)
- .with(merge_requests: [merge_request], update_sequence_id: update_sequence_id)
+ .with(merge_requests: [merge_request], branches: [have_attributes(name: 'master')], update_sequence_id: update_sequence_id)
perform
end
@@ -36,5 +36,20 @@ RSpec.describe JiraConnect::SyncMergeRequestWorker, feature_category: :integrati
perform
end
end
+
+ context 'when source branch cannot be found' do
+ before do
+ allow_next_found_instance_of(MergeRequest) do |mr|
+ allow(mr).to receive(:source_branch).and_return('non-existant-branch')
+ end
+ end
+
+ it 'calls JiraConnect::SyncService will an empty branch' do
+ expect_next(JiraConnect::SyncService).to receive(:execute)
+ .with(merge_requests: [merge_request], branches: [], update_sequence_id: update_sequence_id)
+
+ perform
+ end
+ end
end
end