From 123e09b957aa5c108a4a0d397c92a5aecfcfaf3a Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Thu, 25 Sep 2025 14:32:05 +0200 Subject: [PATCH 1/7] Filter by status with mapping Adds capability to filter by a status and take existing mappings to that status into account. Changelog: added EE: true --- ee/app/finders/work_items/status_filter.rb | 72 +++++- .../models/concerns/work_items/has_status.rb | 14 +- .../work_items/statuses/custom/mapping.rb | 16 ++ ee/spec/models/work_item_spec.rb | 77 ++++++- .../statuses/custom/mapping_spec.rb | 48 ++++ .../graphql/work_items/status_filters_spec.rb | 208 +++++++++++++++++- 6 files changed, 416 insertions(+), 19 deletions(-) diff --git a/ee/app/finders/work_items/status_filter.rb b/ee/app/finders/work_items/status_filter.rb index b46939f9dbcd95..05961620a074b6 100644 --- a/ee/app/finders/work_items/status_filter.rb +++ b/ee/app/finders/work_items/status_filter.rb @@ -3,28 +3,82 @@ module WorkItems class StatusFilter < ::Issuables::BaseFilter def filter(issuables) - return issuables unless status_param_present? - return issuables unless @parent&.root_ancestor&.licensed_feature_available?(:work_item_status) - return issuables unless issuables.respond_to?(:with_status) + return issuables unless can_filter_by_status?(issuables) - status = params.dig(:status, :id) - status = find_status_by_name(params.dig(:status, :name)) unless status.present? - - return issuables.none unless status.present? + statuses_for_filtering = find_statuses_for_filtering + return issuables.none unless statuses_for_filtering.present? - issuables.with_status(status) + apply_status_filters(issuables, statuses_for_filtering) end private + def can_filter_by_status?(issuables) + status_param_present? && + root_ancestor&.licensed_feature_available?(:work_item_status) && + issuables.respond_to?(:with_status) + end + def status_param_present? params[:status].to_h&.slice(:id, :name).present? end + def find_statuses_for_filtering + requested_status = find_requested_status + return [] unless requested_status + + statuses = [{ status: requested_status, mapping: nil }] + + # Add any statuses that map to the requested status + if requested_status.is_a?(::WorkItems::Statuses::Custom::Status) + statuses.concat(find_statuses_mapping_to(requested_status)) + end + + statuses.uniq + end + + def find_requested_status + status = params.dig(:status, :id) + status = find_status_by_name(params.dig(:status, :name)) unless status.present? + status + end + def find_status_by_name(name) return unless name.present? - ::WorkItems::Statuses::Finder.new(@parent.root_ancestor, { 'name' => name }).execute + ::WorkItems::Statuses::Finder.new(root_ancestor, { 'name' => name }).execute + end + + def find_statuses_mapping_to(status) + mappings_to_status = load_cached_mappings(root_ancestor).select { |m| m.new_status_id == status.id } + return [] if mappings_to_status.empty? + + mappings_to_status.map do |mapping| + { + status: mapping.old_status, + mapping: mapping + } + end + end + + def load_cached_mappings(namespace) + cache_key = "work_items:status_mappings_for_filter:#{namespace.id}" + + ::Gitlab::SafeRequestStore.fetch(cache_key) do + ::WorkItems::Statuses::Custom::Mapping + .with_namespace_id(namespace.id) + .includes(:old_status) # rubocop:disable CodeReuse/ActiveRecord -- Preloading depends on the context + end + end + + def apply_status_filters(issuables, statuses_for_filtering) + statuses_for_filtering.reduce(issuables.none) do |relation, status_mapping| + relation.or(issuables.with_status(status_mapping[:status], status_mapping[:mapping])) + end + end + + def root_ancestor + @parent&.root_ancestor end end end diff --git a/ee/app/models/concerns/work_items/has_status.rb b/ee/app/models/concerns/work_items/has_status.rb index 091f9d094cefa4..8185efc98849a2 100644 --- a/ee/app/models/concerns/work_items/has_status.rb +++ b/ee/app/models/concerns/work_items/has_status.rb @@ -8,15 +8,25 @@ module HasStatus has_one :current_status, class_name: 'WorkItems::Statuses::CurrentStatus', foreign_key: 'work_item_id', inverse_of: :work_item - scope :with_status, ->(status) { + scope :with_status, ->(status, mapping = nil) { relation = left_joins(:current_status) if status.is_a?(::WorkItems::Statuses::SystemDefined::Status) relation = with_system_defined_status(status) else + matching_condition = { work_item_current_statuses: { custom_status_id: status.id } } + + if mapping.present? + matching_condition[:work_item_type_id] = mapping.work_item_type_id + + if mapping.time_constrained? + matching_condition[:work_item_current_statuses][:updated_at] = mapping.time_range + end + end + relation = relation .where.not(work_item_current_statuses: { custom_status_id: nil }) - .where(work_item_current_statuses: { custom_status_id: status.id }) + .where(matching_condition) if status.converted_from_system_defined_status_identifier.present? system_defined_status = WorkItems::Statuses::SystemDefined::Status.find( diff --git a/ee/app/models/work_items/statuses/custom/mapping.rb b/ee/app/models/work_items/statuses/custom/mapping.rb index b90eb59fdff989..fcbc30f8d23c6d 100644 --- a/ee/app/models/work_items/statuses/custom/mapping.rb +++ b/ee/app/models/work_items/statuses/custom/mapping.rb @@ -28,6 +28,22 @@ def applicable_for?(date) (valid_from.nil? || valid_from <= date) && (valid_until.nil? || valid_until > date) end + def time_range + return unless time_constrained? + + if valid_from.present? && valid_until.present? + valid_from..valid_until + elsif valid_from.present? + valid_from.. + elsif valid_until.present? + ..valid_until + end + end + + def time_constrained? + valid_from.present? || valid_until.present? + end + private def statuses_in_same_namespace diff --git a/ee/spec/models/work_item_spec.rb b/ee/spec/models/work_item_spec.rb index 80923af938eece..8f9017f9e7ec8d 100644 --- a/ee/spec/models/work_item_spec.rb +++ b/ee/spec/models/work_item_spec.rb @@ -627,7 +627,9 @@ end describe '.with_status' do - subject { described_class.with_status(status) } + let(:mapping) { nil } + + subject { described_class.with_status(status, mapping) } context 'with a system defined status' do let(:status) { system_defined_todo_status } @@ -668,6 +670,79 @@ is_expected.to contain_exactly(wi_custom) end end + + context 'with mapping' do + let_it_be(:wi_other_custom) { create(:work_item, project: project) } + + let(:status) { custom_status } + let(:valid_from) { nil } + let(:valid_until) { nil } + let(:mapping) do + create(:work_item_custom_status_mapping, + namespace: reusable_group, work_item_type: issue_work_item_type, + old_status: custom_status, new_status: lifecycle.default_open_status, + valid_from: valid_from, valid_until: valid_until) + end + + before_all do + create(:work_item_current_status, + work_item: wi_other_custom, custom_status: custom_status, updated_at: 2.days.ago) + end + + it 'returns items with matching current_status' do + is_expected.to contain_exactly(wi_custom, wi_other_custom) + end + + context 'with valid_from' do + let(:valid_from) { 1.day.ago } + + it 'returns items with matching current_status' do + is_expected.to contain_exactly(wi_custom) + end + + context 'and it covers both items' do + let(:valid_from) { 3.days.ago } + + it 'returns items with matching current_status' do + is_expected.to contain_exactly(wi_custom, wi_other_custom) + end + end + end + + context 'with valid_until' do + let(:valid_until) { 1.day.ago } + + it 'returns items with matching current_status' do + is_expected.to contain_exactly(wi_other_custom) + end + + context 'and it covers no item' do + let(:valid_until) { 3.days.ago } + + it 'returns no items' do + is_expected.to be_empty + end + end + end + + context 'with both valid_from and valid_until' do + let(:valid_from) { 1.day.ago } + let(:valid_until) { 1.day.from_now } + + it 'returns items with matching current_status' do + is_expected.to contain_exactly(wi_custom) + end + + context 'and it covers only the item with older status update' do + let(:valid_from) { 3.days.ago } + let(:valid_until) { 1.day.ago } + + it 'returns items with matching current_status' do + is_expected.to contain_exactly(wi_other_custom) + end + end + end + end end describe '.with_system_defined_status' do diff --git a/ee/spec/models/work_items/statuses/custom/mapping_spec.rb b/ee/spec/models/work_items/statuses/custom/mapping_spec.rb index 2e2b22483c2ae7..12d6b99f770e41 100644 --- a/ee/spec/models/work_items/statuses/custom/mapping_spec.rb +++ b/ee/spec/models/work_items/statuses/custom/mapping_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe WorkItems::Statuses::Custom::Mapping, feature_category: :team_planning do + using RSpec::Parameterized::TableSyntax + let_it_be_with_refind(:namespace) { create(:namespace) } let_it_be_with_refind(:other_namespace) { create(:namespace) } let_it_be_with_refind(:work_item_type) { create(:work_item_type) } @@ -490,6 +492,52 @@ end end + describe '#time_range' do + let(:mapping) { build(:work_item_custom_status_mapping, valid_from: valid_from, valid_until: valid_until) } + let(:from_time) { 5.days.ago } + let(:until_time) { 2.days.ago } + + subject(:time_range) { mapping.time_range } + + where(:valid_from, :valid_until, :expected_type, :expected_begin, :expected_end) do + ref(:from_time) | ref(:until_time) | Range | ref(:from_time) | ref(:until_time) + ref(:from_time) | nil | Range | ref(:from_time) | nil + nil | ref(:until_time) | Range | nil | ref(:until_time) + nil | nil | NilClass | nil | nil + end + + with_them do + it 'returns the expected range type and boundaries' do + if expected_type == NilClass + is_expected.to be_nil + else + is_expected.to be_a(Range) + expect(time_range.begin).to eq(expected_begin) + expect(time_range.end).to eq(expected_end) + end + end + end + end + + describe '#time_constrained?' do + let(:mapping) { build(:work_item_custom_status_mapping, valid_from: valid_from, valid_until: valid_until) } + + subject { mapping.time_constrained? } + + where(:valid_from, :valid_until, :expected_result) do + nil | nil | false + 3.days.ago | nil | true + nil | 1.day.from_now | true + 5.days.ago | 2.days.ago | true + 1.year.ago | nil | true + nil | 1.year.from_now | true + end + + with_them do + it { is_expected.to eq(expected_result) } + end + end + describe 'foreign key constraints' do let!(:mapping) do create(:work_item_custom_status_mapping, diff --git a/ee/spec/requests/api/graphql/work_items/status_filters_spec.rb b/ee/spec/requests/api/graphql/work_items/status_filters_spec.rb index 4e32b55e51d8fe..20e84be3753583 100644 --- a/ee/spec/requests/api/graphql/work_items/status_filters_spec.rb +++ b/ee/spec/requests/api/graphql/work_items/status_filters_spec.rb @@ -12,6 +12,9 @@ let(:board) { create(:board, resource_parent: resource_parent) } let(:label_list) { create(:list, board: board, label: group_label) } + let_it_be(:issue_work_item_type) { create(:work_item_type, :issue) } + let_it_be(:task_work_item_type) { create(:work_item_type, :task) } + let_it_be(:work_item_1) { create(:work_item, :issue, project: project, labels: [group_label]) } let_it_be(:work_item_2) { create(:work_item, :task, project: project, labels: [group_label]) } let_it_be(:work_item_3) { create(:work_item, :task, project: project, labels: [group_label]) } @@ -19,18 +22,21 @@ let(:current_user) { create(:user, guest_of: group) } + let(:expected_work_items) { [work_item_1, work_item_2, work_item_3] } + let(:expected_unfiltered_work_items) { [work_item_1, work_item_2, work_item_3, work_item_4] } + before do stub_licensed_features(work_item_status: true) end shared_examples 'a filtered list' do - it 'filters by status argument' do + it 'filters by status argument', :aggregate_failures do post_graphql(query, current_user: current_user) model_ids = items.map { |item| GlobalID.parse(item['id']).model_id.to_i } - expect(model_ids.size).to eq(3) - expect(model_ids).to contain_exactly(work_item_1.id, work_item_2.id, work_item_3.id) + expect(model_ids.size).to eq(expected_work_items.size) + expect(model_ids).to match_array(expected_work_items.map(&:id)) end end @@ -40,8 +46,8 @@ model_ids = items.map { |item| GlobalID.parse(item['id']).model_id.to_i } - expect(model_ids.size).to eq(4) - expect(model_ids).to contain_exactly(work_item_1.id, work_item_2.id, work_item_3.id, work_item_4.id) + expect(model_ids.size).to eq(expected_unfiltered_work_items.size) + expect(model_ids).to match_array(expected_unfiltered_work_items.map(&:id)) end end @@ -230,13 +236,13 @@ # We can't stub licensed features for let_it_be blocks. build(:work_item_type_custom_lifecycle, namespace: group, - work_item_type: create(:work_item_type, :issue), + work_item_type: issue_work_item_type, lifecycle: lifecycle ).save!(validate: false) build(:work_item_type_custom_lifecycle, namespace: group, - work_item_type: create(:work_item_type, :task), + work_item_type: task_work_item_type, lifecycle: lifecycle ).save!(validate: false) end @@ -253,5 +259,193 @@ end it_behaves_like 'filtering by status' + + # rubocop:disable RSpec/MultipleMemoizedHelpers -- we need additional memoization to fully test all paths + context 'with status mappings' do + let_it_be(:old_status) do + create(:work_item_custom_status, :without_conversion_mapping, namespace: group).tap do |status| + create(:work_item_custom_lifecycle_status, lifecycle: lifecycle, status: status) + end + end + + let_it_be(:new_status) do + create(:work_item_custom_status, :without_conversion_mapping, namespace: group).tap do |status| + create(:work_item_custom_lifecycle_status, lifecycle: lifecycle, status: status) + end + end + + let_it_be(:another_status) do + create(:work_item_custom_status, :without_conversion_mapping, namespace: group).tap do |status| + create(:work_item_custom_lifecycle_status, lifecycle: lifecycle, status: status) + end + end + + let_it_be(:converted_custom_status) do + create(:work_item_custom_status, :in_progress, namespace: group).tap do |status| + create(:work_item_custom_lifecycle_status, lifecycle: lifecycle, status: status) + end + end + + let_it_be(:work_item_issue_with_new_status) do + create(:work_item, :issue, project: project, labels: [group_label]).tap do |wi| + create(:work_item_current_status, :custom, work_item: wi, custom_status: new_status, updated_at: 1.day.ago) + end + end + + let_it_be(:work_item_issue_old) do + create(:work_item, :issue, project: project, labels: [group_label]).tap do |wi| + create(:work_item_current_status, :custom, work_item: wi, custom_status: old_status, updated_at: 1.day.ago) + end + end + + let_it_be(:work_item_issue_older) do + create(:work_item, :issue, project: project, labels: [group_label]).tap do |wi| + create(:work_item_current_status, :custom, work_item: wi, custom_status: old_status, updated_at: 5.days.ago) + end + end + + let_it_be(:work_item_task_recent) do + create(:work_item, :task, project: project, labels: [group_label]).tap do |wi| + create(:work_item_current_status, :custom, work_item: wi, custom_status: old_status, updated_at: 2.hours.ago) + end + end + + let_it_be(:work_item_issue_with_another_status) do + create(:work_item, :issue, project: project, labels: [group_label]).tap do |wi| + create(:work_item_current_status, :custom, work_item: wi, custom_status: another_status, + updated_at: 1.day.ago) + end + end + + let_it_be(:work_item_issue_with_system_status) do + create(:work_item, :issue, project: project, labels: [group_label]).tap do |wi| + # Skip validations since we are simulating an old record + # when the namespace still used the system defined lifecycle + build(:work_item_current_status, + work_item: wi, + system_defined_status_id: converted_custom_status.converted_from_system_defined_status_identifier, + updated_at: 1.day.ago + ).save!(validate: false) + end + end + + let(:status) { new_status } + + let(:expected_unfiltered_work_items) do + [work_item_1, work_item_2, work_item_3, work_item_4, work_item_issue_with_new_status, + work_item_issue_old, work_item_issue_older, work_item_task_recent, + work_item_issue_with_another_status, work_item_issue_with_system_status] + end + + context 'when unbounded mapping for both work item types is present' do + before_all do + [issue_work_item_type, task_work_item_type].each do |wit| + create(:work_item_custom_status_mapping, + namespace: group, + work_item_type: wit, + old_status: old_status, + new_status: new_status, + valid_from: nil, + valid_until: nil + ) + end + end + + let(:expected_work_items) do + [work_item_issue_with_new_status, work_item_issue_old, work_item_issue_older, + work_item_task_recent] + end + + it_behaves_like 'filtering by status' + end + + context 'when valid_until mapping for issues is present' do + before_all do + create(:work_item_custom_status_mapping, + namespace: group, + work_item_type: issue_work_item_type, + old_status: old_status, + new_status: new_status, + valid_from: nil, + valid_until: 3.days.ago + ) + end + + let(:expected_work_items) { [work_item_issue_with_new_status, work_item_issue_older] } + + it_behaves_like 'filtering by status' + end + + context 'when two mappings for issues are present with different time constraints' do + before_all do + create(:work_item_custom_status_mapping, + namespace: group, + work_item_type: issue_work_item_type, + old_status: old_status, + new_status: new_status, + valid_from: nil, + valid_until: 3.days.ago + ) + create(:work_item_custom_status_mapping, + namespace: group, + work_item_type: issue_work_item_type, + old_status: old_status, + new_status: new_status, + valid_from: 2.days.ago, + valid_until: nil + ) + end + + let(:expected_work_items) { [work_item_issue_with_new_status, work_item_issue_old, work_item_issue_older] } + + it_behaves_like 'filtering by status' + end + + context 'when two mappings for issues are present to different statuses' do + before_all do + create(:work_item_custom_status_mapping, + namespace: group, + work_item_type: issue_work_item_type, + old_status: old_status, + new_status: new_status, + valid_from: nil, + valid_until: nil + ) + create(:work_item_custom_status_mapping, + namespace: group, + work_item_type: issue_work_item_type, + old_status: another_status, + new_status: new_status, + valid_from: nil, + valid_until: nil + ) + end + + let(:expected_work_items) do + [work_item_issue_with_new_status, work_item_issue_old, work_item_issue_older, + work_item_issue_with_another_status] + end + + it_behaves_like 'filtering by status' + end + + context 'when mapping for tasks exists for converted system-defined status' do + before_all do + create(:work_item_custom_status_mapping, + namespace: group, + work_item_type: task_work_item_type, + old_status: converted_custom_status, + new_status: new_status, + valid_from: nil, + valid_until: nil + ) + end + + let(:expected_work_items) { [work_item_issue_with_new_status, work_item_issue_with_system_status] } + + it_behaves_like 'filtering by status' + end + end + # rubocop:enable RSpec/MultipleMemoizedHelpers end end -- GitLab From 9a9d309d96dafe440cae738f1b2504450090bd98 Mon Sep 17 00:00:00 2001 From: Rehab Hassanein Date: Thu, 25 Sep 2025 16:47:43 +0300 Subject: [PATCH 2/7] try @igorwwwwwwwwwwwwwwwwwwww's suggestion --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 518e57d3d96251..9507b849ad4d83 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -222,7 +222,7 @@ variables: BUNDLE_FROZEN: "true" BUNDLE_GEMFILE: Gemfile # we override the max_old_space_size to prevent OOM errors - NODE_OPTIONS: --max-old-space-size=10240 + NODE_OPTIONS: --max-old-space-size=7680 GIT_DEPTH: "20" # 'GIT_STRATEGY: clone' optimizes the pack-objects cache hit ratio GIT_STRATEGY: "clone" -- GitLab From 990d6e726acf10fc8c6b2ce794724af23d8abcd5 Mon Sep 17 00:00:00 2001 From: Rehab Hassanein Date: Thu, 25 Sep 2025 16:50:36 +0300 Subject: [PATCH 3/7] try @igorwwwwwwwwwwwwwwwwwwww's suggestion - amended --- .gitlab-ci.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9507b849ad4d83..db5b1eacf07182 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,7 +24,7 @@ stages: default: image: $DEFAULT_CI_IMAGE tags: - - $DEFAULT_JOB_TAG + - gitlab-org-experimental # All jobs are interruptible by default interruptible: true # Default job timeout doesn't work: https://gitlab.com/gitlab-org/gitlab/-/issues/387528 diff --git a/README.md b/README.md index a28aae47c0f28f..a65c04100844fc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GitLab +# GitLab - Experiment ## Canonical source -- GitLab From e6d77b23dc96314032093f4ee371ab961e6df166 Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Thu, 25 Sep 2025 16:36:59 +0200 Subject: [PATCH 4/7] Applies Duo review feedback we need to fetch the data from the database in the fetch block. If we don't we cache the relation and not the data. --- ee/app/finders/work_items/status_filter.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ee/app/finders/work_items/status_filter.rb b/ee/app/finders/work_items/status_filter.rb index 05961620a074b6..f7388f5ad37dc7 100644 --- a/ee/app/finders/work_items/status_filter.rb +++ b/ee/app/finders/work_items/status_filter.rb @@ -68,6 +68,7 @@ def load_cached_mappings(namespace) ::WorkItems::Statuses::Custom::Mapping .with_namespace_id(namespace.id) .includes(:old_status) # rubocop:disable CodeReuse/ActiveRecord -- Preloading depends on the context + .to_a end end -- GitLab From e611b2c507289dbf183b567a13106cfc64219bdc Mon Sep 17 00:00:00 2001 From: Rehab Hassanein Date: Thu, 25 Sep 2025 18:28:40 +0300 Subject: [PATCH 5/7] lower build memory to 5120 across files --- .gitlab-ci.yml | 2 +- .gitlab/ci/frontend.gitlab-ci.yml | 2 +- lib/tasks/gitlab/assets.rake | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index db5b1eacf07182..893f55ef026067 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -222,7 +222,7 @@ variables: BUNDLE_FROZEN: "true" BUNDLE_GEMFILE: Gemfile # we override the max_old_space_size to prevent OOM errors - NODE_OPTIONS: --max-old-space-size=7680 + NODE_OPTIONS: --max-old-space-size=5120 GIT_DEPTH: "20" # 'GIT_STRATEGY: clone' optimizes the pack-objects cache hit ratio GIT_STRATEGY: "clone" diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index fde30dc5ea1815..c9917352d2b79a 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -302,7 +302,7 @@ jest-build-cache: .vue3: variables: VUE_VERSION: 3 - NODE_OPTIONS: --max-old-space-size=7680 + NODE_OPTIONS: --max-old-space-size=5120 allow_failure: true .with-jest-build-cache-vue3-ensure-compilable-sfcs-needs: diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake index 4f1663a3db69ae..5cf9c68f34b4ce 100644 --- a/lib/tasks/gitlab/assets.rake +++ b/lib/tasks/gitlab/assets.rake @@ -127,7 +127,7 @@ namespace :gitlab do log_path_message += "\nYou can inspect the webpack full log here: #{ENV['CI_JOB_URL']}/artifacts/file/#{log_path}" if ENV['CI_JOB_URL'] end - ENV['NODE_OPTIONS'] = '--max-old-space-size=8192' if ENV.has_key?('CI') + ENV['NODE_OPTIONS'] = '--max-old-space-size=5120' if ENV.has_key?('CI') ENV['NODE_OPTIONS'] = '--max-old-space-size=16384' if ENV['GITLAB_LARGE_RUNNER_OPTIONAL'] == "saas-linux-large-amd64" unless system(cmd) diff --git a/package.json b/package.json index ded02f5afd5f15..ac79bbafd376b0 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "webpack": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=5120}\" webpack --config config/webpack.config.js", "webpack-vendor": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=5120}\" webpack --config config/webpack.vendor.config.js", "webpack-prod": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=5120}\" NODE_ENV=production webpack --config config/webpack.config.js", - "vite-prod": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=8000}\" NODE_ENV=production vite build" + "vite-prod": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=5120}\" NODE_ENV=production vite build" }, "dependencies": { "@apollo/client": "^3.5.10", -- GitLab From ca759b88afb882e6efef6ff7e77fd1ae66ebaf63 Mon Sep 17 00:00:00 2001 From: Rehab Hassanein Date: Fri, 26 Sep 2025 12:55:17 +0300 Subject: [PATCH 6/7] Test caching -- GitLab From 38737f99955cdd15a58c3fdc399bd6183b57e0a4 Mon Sep 17 00:00:00 2001 From: Rehab Hassanein Date: Fri, 26 Sep 2025 15:38:47 +0300 Subject: [PATCH 7/7] bump up resources to match hosted runners --- .gitlab-ci.yml | 2 +- .gitlab/ci/frontend.gitlab-ci.yml | 2 +- package.json | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7fd9476548382b..7aff9d0fd5e8dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -223,7 +223,7 @@ variables: BUNDLE_FROZEN: "true" BUNDLE_GEMFILE: Gemfile # we override the max_old_space_size to prevent OOM errors - NODE_OPTIONS: --max-old-space-size=5120 + NODE_OPTIONS: --max-old-space-size=7000 GIT_DEPTH: "20" # 'GIT_STRATEGY: clone' optimizes the pack-objects cache hit ratio GIT_STRATEGY: "clone" diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index 2193bbe7c3f44f..d36739f4d522f9 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -268,7 +268,7 @@ jest-build-cache: .vue3: variables: VUE_VERSION: 3 - NODE_OPTIONS: --max-old-space-size=5120 + NODE_OPTIONS: --max-old-space-size=7000 allow_failure: true .with-jest-build-cache-vue3-ensure-compilable-sfcs-needs: diff --git a/package.json b/package.json index 5bc1845032b7ed..2fb21017e237bd 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "deps:check": "depcruise --config config/dependency_cruiser.js --no-ignore-known", "deps:check:all": "yarn run deps:check .", "clean": "rm -rf public/assets tmp/cache/webpack tmp/cache/vite", - "dev-server": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=5120}\" node scripts/frontend/webpack_dev_server.js", + "dev-server": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=7000}\" node scripts/frontend/webpack_dev_server.js", "file-coverage": "scripts/frontend/file_test_coverage.js", "lint-docs": "scripts/lint-doc.sh", "internal:eslint": "eslint --cache --max-warnings 0 --report-unused-disable-directives", @@ -47,10 +47,10 @@ "storybook:start": "./scripts/frontend/start_storybook.sh", "storybook:start:skip-fixtures-update": "./scripts/frontend/start_storybook.sh --skip-fixtures-update", "swagger:validate": "swagger-cli validate", - "webpack": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=5120}\" webpack --config config/webpack.config.js", - "webpack-vendor": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=5120}\" webpack --config config/webpack.vendor.config.js", - "webpack-prod": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=5120}\" NODE_ENV=production webpack --config config/webpack.config.js", - "vite-prod": "yarn run tailwindcss:build && yarn run tailwindcss:cqs:build && NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=5120}\" NODE_ENV=production vite build" + "webpack": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=7000}\" webpack --config config/webpack.config.js", + "webpack-vendor": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=7000}\" webpack --config config/webpack.vendor.config.js", + "webpack-prod": "NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=7000}\" NODE_ENV=production webpack --config config/webpack.config.js", + "vite-prod": "yarn run tailwindcss:build && yarn run tailwindcss:cqs:build && NODE_OPTIONS=\"${NODE_OPTIONS:=--max-old-space-size=7000}\" NODE_ENV=production vite build" }, "dependencies": { "@apollo/client": "^3.5.10", -- GitLab