From 3f00efa7c563554aa6c797196a4eeef809a9afc7 Mon Sep 17 00:00:00 2001 From: Vasilii Iakliushin <viakliushin@gitlab.com> Date: Fri, 28 Feb 2025 11:20:26 +0100 Subject: [PATCH] Add a upper limit for merge request diffs versions Contributes to https://gitlab.com/gitlab-org/gitlab/-/issues/517497 **Problem** The upper limit for merge request diffs version is missing. **Solution** * Stop creating new diffs versions after reaching the limit. * Display an error message to the user --- .../projects/merge_requests_controller.rb | 10 ++++++++ app/models/merge_request.rb | 7 ++++++ .../merge_requests/reload_diffs_service.rb | 3 +++ .../merge_requests_diffs_limit.yml | 9 +++++++ locale/gitlab.pot | 3 +++ .../merge_requests_controller_spec.rb | 24 +++++++++++++++++++ .../reload_diffs_service_spec.rb | 20 ++++++++++++++++ 7 files changed, 76 insertions(+) create mode 100644 config/feature_flags/gitlab_com_derisk/merge_requests_diffs_limit.yml diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 7caa2be78f219..164d10ff52676 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -411,6 +411,8 @@ def show_merge_request ) end + display_max_limit_warning + respond_to do |format| format.html do # use next to appease Rubocop @@ -685,6 +687,14 @@ def diffs_stream_resource_url(merge_request, offset, diff_view) view: diff_view ) end + + def display_max_limit_warning + return unless @merge_request.reached_versions_limit? + + flash[:alert] = format( + _("This merge request has reached the maximum limit of %{limit} versions and cannot be updated further. " \ + "Close this merge request and create a new one instead."), limit: MergeRequest::DIFF_VERSION_LIMIT) + end end Projects::MergeRequestsController.prepend_mod_with('Projects::MergeRequestsController') diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 3e4cb7be7e61e..858a46031087a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -39,6 +39,7 @@ class MergeRequest < ApplicationRecord SORTING_PREFERENCE_FIELD = :merge_requests_sort CI_MERGE_REQUEST_DESCRIPTION_MAX_LENGTH = 2700 MERGE_LEASE_TIMEOUT = 15.minutes.to_i + DIFF_VERSION_LIMIT = 1_000 belongs_to :target_project, class_name: "Project" belongs_to :source_project, class_name: "Project" @@ -2454,6 +2455,12 @@ def squash_option delegate :squash_always?, :squash_never?, :squash_enabled_by_default?, :squash_readonly?, to: :squash_option + def reached_versions_limit? + return false if Feature.disabled?(:merge_requests_diffs_limit, target_project) + + merge_request_diffs.count >= DIFF_VERSION_LIMIT + end + private def merge_base_pipelines diff --git a/app/services/merge_requests/reload_diffs_service.rb b/app/services/merge_requests/reload_diffs_service.rb index 2c6ec9333b2d5..ab41ac7526e21 100644 --- a/app/services/merge_requests/reload_diffs_service.rb +++ b/app/services/merge_requests/reload_diffs_service.rb @@ -9,6 +9,9 @@ def initialize(merge_request, current_user) def execute old_diff_refs = merge_request.diff_refs + + return if merge_request.reached_versions_limit? + new_diff = merge_request.create_merge_request_diff clear_cache(new_diff) diff --git a/config/feature_flags/gitlab_com_derisk/merge_requests_diffs_limit.yml b/config/feature_flags/gitlab_com_derisk/merge_requests_diffs_limit.yml new file mode 100644 index 0000000000000..4311b5187f2d8 --- /dev/null +++ b/config/feature_flags/gitlab_com_derisk/merge_requests_diffs_limit.yml @@ -0,0 +1,9 @@ +--- +name: merge_requests_diffs_limit +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/517497 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/183063 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/521970 +milestone: '17.10' +group: group::source code +type: gitlab_com_derisk +default_enabled: false diff --git a/locale/gitlab.pot b/locale/gitlab.pot index fe24325e6d19b..eb13e8c2911a2 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -59421,6 +59421,9 @@ msgstr "" msgid "This merge request does not have codequality reports" msgstr "" +msgid "This merge request has reached the maximum limit of %{limit} versions and cannot be updated further. Close this merge request and create a new one instead." +msgstr "" + msgid "This merge request is closed. To apply this suggestion, edit this file directly." msgstr "" diff --git a/spec/requests/projects/merge_requests_controller_spec.rb b/spec/requests/projects/merge_requests_controller_spec.rb index adcaa807b009b..7555669b23b12 100644 --- a/spec/requests/projects/merge_requests_controller_spec.rb +++ b/spec/requests/projects/merge_requests_controller_spec.rb @@ -25,6 +25,30 @@ it { is_expected.to have_gitlab_http_status(:not_found) } end + + context 'when diff version limit is reached' do + before do + stub_const('MergeRequest::DIFF_VERSION_LIMIT', 1) + end + + it 'displays a warning' do + get project_merge_request_path(project, merge_request) + + expect(flash[:alert]).to include('This merge request has reached the maximum limit') + end + + context 'when "merge_requests_diffs_limit" feature flag is disabled' do + before do + stub_feature_flags(merge_requests_diffs_limit: false) + end + + it 'does not display a warning' do + get project_merge_request_path(project, merge_request) + + expect(flash[:alert]).to be_blank + end + end + end end describe 'GET #index' do diff --git a/spec/services/merge_requests/reload_diffs_service_spec.rb b/spec/services/merge_requests/reload_diffs_service_spec.rb index a6654989374a7..f9bee05c56674 100644 --- a/spec/services/merge_requests/reload_diffs_service_spec.rb +++ b/spec/services/merge_requests/reload_diffs_service_spec.rb @@ -32,6 +32,26 @@ subject.execute end + context 'when the number of diff versions reaches the limit' do + before do + stub_const('MergeRequest::DIFF_VERSION_LIMIT', 1) + end + + it 'does not create a new diff' do + expect { subject.execute }.not_to change { merge_request.merge_request_diffs.count } + end + + context 'when "merge_requests_diffs_limit" feature flag is disabled' do + before do + stub_feature_flags(merge_requests_diffs_limit: false) + end + + it 'creates new merge request diff' do + expect { subject.execute }.to change { merge_request.merge_request_diffs.count }.by(1) + end + end + end + context 'cache clearing' do it 'clears the cache for older diffs on the merge request' do expect_next_instance_of(Gitlab::Diff::FileCollection::MergeRequestDiff) do |instance| -- GitLab