From ba2e4183d9b07237657595821cd06689667e6762 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 20 Oct 2022 06:09:59 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- app/assets/javascripts/gl_field_errors.js | 11 +- .../admin/jobs/index/components/constants.js | 11 ++ .../jobs/index/components/stop_jobs_modal.vue | 31 +++-- .../pages/admin/jobs/index/index.js | 8 +- app/graphql/types/deployment_details_type.rb | 4 +- app/graphql/types/deployment_type.rb | 2 +- app/helpers/nav/top_nav_helper.rb | 76 ++--------- app/models/merge_request.rb | 6 +- .../merge_requests/after_create_service.rb | 2 + app/services/merge_requests/create_service.rb | 7 +- app/views/layouts/header/_default.html.haml | 15 +-- .../async_merge_request_diff_creation.yml | 8 ++ .../development/new_navbar_layout.yml | 8 -- .../remove_extra_primary_submenu_options.yml | 8 -- ..._id_target_project_id_on_merge_requests.rb | 15 +++ db/schema_migrations/20221010141500 | 1 + db/structure.sql | 2 + doc/api/graphql/reference/index.md | 64 +++++++++ doc/api/projects.md | 36 +++++ doc/api/users.md | 2 +- doc/user/admin_area/moderate_users.md | 2 +- doc/user/group/manage.md | 7 + doc/user/profile/personal_access_tokens.md | 6 +- doc/user/project/members/index.md | 15 +++ lib/api/entities/pull_mirror.rb | 17 +++ lib/gitlab/nav/top_nav_view_model_builder.rb | 5 +- locale/gitlab.pot | 21 ++- qa/qa/page/main/menu.rb | 14 +- .../formatters/allure_metadata_formatter.rb | 16 ++- .../formatters/test_stats_formatter.rb | 1 + .../formatters/test_stats_formatter_spec.rb | 15 +++ spec/features/cycle_analytics_spec.rb | 8 +- spec/fixtures/api/schemas/project_mirror.json | 48 +++++++ .../fixtures/static/gl_field_errors.html | 3 + spec/frontend/gl_field_errors_spec.js | 2 +- .../types/deployment_details_type_spec.rb | 2 +- spec/helpers/nav/top_nav_helper_spec.rb | 126 +----------------- spec/models/merge_request_spec.rb | 28 ++++ .../projects/cycle_analytics_events_spec.rb | 4 +- .../merge_requests/create_service_spec.rb | 37 ++++- 40 files changed, 418 insertions(+), 276 deletions(-) create mode 100644 app/assets/javascripts/pages/admin/jobs/index/components/constants.js create mode 100644 config/feature_flags/development/async_merge_request_diff_creation.yml delete mode 100644 config/feature_flags/development/new_navbar_layout.yml delete mode 100644 config/feature_flags/development/remove_extra_primary_submenu_options.yml create mode 100644 db/post_migrate/20221010141500_add_index_author_id_target_project_id_on_merge_requests.rb create mode 100644 db/schema_migrations/20221010141500 create mode 100644 lib/api/entities/pull_mirror.rb create mode 100644 spec/fixtures/api/schemas/project_mirror.json diff --git a/app/assets/javascripts/gl_field_errors.js b/app/assets/javascripts/gl_field_errors.js index eec7a138ea7..28aa9906116 100644 --- a/app/assets/javascripts/gl_field_errors.js +++ b/app/assets/javascripts/gl_field_errors.js @@ -15,9 +15,14 @@ export default class GlFieldErrors { initValidators() { // register selectors here as needed - const validateSelectors = [':text', ':password', '[type=email]', '[type=url]', '[type=number]'] - .map((selector) => `input${selector}`) - .join(','); + const validateSelectors = [ + 'input:text', + 'input:password', + 'input[type=email]', + 'input[type=url]', + 'input[type=number]', + 'textarea', + ].join(','); this.state.inputs = this.form .find(validateSelectors) diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/constants.js b/app/assets/javascripts/pages/admin/jobs/index/components/constants.js new file mode 100644 index 00000000000..9e2d464bc4d --- /dev/null +++ b/app/assets/javascripts/pages/admin/jobs/index/components/constants.js @@ -0,0 +1,11 @@ +import { s__, __ } from '~/locale'; + +export const STOP_JOBS_MODAL_ID = 'stop-jobs-modal'; +export const STOP_JOBS_MODAL_TITLE = s__('AdminArea|Stop all jobs?'); +export const STOP_JOBS_BUTTON_TEXT = s__('AdminArea|Stop all jobs'); +export const CANCEL_TEXT = __('Cancel'); +export const STOP_JOBS_FAILED_TEXT = s__('AdminArea|Stopping jobs failed'); +export const PRIMARY_ACTION_TEXT = s__('AdminArea|Stop jobs'); +export const STOP_JOBS_WARNING = s__( + 'AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.', +); diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue index 4f42ef2892d..b608b3b9492 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue +++ b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue @@ -3,7 +3,14 @@ import { GlModal } from '@gitlab/ui'; import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; import { redirectTo } from '~/lib/utils/url_utility'; -import { __, s__ } from '~/locale'; +import { + CANCEL_TEXT, + STOP_JOBS_MODAL_ID, + STOP_JOBS_FAILED_TEXT, + STOP_JOBS_MODAL_TITLE, + STOP_JOBS_WARNING, + PRIMARY_ACTION_TEXT, +} from './constants'; export default { components: { @@ -15,13 +22,6 @@ export default { required: true, }, }, - computed: { - text() { - return s__( - 'AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.', - ); - }, - }, methods: { onSubmit() { return axios @@ -32,30 +32,33 @@ export default { }) .catch((error) => { createAlert({ - message: s__('AdminArea|Stopping jobs failed'), + message: STOP_JOBS_FAILED_TEXT, }); throw error; }); }, }, primaryAction: { - text: s__('AdminArea|Stop jobs'), + text: PRIMARY_ACTION_TEXT, attributes: [{ variant: 'danger' }], }, cancelAction: { - text: __('Cancel'), + text: CANCEL_TEXT, }, + STOP_JOBS_WARNING, + STOP_JOBS_MODAL_ID, + STOP_JOBS_MODAL_TITLE, }; diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js index 4cd312b403c..c82b186f671 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/index.js +++ b/app/assets/javascripts/pages/admin/jobs/index/index.js @@ -1,29 +1,29 @@ import Vue from 'vue'; import { BV_SHOW_MODAL } from '~/lib/utils/constants'; import Translate from '~/vue_shared/translate'; +import { STOP_JOBS_MODAL_ID } from './components/constants'; import StopJobsModal from './components/stop_jobs_modal.vue'; Vue.use(Translate); function initJobs() { const buttonId = 'js-stop-jobs-button'; - const modalId = 'stop-jobs-modal'; const stopJobsButton = document.getElementById(buttonId); if (stopJobsButton) { // eslint-disable-next-line no-new new Vue({ - el: `#js-${modalId}`, + el: `#js-${STOP_JOBS_MODAL_ID}`, components: { StopJobsModal, }, mounted() { stopJobsButton.classList.remove('disabled'); stopJobsButton.addEventListener('click', () => { - this.$root.$emit(BV_SHOW_MODAL, modalId, `#${buttonId}`); + this.$root.$emit(BV_SHOW_MODAL, STOP_JOBS_MODAL_ID, `#${buttonId}`); }); }, render(createElement) { - return createElement(modalId, { + return createElement(STOP_JOBS_MODAL_ID, { props: { url: stopJobsButton.dataset.url, }, diff --git a/app/graphql/types/deployment_details_type.rb b/app/graphql/types/deployment_details_type.rb index f8ba0cb1b24..bbb5cc8e3f1 100644 --- a/app/graphql/types/deployment_details_type.rb +++ b/app/graphql/types/deployment_details_type.rb @@ -5,7 +5,7 @@ module Types graphql_name 'DeploymentDetails' description 'The details of the deployment' authorize :read_deployment - present_using Deployments::DeploymentPresenter + present_using ::Deployments::DeploymentPresenter field :tags, [Types::DeploymentTagType], @@ -13,3 +13,5 @@ module Types calls_gitaly: true end end + +Types::DeploymentDetailsType.prepend_mod_with('Types::DeploymentDetailsType') diff --git a/app/graphql/types/deployment_type.rb b/app/graphql/types/deployment_type.rb index 70a3a4cb574..59b59dc4e1d 100644 --- a/app/graphql/types/deployment_type.rb +++ b/app/graphql/types/deployment_type.rb @@ -11,7 +11,7 @@ module Types graphql_name 'Deployment' description 'The deployment of an environment' - present_using Deployments::DeploymentPresenter + present_using ::Deployments::DeploymentPresenter authorize :read_deployment diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb index 32d3f4aebb4..751900f4593 100644 --- a/app/helpers/nav/top_nav_helper.rb +++ b/app/helpers/nav/top_nav_helper.rb @@ -281,76 +281,28 @@ module Nav end def projects_submenu_items(builder:) - if Feature.enabled?(:remove_extra_primary_submenu_options) - title = _('View all projects') + title = _('View all projects') - builder.add_primary_menu_item( - id: 'your', - title: title, - href: dashboard_projects_path, - data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) } - ) - else - # These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml` - [ - { id: 'your', title: _('Your projects'), href: dashboard_projects_path }, - { id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path }, - { id: 'explore', title: _('Explore projects'), href: explore_root_path }, - { id: 'topics', title: _('Explore topics'), href: topics_explore_projects_path } - ].each do |item| - builder.add_primary_menu_item( - **item, - data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) } - ) - end - - title = _('Create new project') - - builder.add_secondary_menu_item( - id: 'create', - title: title, - href: new_project_path, - data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) } - ) - end + builder.add_primary_menu_item( + id: 'your', + title: title, + href: dashboard_projects_path, + data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) } + ) end def groups_submenu # These group links come from `app/views/layouts/nav/groups_dropdown/_show.html.haml` builder = ::Gitlab::Nav::TopNavMenuBuilder.new - if Feature.enabled?(:remove_extra_primary_submenu_options) - title = _('View all groups') - - builder.add_primary_menu_item( - id: 'your', - title: title, - href: dashboard_groups_path, - data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) } - ) - else - [ - { id: 'your', title: _('Your groups'), href: dashboard_groups_path }, - { id: 'explore', title: _('Explore groups'), href: explore_groups_path } - ].each do |item| - builder.add_primary_menu_item( - **item, - data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) } - ) - end - - if current_user.can_create_group? - title = _('Create group') - - builder.add_secondary_menu_item( - id: 'create', - title: title, - href: new_group_path, - data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) } - ) - end - end + title = _('View all groups') + builder.add_primary_menu_item( + id: 'your', + title: title, + href: dashboard_groups_path, + data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) } + ) builder.build end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index fb20d91fa20..0f47e6d3936 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -136,7 +136,7 @@ class MergeRequest < ApplicationRecord before_validation :set_draft_status - after_create :ensure_merge_request_diff + after_create :ensure_merge_request_diff, unless: :skip_ensure_merge_request_diff after_update :clear_memoized_shas after_update :reload_diff_if_branch_changed after_commit :ensure_metrics, on: [:create, :update], unless: :importing? @@ -146,6 +146,10 @@ class MergeRequest < ApplicationRecord # It allows us to close or modify broken merge requests attr_accessor :allow_broken + # Temporary flag to skip merge_request_diff creation on create. + # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100390 + attr_accessor :skip_ensure_merge_request_diff + # Temporary fields to store compare vars # when creating new merge request attr_accessor :can_be_created, :compare_commits, :diff_options, :compare diff --git a/app/services/merge_requests/after_create_service.rb b/app/services/merge_requests/after_create_service.rb index 9d12eb80eb6..20b32dbc2a0 100644 --- a/app/services/merge_requests/after_create_service.rb +++ b/app/services/merge_requests/after_create_service.rb @@ -5,6 +5,8 @@ module MergeRequests include Gitlab::Utils::StrongMemoize def execute(merge_request) + merge_request.ensure_merge_request_diff + prepare_for_mergeability(merge_request) prepare_merge_request(merge_request) end diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb index 8e0f72eb380..9303b0c4e51 100644 --- a/app/services/merge_requests/create_service.rb +++ b/app/services/merge_requests/create_service.rb @@ -34,7 +34,12 @@ module MergeRequests # callback (e.g. after_create), a database transaction will be # open while the Gitaly RPC waits. To avoid an idle in transaction # timeout, we do this before we attempt to save the merge request. - merge_request.eager_fetch_ref! + + if Feature.enabled?(:async_merge_request_diff_creation, current_user) + merge_request.skip_ensure_merge_request_diff = true + else + merge_request.eager_fetch_ref! + end end def set_projects! diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index b74dfd4d3a1..0dbeebd0ad0 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -28,7 +28,7 @@ .gl-display-none.gl-sm-display-block = render "layouts/nav/top_nav" - - if top_nav_show_search && Feature.enabled?(:new_navbar_layout) + - if top_nav_show_search .navbar-collapse.gl-transition-medium.collapse.gl-mr-auto.global-search-container.hide-when-top-nav-responsive-open - search_menu_item = top_nav_search_menu_item_attrs %ul.nav.navbar-nav.gl-w-full.gl-align-items-center @@ -42,21 +42,10 @@ = link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = sprite_icon(search_menu_item.fetch(:icon)) - .navbar-collapse.gl-transition-medium.collapse{ class: ('global-search-container' unless Feature.enabled?(:new_navbar_layout)) } + .navbar-collapse.gl-transition-medium.collapse %ul.nav.navbar-nav.gl-w-full.gl-align-items-center.gl-justify-content-end - if current_user = render 'layouts/header/new_dropdown', class: 'gl-display-none gl-sm-display-block gl-white-space-nowrap gl-text-right' - - if top_nav_show_search && Feature.disabled?(:new_navbar_layout) - - search_menu_item = top_nav_search_menu_item_attrs - %li.nav-item.header-search-new.gl-display-none.gl-lg-display-block.gl-w-full - - unless current_controller?(:search) - - if Feature.enabled?(:new_header_search) - = render 'layouts/header_search' - - else - = render 'layouts/search' - %li.nav-item{ class: 'd-none d-sm-inline-block d-lg-none' } - = link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = sprite_icon(search_menu_item.fetch(:icon)) - if header_link?(:issues) = nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do = link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues js-prefetch-document', aria: { label: _('Issues') }, diff --git a/config/feature_flags/development/async_merge_request_diff_creation.yml b/config/feature_flags/development/async_merge_request_diff_creation.yml new file mode 100644 index 00000000000..8e4bdd13b2b --- /dev/null +++ b/config/feature_flags/development/async_merge_request_diff_creation.yml @@ -0,0 +1,8 @@ +--- +name: async_merge_request_diff_creation +introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100390" +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/376759 +milestone: '15.6' +type: development +group: group::code review +default_enabled: false diff --git a/config/feature_flags/development/new_navbar_layout.yml b/config/feature_flags/development/new_navbar_layout.yml deleted file mode 100644 index 2d212922fcc..00000000000 --- a/config/feature_flags/development/new_navbar_layout.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: new_navbar_layout -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96853 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373078 -milestone: '15.4' -type: development -group: group::foundations -default_enabled: true diff --git a/config/feature_flags/development/remove_extra_primary_submenu_options.yml b/config/feature_flags/development/remove_extra_primary_submenu_options.yml deleted file mode 100644 index dda22c5d57e..00000000000 --- a/config/feature_flags/development/remove_extra_primary_submenu_options.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: remove_extra_primary_submenu_options -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96931 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373078 -milestone: '15.4' -type: development -group: group::foundations -default_enabled: true diff --git a/db/post_migrate/20221010141500_add_index_author_id_target_project_id_on_merge_requests.rb b/db/post_migrate/20221010141500_add_index_author_id_target_project_id_on_merge_requests.rb new file mode 100644 index 00000000000..5b9d5be2b3f --- /dev/null +++ b/db/post_migrate/20221010141500_add_index_author_id_target_project_id_on_merge_requests.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddIndexAuthorIdTargetProjectIdOnMergeRequests < Gitlab::Database::Migration[2.0] + INDEX_NAME = 'index_merge_requests_on_author_id_and_target_project_id' + + disable_ddl_transaction! + + def up + add_concurrent_index :merge_requests, %i[author_id target_project_id], name: INDEX_NAME + end + + def down + remove_concurrent_index_by_name :merge_requests, INDEX_NAME + end +end diff --git a/db/schema_migrations/20221010141500 b/db/schema_migrations/20221010141500 new file mode 100644 index 00000000000..8479fb0519c --- /dev/null +++ b/db/schema_migrations/20221010141500 @@ -0,0 +1 @@ +250ec3ff701dacd333d669f128762e9f035a626f2f7720c6e7e1dc61499d431d \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 2dab5e7abc9..13ba431e5e5 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -29481,6 +29481,8 @@ CREATE INDEX index_merge_requests_on_assignee_id ON merge_requests USING btree ( CREATE INDEX index_merge_requests_on_author_id ON merge_requests USING btree (author_id); +CREATE INDEX index_merge_requests_on_author_id_and_target_project_id ON merge_requests USING btree (author_id, target_project_id); + CREATE INDEX index_merge_requests_on_created_at ON merge_requests USING btree (created_at); CREATE INDEX index_merge_requests_on_description_trigram ON merge_requests USING gin (description gin_trgm_ops); diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 64ac83ef46e..649df9a835a 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -11442,6 +11442,33 @@ The deployment of an environment. | `triggerer` | [`UserCore`](#usercore) | User who executed the deployment. | | `updatedAt` | [`Time`](#time) | When the deployment record was updated. | +### `DeploymentApproval` + +Approval of the deployment. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `comment` | [`String`](#string) | Additional comment. | +| `createdAt` | [`Time`](#time) | When the user approved/rejected first time. | +| `status` | [`DeploymentsApprovalStatus`](#deploymentsapprovalstatus) | Whether the deployment was approved/rejected. | +| `updatedAt` | [`Time`](#time) | When the user updated the approval. | +| `user` | [`UserCore`](#usercore) | User who approved or rejected the deployment. | + +### `DeploymentApprovalSummary` + +Approval summary of the deployment. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `rules` | [`[ProtectedEnvironmentApprovalRuleForSummary!]`](#protectedenvironmentapprovalruleforsummary) | Approval Rules for the deployment. | +| `status` | [`DeploymentApprovalSummaryStatus`](#deploymentapprovalsummarystatus) | Status of the approvals. | +| `totalPendingApprovalCount` | [`Int`](#int) | Total pending approval count. | +| `totalRequiredApprovals` | [`Int`](#int) | Total number of required approvals. | + ### `DeploymentDetails` The details of the deployment. @@ -11450,6 +11477,7 @@ The details of the deployment. | Name | Type | Description | | ---- | ---- | ----------- | +| `approvalSummary` | [`DeploymentApprovalSummary`](#deploymentapprovalsummary) | Approval summary of the deployment. | | `commit` | [`Commit`](#commit) | Commit details of the deployment. | | `createdAt` | [`Time`](#time) | When the deployment record was created. | | `finishedAt` | [`Time`](#time) | When the deployment finished. | @@ -17605,6 +17633,23 @@ Which group, user or role is allowed to approve deployments to the environment. | `requiredApprovals` | [`Int`](#int) | Number of required approvals. | | `user` | [`UserCore`](#usercore) | User details. Present if it's user specific access control. | +### `ProtectedEnvironmentApprovalRuleForSummary` + +Which group, user or role is allowed to approve deployments to the environment. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `accessLevel` | [`AccessLevel`](#accesslevel) | Role details. Present if it's role specific access control. | +| `approvals` | [`[DeploymentApproval!]`](#deploymentapproval) | Current approvals of the deployment. | +| `approvedCount` | [`Int`](#int) | Approved count. | +| `group` | [`Group`](#group) | Group details. Present if it's group specific access control. | +| `pendingApprovalCount` | [`Int`](#int) | Pending approval count. | +| `requiredApprovals` | [`Int`](#int) | Number of required approvals. | +| `status` | [`DeploymentApprovalSummaryStatus`](#deploymentapprovalsummarystatus) | Status of the approval summary. | +| `user` | [`UserCore`](#usercore) | User details. Present if it's user specific access control. | + ### `ProtectedEnvironmentDeployAccessLevel` Which group, user or role is allowed to execute deployments to the environment. @@ -20479,6 +20524,16 @@ Weight of the data visualization palette. | `PENDING_DESTRUCTION` | Dependency proxy manifest has a status of pending_destruction. | | `PROCESSING` | Dependency proxy manifest has a status of processing. | +### `DeploymentApprovalSummaryStatus` + +Status of the deployment approval summary. + +| Value | Description | +| ----- | ----------- | +| `APPROVED` | Summarized deployment approval status that is approved. | +| `PENDING_APPROVAL` | Summarized deployment approval status that is pending approval. | +| `REJECTED` | Summarized deployment approval status that is rejected. | + ### `DeploymentStatus` All deployment statuses. @@ -20505,6 +20560,15 @@ All environment deployment tiers. | `STAGING` | Staging. | | `TESTING` | Testing. | +### `DeploymentsApprovalStatus` + +Status of the deployment approval. + +| Value | Description | +| ----- | ----------- | +| `APPROVED` | A deployment approval that is approved. | +| `REJECTED` | A deployment approval that is rejected. | + ### `DesignCollectionCopyState` Copy state of a DesignCollection. diff --git a/doc/api/projects.md b/doc/api/projects.md index 0eab8e485a8..50a2e1b9f34 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -2830,6 +2830,42 @@ Read more in the [Project members](members.md) documentation. Read more in the [Project vulnerabilities](project_vulnerabilities.md) documentation. +## Get a project's pull mirror details **(PREMIUM)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/354506) in GitLab 15.5. + +Returns the details of the project's pull mirror. + +```plaintext +GET /projects/:id/mirror/pull +``` + +Supported attributes: + +| Attribute | Type | Required | Description | +|:----------|:------|:------------|:------------| +| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). | + +Example request: + +```shell +curl --request GET --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/:id/mirror/pull" +``` + +Example response: + +```json +{ + "id": 101486, + "last_error": null, + "last_successful_update_at": "2020-01-06T17:32:02.823Z", + "last_update_at": "2020-01-06T17:32:02.823Z", + "last_update_started_at": "2020-01-06T17:31:55.864Z", + "update_status": "finished", + "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" +} +``` + ## Configure pull mirroring for a project **(PREMIUM)** > Moved to GitLab Premium in 13.9. diff --git a/doc/api/users.md b/doc/api/users.md index dd712cc881c..f4aadfd33db 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -1580,7 +1580,7 @@ Returns: - `404 User Not Found` if user cannot be found. - `403 Forbidden` when trying to deactivate a user: - Blocked by administrator or by LDAP synchronization. - - That has any activity in past 90 days. These users cannot be deactivated. + - That is not [dormant](../user/admin_area/moderate_users.md#automatically-deactivate-dormant-users). - That is internal. ## Activate user **(FREE SELF)** diff --git a/doc/user/admin_area/moderate_users.md b/doc/user/admin_area/moderate_users.md index ace1c6be5f8..52bb376d1fd 100644 --- a/doc/user/admin_area/moderate_users.md +++ b/doc/user/admin_area/moderate_users.md @@ -162,7 +162,7 @@ A user can be deactivated from the Admin Area. To do this: For the deactivation option to be visible to an administrator, the user: - Must be currently active. -- Must not have signed in, or have any activity, in the last 90 days. +- Must not be [dormant](#automatically-deactivate-dormant-users). NOTE: Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user). diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md index f11d9035a52..66d939894c2 100644 --- a/doc/user/group/manage.md +++ b/doc/user/group/manage.md @@ -201,6 +201,13 @@ To remove a member from a group: - To unassign the user from linked issues and merge requests, select the **Also unassign this user from linked issues and merge requests** checkbox. 1. Select **Remove member**. +## Ensure removed users cannot invite themselves back + +Malicious users with the Maintainer or Owner role could exploit a race condition that allows +them to invite themselves back to a group or project that a GitLab administrator has removed them from. + +To avoid this problem, GitLab administrators can [ensure removed users cannot invite themselves back](../project/members/index.md#ensure-removed-users-cannot-invite-themselves-back). + ## Add projects to a group There are two different ways to add a new project to a group: diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index c7fe68c0609..c5958c4fd11 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -89,8 +89,10 @@ At any time, you can revoke a personal access token. ## View the last time a token was used -Token usage is updated once every 24 hours. It is updated each time the token is used to request -[API resources](../../api/api_resources.md) and the [GraphQL API](../../api/graphql/index.md). +Token usage information is updated every 24 hours. GitLab considers a token used when the token is used to: + +- Authenticate with the [REST](../../api/index.md) or [GraphQL](../../api/graphql/index.md) APIs. +- Perform a Git operation. To view the last time a token was used: diff --git a/doc/user/project/members/index.md b/doc/user/project/members/index.md index a8f1b634127..61181f157f4 100644 --- a/doc/user/project/members/index.md +++ b/doc/user/project/members/index.md @@ -187,6 +187,21 @@ To remove a member from a project: [from being forked outside their group](../../group/access_and_permissions.md#prevent-project-forking-outside-group). 1. Select **Remove member**. +## Ensure removed users cannot invite themselves back + +Malicious users with the Maintainer or Owner role could exploit a race condition that allows +them to invite themselves back to a group or project that a GitLab administrator has removed them from. + +To avoid this problem, GitLab administrators can: + +- Remove the malicious user session from the [GitLab Rails console](../../../administration/operations/rails_console.md). +- Impersonate the malicious user to: + - Remove the user from the project. + - Log the user out of GitLab. +- Block the malicious user account. +- Remove the malicious user account. +- Change the password for the malicious user account. + ## Filter and sort members > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) in GitLab 12.6. diff --git a/lib/api/entities/pull_mirror.rb b/lib/api/entities/pull_mirror.rb new file mode 100644 index 00000000000..6914a79b18e --- /dev/null +++ b/lib/api/entities/pull_mirror.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module API + module Entities + class PullMirror < Grape::Entity + expose :id + expose :status, as: :update_status + expose :url do |import_state| + import_state.project.safe_import_url + end + expose :last_error + expose :last_update_at + expose :last_update_started_at + expose :last_successful_update_at + end + end +end diff --git a/lib/gitlab/nav/top_nav_view_model_builder.rb b/lib/gitlab/nav/top_nav_view_model_builder.rb index a8e25708107..8cb2729ff61 100644 --- a/lib/gitlab/nav/top_nav_view_model_builder.rb +++ b/lib/gitlab/nav/top_nav_view_model_builder.rb @@ -42,13 +42,10 @@ module Gitlab def build menu = @menu_builder.build - hide_menu_text = Feature.enabled?(:new_navbar_layout) - menu.merge({ views: @views, shortcuts: @shortcuts, - menuTitle: (_('Menu') unless hide_menu_text), - menuTooltip: (_('Main menu') if hide_menu_text) + menuTooltip: _('Main menu') }.compact) end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a31b04ec47b..e4b18209a14 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2689,6 +2689,24 @@ msgstr "" msgid "AdminDashboard|Error loading the statistics. Please try again" msgstr "" +msgid "AdminEmail|Body" +msgstr "" + +msgid "AdminEmail|Body is required." +msgstr "" + +msgid "AdminEmail|Recipient group or project" +msgstr "" + +msgid "AdminEmail|Recipient group or project is required." +msgstr "" + +msgid "AdminEmail|Subject" +msgstr "" + +msgid "AdminEmail|Subject is required." +msgstr "" + msgid "AdminLabels|Define your default set of project labels" msgstr "" @@ -11226,9 +11244,6 @@ msgstr "" msgid "Create new label" msgstr "" -msgid "Create new project" -msgstr "" - msgid "Create new..." msgstr "" diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index 2f618224a73..ecd71e7c2f4 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -64,12 +64,7 @@ module QA def go_to_groups within_groups_menu do - # Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default - if has_element?(:menu_item_link, title: 'Your groups') - click_element(:menu_item_link, title: 'Your groups') - else - click_element(:menu_item_link, title: 'View all groups') - end + click_element(:menu_item_link, title: 'View all groups') end end @@ -80,12 +75,7 @@ module QA def go_to_projects within_projects_menu do - # Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default - if has_element?(:menu_item_link, title: 'Your projects') - click_element(:menu_item_link, title: 'Your projects') - else - click_element(:menu_item_link, title: 'View all projects') - end + click_element(:menu_item_link, title: 'View all projects') end end diff --git a/qa/qa/support/formatters/allure_metadata_formatter.rb b/qa/qa/support/formatters/allure_metadata_formatter.rb index d1baf87799a..02719536b17 100644 --- a/qa/qa/support/formatters/allure_metadata_formatter.rb +++ b/qa/qa/support/formatters/allure_metadata_formatter.rb @@ -39,6 +39,7 @@ module QA add_failure_issues_link(example) add_ci_job_link(example) set_flaky_status(example) + set_behaviour_categories(example) end private @@ -97,6 +98,19 @@ module QA log(:error, "Failed to add spec pass rate data for example '#{example.description}', error: #{e}") end + # Add behaviour categories to report + # + # @param [RSpec::Core::Example] example + # @return [void] + def set_behaviour_categories(example) + file_path = example.file_path.gsub('./qa/specs/features', '') + devops_stage = file_path.match(%r{\d{1,2}_(\w+)/})&.captures&.first + product_group = example.metadata[:product_group] + + example.epic(devops_stage) if devops_stage + example.feature(product_group) if product_group + end + # Flaky specs with pass rate below 98% # # @return [Array] @@ -107,7 +121,7 @@ module QA runs = records.count failed = records.count { |r| r.values["status"] == "failed" } - pass_rate = 100 - ((failed.to_f / runs.to_f) * 100) + pass_rate = 100 - ((failed.to_f / runs) * 100) # Consider spec with a pass rate less than 98% as flaky result[records.last.values["testcase"]] = pass_rate if pass_rate < 98 diff --git a/qa/qa/support/formatters/test_stats_formatter.rb b/qa/qa/support/formatters/test_stats_formatter.rb index 2cde2d0928e..818b0a61120 100644 --- a/qa/qa/support/formatters/test_stats_formatter.rb +++ b/qa/qa/support/formatters/test_stats_formatter.rb @@ -72,6 +72,7 @@ module QA merge_request: merge_request, run_type: run_type, stage: devops_stage(file_path), + product_group: example.metadata[:product_group], testcase: example.metadata[:testcase] }, fields: { diff --git a/qa/spec/support/formatters/test_stats_formatter_spec.rb b/qa/spec/support/formatters/test_stats_formatter_spec.rb index d0d89b5ee73..a8e0ae62280 100644 --- a/qa/spec/support/formatters/test_stats_formatter_spec.rb +++ b/qa/spec/support/formatters/test_stats_formatter_spec.rb @@ -28,6 +28,7 @@ describe QA::Support::Formatters::TestStatsFormatter do let(:api_fabrication) { 0 } let(:fabrication_resources) { {} } let(:testcase) { 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234' } + let(:product_group) { nil } let(:influx_client_args) do { @@ -53,6 +54,7 @@ describe QA::Support::Formatters::TestStatsFormatter do merge_request: 'false', run_type: run_type, stage: stage.match(%r{\d{1,2}_(\w+)}).captures.first, + product_group: product_group, testcase: testcase }, fields: { @@ -146,6 +148,19 @@ describe QA::Support::Formatters::TestStatsFormatter do end end + context 'with product group tag' do + let(:product_group) { :import } + + it 'exports data to influxdb with correct reliable tag' do + run_spec do + it('spec', product_group: :import, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234') {} + end + + expect(influx_write_api).to have_received(:write).once + expect(influx_write_api).to have_received(:write).with(data: [data]) + end + end + context 'with smoke spec' do let(:smoke) { 'true' } diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index 488a4f84297..8de4c66c62f 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -56,7 +56,7 @@ RSpec.describe 'Value Stream Analytics', :js do end end - context "when there's value stream analytics data" do + context "when there's value stream analytics data", :sidekiq_inline do # NOTE: in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68595 travel back # 5 days in time before we create data for these specs, to mitigate some flakiness # So setting the date range to be the last 2 days should skip past the existing data @@ -103,7 +103,7 @@ RSpec.describe 'Value Stream Analytics', :js do end end - it 'shows data on each stage', :sidekiq_might_not_need_inline, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338332' do + it 'shows data on each stage', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338332' do expect_issue_to_be_present click_stage('Plan') @@ -207,11 +207,11 @@ RSpec.describe 'Value Stream Analytics', :js do wait_for_requests end - it 'does not show the commit stats' do + it 'does not show the commit stats', :sidekiq_inline do expect(page.find(metrics_selector)).not_to have_selector("#commits") end - it 'does not show restricted stages', :aggregate_failures do + it 'does not show restricted stages', :aggregate_failures, :sidekiq_inline do expect(find(stage_table_selector)).to have_content(issue.title) expect(page).to have_selector('.gl-path-nav-list-item', text: 'Issue') diff --git a/spec/fixtures/api/schemas/project_mirror.json b/spec/fixtures/api/schemas/project_mirror.json new file mode 100644 index 00000000000..0f626c04f24 --- /dev/null +++ b/spec/fixtures/api/schemas/project_mirror.json @@ -0,0 +1,48 @@ +{ + "type": "object", + "required": [ + "id", + "url", + "update_status", + "last_update_at", + "last_update_started_at", + "last_successful_update_at", + "last_error" + ], + "properties": { + "id": { + "type": "integer" + }, + "url": { + "type": "string" + }, + "update_status": { + "type": "string" + }, + "last_update_at": { + "type": [ + "string", + "null" + ] + }, + "last_update_started_at": { + "type": [ + "string", + "null" + ] + }, + "last_successful_update_at": { + "type": [ + "string", + "null" + ] + }, + "last_error": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/spec/frontend/fixtures/static/gl_field_errors.html b/spec/frontend/fixtures/static/gl_field_errors.html index f8470e02b7c..a53366fc29f 100644 --- a/spec/frontend/fixtures/static/gl_field_errors.html +++ b/spec/frontend/fixtures/static/gl_field_errors.html @@ -17,6 +17,9 @@
Custom, do not validate
+
+ +
Submit diff --git a/spec/frontend/gl_field_errors_spec.js b/spec/frontend/gl_field_errors_spec.js index 92d04927ee5..1f6929baa75 100644 --- a/spec/frontend/gl_field_errors_spec.js +++ b/spec/frontend/gl_field_errors_spec.js @@ -27,7 +27,7 @@ describe('GL Style Field Errors', () => { expect(testContext.fieldErrors).toBeDefined(); const { inputs } = testContext.fieldErrors.state; - expect(inputs.length).toBe(4); + expect(inputs.length).toBe(5); }); it('should ignore elements with custom error handling', () => { diff --git a/spec/graphql/types/deployment_details_type_spec.rb b/spec/graphql/types/deployment_details_type_spec.rb index 70fdc38019e..7dc0c8f97ac 100644 --- a/spec/graphql/types/deployment_details_type_spec.rb +++ b/spec/graphql/types/deployment_details_type_spec.rb @@ -10,7 +10,7 @@ RSpec.describe GitlabSchema.types['DeploymentDetails'] do id iid ref tag tags sha created_at updated_at finished_at status commit job triggerer ] - expect(described_class).to have_graphql_fields(*expected_fields) + expect(described_class).to include_graphql_fields(*expected_fields) end specify { expect(described_class).to require_graphql_authorizations(:read_deployment) } diff --git a/spec/helpers/nav/top_nav_helper_spec.rb b/spec/helpers/nav/top_nav_helper_spec.rb index 9c396d6bf25..0d43cfaae90 100644 --- a/spec/helpers/nav/top_nav_helper_spec.rb +++ b/spec/helpers/nav/top_nav_helper_spec.rb @@ -27,11 +27,9 @@ RSpec.describe Nav::TopNavHelper do let(:subject) { helper.top_nav_view_model(project: current_project, group: current_group) } - let(:menu_title) { 'Menu' } + let(:menu_tooltip) { 'Main menu' } before do - stub_feature_flags(new_navbar_layout: false) - allow(Gitlab::CurrentSettings).to receive(:admin_mode) { with_current_settings_admin_mode } allow(helper).to receive(:header_link?).with(:admin_mode) { with_header_link_admin_mode } @@ -46,8 +44,8 @@ RSpec.describe Nav::TopNavHelper do allow(helper).to receive(:dashboard_nav_link?).with(:activity) { with_activity } end - it 'has :menuTitle' do - expect(subject[:menuTitle]).to eq(menu_title) + it 'has :menuTooltip' do + expect(subject[:menuTooltip]).to eq(menu_tooltip) end context 'when current_user is nil (anonymous)' do @@ -108,7 +106,7 @@ RSpec.describe Nav::TopNavHelper do let(:current_user) { user } it 'has no menu items or views by default' do - expect(subject).to eq({ menuTitle: menu_title, + expect(subject).to eq({ menuTooltip: menu_tooltip, primary: [], secondary: [], shortcuts: [], @@ -176,74 +174,6 @@ RSpec.describe Nav::TopNavHelper do expect(projects_view[:linksSecondary]).to eq([]) end - context 'when extra submenu options are not hidden' do - before do - stub_feature_flags(remove_extra_primary_submenu_options: false) - end - - it 'has expected :linksPrimary' do - expected_links_primary = [ - ::Gitlab::Nav::TopNavMenuItem.build( - data: { - qa_selector: 'menu_item_link', - qa_title: 'Your projects', - **menu_data_tracking_attrs('your_projects') - }, - href: '/dashboard/projects', - id: 'your', - title: 'Your projects' - ), - ::Gitlab::Nav::TopNavMenuItem.build( - data: { - qa_selector: 'menu_item_link', - qa_title: 'Starred projects', - **menu_data_tracking_attrs('starred_projects') - }, - href: '/dashboard/projects/starred', - id: 'starred', - title: 'Starred projects' - ), - ::Gitlab::Nav::TopNavMenuItem.build( - data: { - qa_selector: 'menu_item_link', - qa_title: 'Explore projects', - **menu_data_tracking_attrs('explore_projects') - }, - href: '/explore', - id: 'explore', - title: 'Explore projects' - ), - ::Gitlab::Nav::TopNavMenuItem.build( - data: { - qa_selector: 'menu_item_link', - qa_title: 'Explore topics', - **menu_data_tracking_attrs('explore_topics') - }, - href: '/explore/projects/topics', - id: 'topics', - title: 'Explore topics' - ) - ] - expect(projects_view[:linksPrimary]).to eq(expected_links_primary) - end - - it 'has expected :linksSecondary' do - expected_links_secondary = [ - ::Gitlab::Nav::TopNavMenuItem.build( - data: { - qa_selector: 'menu_item_link', - qa_title: 'Create new project', - **menu_data_tracking_attrs('create_new_project') - }, - href: '/projects/new', - id: 'create', - title: 'Create new project' - ) - ] - expect(projects_view[:linksSecondary]).to eq(expected_links_secondary) - end - end - context 'with current nav as project' do before do helper.nav('project') @@ -341,54 +271,6 @@ RSpec.describe Nav::TopNavHelper do expect(groups_view[:linksSecondary]).to eq([]) end - context 'when extra submenu options are not hidden' do - before do - stub_feature_flags(remove_extra_primary_submenu_options: false) - end - - it 'has expected :linksPrimary' do - expected_links_primary = [ - ::Gitlab::Nav::TopNavMenuItem.build( - data: { - qa_selector: 'menu_item_link', - qa_title: 'Your groups', - **menu_data_tracking_attrs('your_groups') - }, - href: '/dashboard/groups', - id: 'your', - title: 'Your groups' - ), - ::Gitlab::Nav::TopNavMenuItem.build( - data: { - qa_selector: 'menu_item_link', - qa_title: 'Explore groups', - **menu_data_tracking_attrs('explore_groups') - }, - href: '/explore/groups', - id: 'explore', - title: 'Explore groups' - ) - ] - expect(groups_view[:linksPrimary]).to eq(expected_links_primary) - end - - it 'has expected :linksSecondary' do - expected_links_secondary = [ - ::Gitlab::Nav::TopNavMenuItem.build( - data: { - qa_selector: 'menu_item_link', - qa_title: 'Create group', - **menu_data_tracking_attrs('create_group') - }, - href: '/groups/new', - id: 'create', - title: 'Create group' - ) - ] - expect(groups_view[:linksSecondary]).to eq(expected_links_secondary) - end - end - context 'with external user' do let(:current_user) { external_user } diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 32518b867cb..21bba0c270d 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -278,6 +278,34 @@ RSpec.describe MergeRequest, factory_default: :keep do end describe 'callbacks' do + describe '#ensure_merge_request_diff' do + let(:merge_request) { build(:merge_request) } + + context 'when async_merge_request_diff_creation is true' do + before do + merge_request.skip_ensure_merge_request_diff = true + end + + it 'does not create a merge_request_diff after create' do + merge_request.save! + + expect(merge_request.merge_request_diff).to be_empty + end + end + + context 'when async_merge_request_diff_creation is false' do + before do + merge_request.skip_ensure_merge_request_diff = false + end + + it 'creates merge_request_diff after create' do + merge_request.save! + + expect(merge_request.merge_request_diff).not_to be_empty + end + end + end + describe '#ensure_merge_request_metrics' do let(:merge_request) { create(:merge_request) } diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 65540f86d34..370febf82ff 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'value stream analytics events' do let(:project) { create(:project, :repository, public_builds: false) } let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } - describe 'GET /:namespace/:project/value_stream_analytics/events/issues' do + describe 'GET /:namespace/:project/value_stream_analytics/events/issues', :sidekiq_inline do let(:first_issue_iid) { project.issues.sort_by_attribute(:created_desc).pick(:iid).to_s } let(:first_mr_iid) { project.merge_requests.sort_by_attribute(:created_desc).pick(:iid).to_s } @@ -65,7 +65,7 @@ RSpec.describe 'value stream analytics events' do expect(json_response['events'].first['iid']).to eq(first_mr_iid) end - it 'lists the staging events', :sidekiq_inline do + it 'lists the staging events' do get project_cycle_analytics_staging_path(project, format: :json) expect(json_response['events']).not_to be_empty diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index 0bc8258af42..0892b91032b 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -495,15 +495,40 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do project.add_developer(user) end - it 'creates the merge request', :sidekiq_might_not_need_inline do - expect_next_instance_of(MergeRequest) do |instance| - expect(instance).to receive(:eager_fetch_ref!).and_call_original + context 'when async_merge_request_diff_creation is enabled' do + before do + stub_feature_flags(async_merge_request_diff_creation: true) end - merge_request = described_class.new(project: project, current_user: user, params: opts).execute + it 'creates the merge request', :sidekiq_inline do + expect_next_instance_of(MergeRequest) do |instance| + expect(instance).not_to receive(:eager_fetch_ref!) + end - expect(merge_request).to be_persisted - expect(merge_request.iid).to be > 0 + merge_request = described_class.new(project: project, current_user: user, params: opts).execute + + expect(merge_request).to be_persisted + expect(merge_request.iid).to be > 0 + expect(merge_request.merge_request_diff).not_to be_empty + end + end + + context 'when async_merge_request_diff_creation is disabled' do + before do + stub_feature_flags(async_merge_request_diff_creation: false) + end + + it 'creates the merge request' do + expect_next_instance_of(MergeRequest) do |instance| + expect(instance).to receive(:eager_fetch_ref!).and_call_original + end + + merge_request = described_class.new(project: project, current_user: user, params: opts).execute + + expect(merge_request).to be_persisted + expect(merge_request.iid).to be > 0 + expect(merge_request.merge_request_diff).not_to be_empty + end end it 'does not create the merge request when the target project is archived' do