-
+
{{ __('Ready to merge!') }}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
index e1c54a8827c..b80b5246e24 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
@@ -1,48 +1,48 @@
-
-
-
-
![]()
-
-
-
- {{ s__('mrWidgetNothingToMerge|Merge request contains no changes') }}
-
-
-
-
- {{ content }}
-
-
- {{ content }}
-
-
-
-
+
+
+
+
+ {{ s__('mrWidgetNothingToMerge|Merge request contains no changes') }}
+
+
+
+
+ {{ content }}
+
+
+ {{ content }}
+
+
+
diff --git a/app/mailers/emails/service_desk.rb b/app/mailers/emails/service_desk.rb
index f67c2636fc6..a421e76e0de 100644
--- a/app/mailers/emails/service_desk.rb
+++ b/app/mailers/emails/service_desk.rb
@@ -7,6 +7,7 @@ module Emails
include ::ServiceDesk::CustomEmails::Logger
EMAIL_ATTACHMENTS_SIZE_LIMIT = 10.megabytes.freeze
+ VERIFICATION_EMAIL_TIMEOUT = 7
included do
layout 'service_desk', only: [:service_desk_thank_you_email, :service_desk_new_note_email]
@@ -146,7 +147,15 @@ module Emails
log_info(project: @project)
- mail.delivery_method(::Mail::SMTP, @service_desk_setting.custom_email_credential.delivery_options)
+ delivery_options = @service_desk_setting.custom_email_credential.delivery_options
+ # We force the use of custom email settings when sending out the verification email.
+ # If the credentials aren't correct some servers tend to take a while to answer
+ # which leads to some Net::ReadTimeout errors which disguises the
+ # real configuration issue.
+ # We increase the timeout for verification emails only.
+ delivery_options[:read_timeout] = VERIFICATION_EMAIL_TIMEOUT if force
+
+ mail.delivery_method(::Mail::SMTP, delivery_options)
end
def service_desk_custom_email_enabled?
diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb
index 349625a90f6..f684feacace 100644
--- a/app/models/project_setting.rb
+++ b/app/models/project_setting.rb
@@ -11,8 +11,6 @@ class ProjectSetting < ApplicationRecord
scope :for_projects, ->(projects) { where(project_id: projects) }
- ignore_column :jitsu_key, remove_with: '16.7', remove_after: '2023-11-17'
-
attr_encrypted :cube_api_key,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_32,
diff --git a/app/models/vulnerability.rb b/app/models/vulnerability.rb
index 0e3fe2cc8ac..abdf585af81 100644
--- a/app/models/vulnerability.rb
+++ b/app/models/vulnerability.rb
@@ -5,6 +5,8 @@ class Vulnerability < ApplicationRecord
include EachBatch
include IgnorableColumns
+ ignore_column :milestone_id, remove_with: '16.9', remove_after: '2023-01-13'
+
alias_attribute :vulnerability_id, :id
scope :with_projects, -> { includes(:project) }
diff --git a/app/services/bulk_imports/process_service.rb b/app/services/bulk_imports/process_service.rb
index 7a6a883f1a9..2d9a0d6a6c9 100644
--- a/app/services/bulk_imports/process_service.rb
+++ b/app/services/bulk_imports/process_service.rb
@@ -32,7 +32,9 @@ module BulkImports
entity.start!
- BulkImports::ExportRequestWorker.perform_async(entity.id)
+ Gitlab::ApplicationContext.with_context(bulk_import_entity_id: entity.id) do
+ BulkImports::ExportRequestWorker.perform_async(entity.id)
+ end
end
end
diff --git a/app/views/projects/branch_defaults/_default_branch_fields.html.haml b/app/views/projects/branch_defaults/_default_branch_fields.html.haml
index 78ce43ca8c9..f3a7e93a5f7 100644
--- a/app/views/projects/branch_defaults/_default_branch_fields.html.haml
+++ b/app/views/projects/branch_defaults/_default_branch_fields.html.haml
@@ -1,3 +1,13 @@
+- change_default_disabled = @default_branch_blocked_by_security_policy
+- popover_data = {}
+
+- if change_default_disabled
+ - tag_pair_security_policies_page = tag_pair(link_to('', namespace_project_security_policies_path, target: '_blank', rel: 'noopener noreferrer'), :security_policies_link_start, :security_policies_link_end)
+ - tag_pair_security_policies_docs = tag_pair(link_to('', help_page_path('user/application_security/policies/scan-result-policies'), target: '_blank', rel: 'noopener noreferrer'), :learn_more_link_start, :learn_more_link_end)
+ - popover_content = safe_format(s_("SecurityOrchestration|You can't change the default branch because its protection is enforced by one or more %{security_policies_link_start}security policies%{security_policies_link_end}. %{learn_more_link_start}Learn more%{learn_more_link_end}."), tag_pair_security_policies_docs, tag_pair_security_policies_page)
+ - popover_title = s_("SecurityOrchestration|Security policy overwrites this setting")
+ - popover_data = { container: 'body', toggle: 'popover', html: 'true', triggers: 'hover', title: popover_title, content: popover_content }
+
%fieldset#default-branch-settings
- if @project.empty_repo?
.text-secondary
@@ -6,8 +16,8 @@
.form-group
= f.label :default_branch, _("Default branch"), class: 'label-bold'
%p= s_('ProjectSettings|All merge requests and commits are made against this branch unless you specify a different one.')
- .gl-form-input-xl
- .js-select-default-branch{ data: { default_branch: @project.default_branch, project_id: @project.id } }
+ .gl-form-input-xl{ data: { **popover_data } }
+ .js-select-default-branch{ data: { default_branch: @project.default_branch, project_id: @project.id, disabled: change_default_disabled.to_s } }
.form-group
- help_text = _("When merge requests and commits in the default branch close, any issues they reference also close.")
diff --git a/app/workers/bulk_imports/entity_worker.rb b/app/workers/bulk_imports/entity_worker.rb
index e510a8c0d06..caee292a504 100644
--- a/app/workers/bulk_imports/entity_worker.rb
+++ b/app/workers/bulk_imports/entity_worker.rb
@@ -49,7 +49,9 @@ module BulkImports
attr_reader :entity
def re_enqueue
- BulkImports::EntityWorker.perform_in(PERFORM_DELAY, entity.id)
+ with_context(bulk_import_entity_id: entity.id) do
+ BulkImports::EntityWorker.perform_in(PERFORM_DELAY, entity.id)
+ end
end
def running_tracker
@@ -66,11 +68,13 @@ module BulkImports
next_pipeline_trackers.each_with_index do |pipeline_tracker, index|
log_info(message: 'Stage starting', entity_stage: pipeline_tracker.stage) if index == 0
- BulkImports::PipelineWorker.perform_async(
- pipeline_tracker.id,
- pipeline_tracker.stage,
- entity.id
- )
+ with_context(bulk_import_entity_id: entity.id) do
+ BulkImports::PipelineWorker.perform_async(
+ pipeline_tracker.id,
+ pipeline_tracker.stage,
+ entity.id
+ )
+ end
end
end
diff --git a/app/workers/bulk_imports/export_request_worker.rb b/app/workers/bulk_imports/export_request_worker.rb
index f7456ddccb1..54815c05c67 100644
--- a/app/workers/bulk_imports/export_request_worker.rb
+++ b/app/workers/bulk_imports/export_request_worker.rb
@@ -20,7 +20,9 @@ module BulkImports
set_source_xid
request_export
- BulkImports::EntityWorker.perform_async(entity_id)
+ with_context(bulk_import_entity_id: entity_id) do
+ BulkImports::EntityWorker.perform_async(entity_id)
+ end
end
def perform_failure(exception, entity_id)
diff --git a/app/workers/bulk_imports/finish_batched_pipeline_worker.rb b/app/workers/bulk_imports/finish_batched_pipeline_worker.rb
index 40d26e14dc1..73ae2188ac4 100644
--- a/app/workers/bulk_imports/finish_batched_pipeline_worker.rb
+++ b/app/workers/bulk_imports/finish_batched_pipeline_worker.rb
@@ -38,7 +38,9 @@ module BulkImports
attr_reader :tracker
def re_enqueue
- self.class.perform_in(REQUEUE_DELAY, tracker.id)
+ with_context(bulk_import_entity_id: tracker.entity.id) do
+ self.class.perform_in(REQUEUE_DELAY, tracker.id)
+ end
end
def import_in_progress?
diff --git a/app/workers/bulk_imports/pipeline_batch_worker.rb b/app/workers/bulk_imports/pipeline_batch_worker.rb
index 1485275e616..957669d4a66 100644
--- a/app/workers/bulk_imports/pipeline_batch_worker.rb
+++ b/app/workers/bulk_imports/pipeline_batch_worker.rb
@@ -42,6 +42,7 @@ module BulkImports
@batch = ::BulkImports::BatchTracker.find(batch_id)
@tracker = @batch.tracker
+ @entity = @tracker.entity
@pending_retry = false
return unless process_batch?
@@ -50,7 +51,11 @@ module BulkImports
try_obtain_lease { run }
ensure
- ::BulkImports::FinishBatchedPipelineWorker.perform_async(tracker.id) unless pending_retry
+ unless pending_retry
+ with_context(bulk_import_entity_id: entity.id) do
+ ::BulkImports::FinishBatchedPipelineWorker.perform_async(tracker.id)
+ end
+ end
end
def perform_failure(batch_id, exception)
@@ -62,7 +67,7 @@ module BulkImports
private
- attr_reader :batch, :tracker, :pending_retry
+ attr_reader :batch, :tracker, :pending_retry, :entity
def run
return batch.skip! if tracker.failed? || tracker.finished?
@@ -83,7 +88,7 @@ module BulkImports
Gitlab::ErrorTracking.track_exception(exception, log_attributes(message: 'Batch tracker failed'))
BulkImports::Failure.create(
- bulk_import_entity_id: batch.tracker.entity.id,
+ bulk_import_entity_id: tracker.entity.id,
pipeline_class: tracker.pipeline_name,
pipeline_step: 'pipeline_batch_worker_run',
exception_class: exception.class.to_s,
@@ -91,7 +96,9 @@ module BulkImports
correlation_id_value: Labkit::Correlation::CorrelationId.current_or_new_id
)
- ::BulkImports::FinishBatchedPipelineWorker.perform_async(tracker.id)
+ with_context(bulk_import_entity_id: tracker.entity.id) do
+ ::BulkImports::FinishBatchedPipelineWorker.perform_async(tracker.id)
+ end
end
def context
@@ -115,7 +122,9 @@ module BulkImports
def re_enqueue(delay = FILE_EXTRACTION_PIPELINE_PERFORM_DELAY)
log_extra_metadata_on_done(:re_enqueue, true)
- self.class.perform_in(delay, batch.id)
+ with_context(bulk_import_entity_id: entity.id) do
+ self.class.perform_in(delay, batch.id)
+ end
end
def process_batch?
diff --git a/app/workers/bulk_imports/pipeline_worker.rb b/app/workers/bulk_imports/pipeline_worker.rb
index 2c1d28b33c5..4bf6da73fdf 100644
--- a/app/workers/bulk_imports/pipeline_worker.rb
+++ b/app/workers/bulk_imports/pipeline_worker.rb
@@ -124,12 +124,14 @@ module BulkImports
def re_enqueue(delay = FILE_EXTRACTION_PIPELINE_PERFORM_DELAY)
log_extra_metadata_on_done(:re_enqueue, true)
- self.class.perform_in(
- delay,
- pipeline_tracker.id,
- pipeline_tracker.stage,
- entity.id
- )
+ with_context(bulk_import_entity_id: entity.id) do
+ self.class.perform_in(
+ delay,
+ pipeline_tracker.id,
+ pipeline_tracker.stage,
+ entity.id
+ )
+ end
end
def context
@@ -218,7 +220,9 @@ module BulkImports
1.upto(export_status.batches_count) do |batch_number|
batch = pipeline_tracker.batches.find_or_create_by!(batch_number: batch_number) # rubocop:disable CodeReuse/ActiveRecord
- ::BulkImports::PipelineBatchWorker.perform_async(batch.id)
+ with_context(bulk_import_entity_id: entity.id) do
+ ::BulkImports::PipelineBatchWorker.perform_async(batch.id)
+ end
end
end
end
diff --git a/app/workers/click_house/events_sync_worker.rb b/app/workers/click_house/events_sync_worker.rb
index e884a43b1e3..5936d30b8b2 100644
--- a/app/workers/click_house/events_sync_worker.rb
+++ b/app/workers/click_house/events_sync_worker.rb
@@ -4,6 +4,7 @@ module ClickHouse
class EventsSyncWorker
include ApplicationWorker
include Gitlab::ExclusiveLeaseHelpers
+ include Gitlab::Utils::StrongMemoize
idempotent!
queue_namespace :cronjob
@@ -91,6 +92,11 @@ module ClickHouse
)
end
+ def last_event_id_in_postgresql
+ Event.maximum(:id)
+ end
+ strong_memoize_attr :last_event_id_in_postgresql
+
def enabled?
ClickHouse::Client.configuration.databases[:main].present? && Feature.enabled?(:event_sync_worker_for_click_house)
end
@@ -110,24 +116,34 @@ module ClickHouse
def process_batch(context)
Enumerator.new do |yielder|
- has_data = false
- # rubocop: disable CodeReuse/ActiveRecord
- Event.where(Event.arel_table[:id].gt(context.last_record_id)).each_batch(of: BATCH_SIZE) do |relation|
- has_data = true
-
- relation.select(*EVENT_PROJECTIONS).each do |row|
+ has_more_data = false
+ batching_scope.each_batch(of: BATCH_SIZE) do |relation|
+ records = relation.select(*EVENT_PROJECTIONS).to_a
+ has_more_data = records.size == BATCH_SIZE
+ records.each do |row|
yielder << row
context.last_processed_id = row.id
break if context.record_limit_reached?
end
- break if context.over_time? || context.record_limit_reached?
+ break if context.over_time? || context.record_limit_reached? || !has_more_data
end
- context.no_more_records! if has_data == false
- # rubocop: enable CodeReuse/ActiveRecord
+ context.no_more_records! unless has_more_data
end
end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def batching_scope
+ return Event.none unless last_event_id_in_postgresql
+
+ table = Event.arel_table
+
+ Event
+ .where(table[:id].gt(context.last_record_id))
+ .where(table[:id].lteq(last_event_id_in_postgresql))
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/config/feature_flags/development/skip_code_generation_instruction_extraction.yml b/config/feature_flags/development/skip_code_generation_instruction_extraction.yml
new file mode 100644
index 00000000000..2418fa7ec39
--- /dev/null
+++ b/config/feature_flags/development/skip_code_generation_instruction_extraction.yml
@@ -0,0 +1,8 @@
+---
+name: skip_code_generation_instruction_extraction
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136343
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431246
+milestone: '16.7'
+type: development
+group: group::code creation
+default_enabled: false
diff --git a/config/metrics/settings/20210204124858_container_registry_enabled.yml b/config/metrics/settings/20210204124858_container_registry_enabled.yml
index f7ef96f196d..f98b9cb96fb 100644
--- a/config/metrics/settings/20210204124858_container_registry_enabled.yml
+++ b/config/metrics/settings/20210204124858_container_registry_enabled.yml
@@ -9,6 +9,10 @@ value_type: boolean
status: active
time_frame: none
data_source: system
+instrumentation_class: GitlabConfigMetric
+options:
+ config:
+ registry: enabled
distribution:
- ee
- ce
diff --git a/config/metrics/settings/20210204124900_dependency_proxy_enabled.yml b/config/metrics/settings/20210204124900_dependency_proxy_enabled.yml
index 0d3382cfe5a..ac359a9ff7b 100644
--- a/config/metrics/settings/20210204124900_dependency_proxy_enabled.yml
+++ b/config/metrics/settings/20210204124900_dependency_proxy_enabled.yml
@@ -9,6 +9,10 @@ value_type: boolean
status: active
time_frame: none
data_source: system
+instrumentation_class: GitlabConfigMetric
+options:
+ config:
+ dependency_proxy: enabled
distribution:
- ee
- ce
diff --git a/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml b/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml
index fa645922f92..dd9a623dc75 100644
--- a/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml
+++ b/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml
@@ -9,6 +9,10 @@ value_type: boolean
status: active
time_frame: none
data_source: system
+instrumentation_class: GitlabConfigMetric
+options:
+ config:
+ gitlab_ci: shared_runners_enabled
distribution:
- ce
- ee
diff --git a/config/metrics/settings/20210204124906_ldap_enabled.yml b/config/metrics/settings/20210204124906_ldap_enabled.yml
index d5f682912f5..59fa6ddfa8d 100644
--- a/config/metrics/settings/20210204124906_ldap_enabled.yml
+++ b/config/metrics/settings/20210204124906_ldap_enabled.yml
@@ -9,6 +9,10 @@ value_type: boolean
status: active
time_frame: none
data_source: system
+instrumentation_class: GitlabConfigMetric
+options:
+ config:
+ ldap: enabled
distribution:
- ce
- ee
diff --git a/config/metrics/settings/20210204124908_mattermost_enabled.yml b/config/metrics/settings/20210204124908_mattermost_enabled.yml
index 6c8b0635471..1e8560ae6fd 100644
--- a/config/metrics/settings/20210204124908_mattermost_enabled.yml
+++ b/config/metrics/settings/20210204124908_mattermost_enabled.yml
@@ -9,6 +9,10 @@ value_type: boolean
status: active
time_frame: none
data_source: system
+instrumentation_class: GitlabConfigMetric
+options:
+ config:
+ mattermost: enabled
distribution:
- ce
- ee
diff --git a/danger/plugins/todos.rb b/danger/plugins/todos.rb
index b31f147f2af..06b76321f95 100644
--- a/danger/plugins/todos.rb
+++ b/danger/plugins/todos.rb
@@ -5,7 +5,11 @@ require_relative '../../tooling/danger/outdated_todo'
module Danger
class Todos < ::Danger::Plugin
def check_outdated_todos(filenames)
- Tooling::Danger::OutdatedTodo.new(filenames, context: self).check
+ Tooling::Danger::OutdatedTodo.new(filenames, context: self, allow_fail: from_lefthook?).check
+ end
+
+ def from_lefthook?
+ %w[1 true].include?(ENV['FROM_LEFTHOOK'])
end
end
end
diff --git a/db/migrate/20231025191217_add_pre_receive_secret_detection_enabled_to_application_settings.rb b/db/migrate/20231025191217_add_pre_receive_secret_detection_enabled_to_application_settings.rb
new file mode 100644
index 00000000000..db37fc5e8f1
--- /dev/null
+++ b/db/migrate/20231025191217_add_pre_receive_secret_detection_enabled_to_application_settings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPreReceiveSecretDetectionEnabledToApplicationSettings < Gitlab::Database::Migration[2.2]
+ milestone '16.7'
+
+ def change
+ add_column :application_settings, :pre_receive_secret_detection_enabled, :boolean, null: false, default: false
+ end
+end
diff --git a/db/post_migrate/20231024073401_cleanup_ci_pipeline_variables_pipeline_id_bigint.rb b/db/post_migrate/20231024073401_cleanup_ci_pipeline_variables_pipeline_id_bigint.rb
new file mode 100644
index 00000000000..bbc2abadcec
--- /dev/null
+++ b/db/post_migrate/20231024073401_cleanup_ci_pipeline_variables_pipeline_id_bigint.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+class CleanupCiPipelineVariablesPipelineIdBigint < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ TABLE = :ci_pipeline_variables
+ REFERENCING_TABLE = :ci_pipelines
+ COLUMNS = [:pipeline_id]
+ INDEX_NAME = :index_ci_pipeline_variables_on_pipeline_id_bigint_and_key
+ FK_NAME = :temp_fk_rails_8d3b04e3e1
+
+ def up
+ with_lock_retries(raise_on_exhaustion: true) do
+ lock_tables(:ci_pipelines, TABLE)
+ cleanup_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+ end
+
+ def down
+ restore_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+
+ add_concurrent_index(
+ TABLE, [:pipeline_id_convert_to_bigint, :key],
+ name: INDEX_NAME, unique: true
+ )
+ add_concurrent_foreign_key(
+ TABLE, REFERENCING_TABLE,
+ column: :pipeline_id_convert_to_bigint, name: FK_NAME,
+ on_delete: :cascade, validate: true, reverse_lock_order: true
+ )
+ end
+end
diff --git a/db/post_migrate/20231114000727_drop_index_namespaces_on_require_two_factor_authentication.rb b/db/post_migrate/20231114000727_drop_index_namespaces_on_require_two_factor_authentication.rb
new file mode 100644
index 00000000000..fa61cdead56
--- /dev/null
+++ b/db/post_migrate/20231114000727_drop_index_namespaces_on_require_two_factor_authentication.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class DropIndexNamespacesOnRequireTwoFactorAuthentication < Gitlab::Database::Migration[2.2]
+ milestone '16.7'
+
+ disable_ddl_transaction!
+
+ TABLE_NAME = :namespaces
+ INDEX_NAME = :index_namespaces_on_require_two_factor_authentication
+
+ def up
+ remove_concurrent_index_by_name TABLE_NAME, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index TABLE_NAME, :require_two_factor_authentication,
+ name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20231024073401 b/db/schema_migrations/20231024073401
new file mode 100644
index 00000000000..c4879144126
--- /dev/null
+++ b/db/schema_migrations/20231024073401
@@ -0,0 +1 @@
+842fcd7c485ec4757810444172f2a0b7ec69ea0eda14662e0418ee3befdcaadc
\ No newline at end of file
diff --git a/db/schema_migrations/20231025191217 b/db/schema_migrations/20231025191217
new file mode 100644
index 00000000000..c0458feed62
--- /dev/null
+++ b/db/schema_migrations/20231025191217
@@ -0,0 +1 @@
+057503cc1306afe9dea3a3d01a2fd8eeb240c33d292a6e3f2bd8ba52b38cfa62
\ No newline at end of file
diff --git a/db/schema_migrations/20231114000727 b/db/schema_migrations/20231114000727
new file mode 100644
index 00000000000..4f96f5ef40c
--- /dev/null
+++ b/db/schema_migrations/20231114000727
@@ -0,0 +1 @@
+ade28e4300509e182d6ab751b4412382c8e00803b102fc4f25bb0a0d049bcc30
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index b259755534e..71cd29bcf7a 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -565,15 +565,6 @@ BEGIN
END;
$$;
-CREATE FUNCTION trigger_7f3d66a7d7f5() RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- NEW."pipeline_id_convert_to_bigint" := NEW."pipeline_id";
- RETURN NEW;
-END;
-$$;
-
CREATE FUNCTION trigger_b2d852e1e2cb() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -12122,6 +12113,7 @@ CREATE TABLE application_settings (
enable_artifact_external_redirect_warning_page boolean DEFAULT true NOT NULL,
allow_project_creation_for_guest_and_below boolean DEFAULT true NOT NULL,
update_namespace_name_rate_limit smallint DEFAULT 120 NOT NULL,
+ pre_receive_secret_detection_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
@@ -14242,7 +14234,6 @@ CREATE TABLE ci_pipeline_variables (
encrypted_value text,
encrypted_value_salt character varying,
encrypted_value_iv character varying,
- pipeline_id_convert_to_bigint integer DEFAULT 0 NOT NULL,
variable_type smallint DEFAULT 1 NOT NULL,
partition_id bigint NOT NULL,
raw boolean DEFAULT false NOT NULL,
@@ -32041,8 +32032,6 @@ CREATE INDEX index_ci_pipeline_schedules_on_project_id ON ci_pipeline_schedules
CREATE UNIQUE INDEX index_ci_pipeline_variables_on_pipeline_id_and_key ON ci_pipeline_variables USING btree (pipeline_id, key);
-CREATE UNIQUE INDEX index_ci_pipeline_variables_on_pipeline_id_bigint_and_key ON ci_pipeline_variables USING btree (pipeline_id_convert_to_bigint, key);
-
CREATE INDEX index_ci_pipelines_config_on_pipeline_id ON ci_pipelines_config USING btree (pipeline_id);
CREATE INDEX index_ci_pipelines_for_ondemand_dast_scans ON ci_pipelines USING btree (id) WHERE (source = 13);
@@ -33467,8 +33456,6 @@ CREATE INDEX index_namespaces_on_path_trigram ON namespaces USING gin (path gin_
CREATE UNIQUE INDEX index_namespaces_on_push_rule_id ON namespaces USING btree (push_rule_id);
-CREATE INDEX index_namespaces_on_require_two_factor_authentication ON namespaces USING btree (require_two_factor_authentication);
-
CREATE UNIQUE INDEX index_namespaces_on_runners_token_encrypted ON namespaces USING btree (runners_token_encrypted);
CREATE INDEX index_namespaces_on_traversal_ids ON namespaces USING gin (traversal_ids);
@@ -36927,8 +36914,6 @@ CREATE TRIGGER trigger_10ee1357e825 BEFORE INSERT OR UPDATE ON p_ci_builds FOR E
CREATE TRIGGER trigger_1bd97da9c1a4 BEFORE INSERT OR UPDATE ON ci_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_1bd97da9c1a4();
-CREATE TRIGGER trigger_7f3d66a7d7f5 BEFORE INSERT OR UPDATE ON ci_pipeline_variables FOR EACH ROW EXECUTE FUNCTION trigger_7f3d66a7d7f5();
-
CREATE TRIGGER trigger_b2d852e1e2cb BEFORE INSERT OR UPDATE ON ci_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_b2d852e1e2cb();
CREATE TRIGGER trigger_delete_project_namespace_on_project_delete AFTER DELETE ON projects FOR EACH ROW WHEN ((old.project_namespace_id IS NOT NULL)) EXECUTE FUNCTION delete_associated_project_namespace();
@@ -39948,9 +39933,6 @@ ALTER TABLE issue_search_data
ALTER TABLE product_analytics_events_experimental
ADD CONSTRAINT product_analytics_events_experimental_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
-ALTER TABLE ONLY ci_pipeline_variables
- ADD CONSTRAINT temp_fk_rails_8d3b04e3e1 FOREIGN KEY (pipeline_id_convert_to_bigint) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY user_follow_users
ADD CONSTRAINT user_follow_users_followee_id_fkey FOREIGN KEY (followee_id) REFERENCES users(id) ON DELETE CASCADE;
diff --git a/doc/administration/backup_restore/backup_gitlab.md b/doc/administration/backup_restore/backup_gitlab.md
index 175b59fba59..12d63232068 100644
--- a/doc/administration/backup_restore/backup_gitlab.md
+++ b/doc/administration/backup_restore/backup_gitlab.md
@@ -346,6 +346,70 @@ The resulting file is named `dump_gitlab_backup.tar`. This is useful for
systems that make use of rsync and incremental backups, and results in
considerably faster transfer speeds.
+#### Backup compression
+
+By default, Gzip fast compression is applied during backup of:
+
+- [PostgreSQL database](#postgresql-databases) dumps.
+- [blobs](#blobs), for example uploads, job artifacts, external merge request diffs.
+
+The default command is `gzip -c -1`. You can override this command with `COMPRESS_CMD`.
+
+Caveats:
+
+- The compression command is used in a pipeline, so your custom command must output to stdout.
+- If you specify a command that is not packaged with GitLab, then you must install it yourself.
+- The resultant filenames will still end in `.gz`.
+- The default decompression command, used during restore, is `gzip -cd`. Therefore if you override the compression command to use a format that cannot be decompressed by `gzip -cd`, you must override the decompression command during restore.
+
+### Default compression: Gzip with fastest method
+
+```shell
+gitlab-backup create
+```
+
+### Gzip with slowest method
+
+```shell
+gitlab-backup create COMPRESS_CMD="gzip -c --best"
+```
+
+If `gzip` was used for backup, then restore does not require any options:
+
+```shell
+gitlab-backup restore
+```
+
+### No compression
+
+If your backup destination has built-in automatic compression, then you may wish to skip compression.
+
+The `tee` command pipes `stdin` to `stdout`.
+
+```shell
+gitlab-backup create COMPRESS_CMD=tee
+```
+
+And on restore:
+
+```shell
+gitlab-backup restore DECOMPRESS_CMD=tee
+```
+
+### Replace Gzip
+
+This is an example of how to use a compression tool which you installed manually:
+
+```shell
+gitlab-backup create COMPRESS_CMD="foo --bar --baz"
+```
+
+Similarly, on restore:
+
+```shell
+gitlab-backup restore DECOMPRESS_CMD="foo --quz"
+```
+
#### Confirm archive can be transferred
To ensure the generated archive is transferable by rsync, you can set the `GZIP_RSYNCABLE=yes`
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index c8a5bf4a5cf..ebd053bdb5a 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1212,6 +1212,7 @@ Input type: `AdminSidekiqQueuesDeleteJobsInput`
|
`artifactUsedCdn` | [`String`](#string) | Delete jobs matching artifact_used_cdn in the context metadata. |
|
`artifactsDependenciesCount` | [`String`](#string) | Delete jobs matching artifacts_dependencies_count in the context metadata. |
|
`artifactsDependenciesSize` | [`String`](#string) | Delete jobs matching artifacts_dependencies_size in the context metadata. |
+|
`bulkImportEntityId` | [`String`](#string) | Delete jobs matching bulk_import_entity_id in the context metadata. |
|
`callerId` | [`String`](#string) | Delete jobs matching caller_id in the context metadata. |
|
`clientId` | [`String`](#string) | Delete jobs matching client_id in the context metadata. |
|
`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
diff --git a/doc/api/group_access_tokens.md b/doc/api/group_access_tokens.md
index 18469741814..15a960ea8ff 100644
--- a/doc/api/group_access_tokens.md
+++ b/doc/api/group_access_tokens.md
@@ -130,14 +130,17 @@ curl --request POST --header "PRIVATE-TOKEN:
" \
Rotate a group access token. Revokes the previous token and creates a new token that expires in one week.
+In GitLab 16.6 and later, you can use the `expires_at` parameter to set a different expiry date. This non-default expiry date can be up to a maximum of one year from the rotation date.
+
```plaintext
POST /groups/:id/access_tokens/:token_id/rotate
```
-| Attribute | Type | required | Description |
-|-----------|---------|----------|---------------------|
-| `id` | integer or string | yes | ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) |
-| `token_id` | integer or string | yes | ID of the project access token |
+| Attribute | Type | required | Description |
+|-----------|------------|----------|---------------------|
+| `id` | integer/string | yes | ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) |
+| `token_id` | integer/string | yes | ID of the access token |
+| `expires_at` | date | no | Expiration date of the access token in ISO format (`YYYY-MM-DD`). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/416795) in GitLab 16.6. |
```shell
curl --request POST --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/groups//access_tokens//rotate"
diff --git a/doc/api/personal_access_tokens.md b/doc/api/personal_access_tokens.md
index 90390acc343..1ad4d066e00 100644
--- a/doc/api/personal_access_tokens.md
+++ b/doc/api/personal_access_tokens.md
@@ -211,13 +211,16 @@ Example response:
Rotate a personal access token. Revokes the previous token and creates a new token that expires in one week.
+In GitLab 16.6 and later, you can use the `expires_at` parameter to set a different expiry date. This non-default expiry date can be up to a maximum of one year from the rotation date.
+
```plaintext
POST /personal_access_tokens/:id/rotate
```
-| Attribute | Type | Required | Description |
-|-----------|---------|----------|---------------------|
-| `id` | integer/string | yes | ID of personal access token |
+| Attribute | Type | Required | Description |
+|-----------|-----------|----------|---------------------|
+| `id` | integer/string | yes | ID of personal access token |
+| `expires_at` | date | no | Expiration date of the access token in ISO format (`YYYY-MM-DD`). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/416795) in GitLab 16.6. |
NOTE:
Non-administrators can rotate their own tokens. Administrators can rotate tokens of any user.
diff --git a/doc/api/project_access_tokens.md b/doc/api/project_access_tokens.md
index 2f386e22001..5925c319d95 100644
--- a/doc/api/project_access_tokens.md
+++ b/doc/api/project_access_tokens.md
@@ -139,6 +139,8 @@ curl --request POST --header "PRIVATE-TOKEN: " \
Rotate a project access token. Revokes the previous token and creates a new token that expires in one week.
+In GitLab 16.6 and later, you can use the `expires_at` parameter to set a different expiry date. This non-default expiry date can be up to a maximum of one year from the rotation date.
+
WARNING:
When you rotate a project access token, the new token retains the expiry date of the old token. For more information, see [issue 423362](https://gitlab.com/gitlab-org/gitlab/-/issues/423362).
@@ -146,10 +148,11 @@ When you rotate a project access token, the new token retains the expiry date of
POST /projects/:id/access_tokens/:token_id/rotate
```
-| Attribute | Type | required | Description |
-|-----------|---------|----------|---------------------|
-| `id` | integer or string | yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) |
-| `token_id` | integer or string | yes | ID of the project access token |
+| Attribute | Type | required | Description |
+|-----------|------------|----------|---------------------|
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) |
+| `token_id` | integer/string | yes | ID of the project access token |
+| `expires_at` | date | no | Expiration date of the access token in ISO format (`YYYY-MM-DD`). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/416795) in GitLab 16.6. |
```shell
curl --request POST --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects//access_tokens//rotate"
diff --git a/doc/tutorials/build_application.md b/doc/tutorials/build_application.md
index 5f4b9da2aa3..2202eb171d0 100644
--- a/doc/tutorials/build_application.md
+++ b/doc/tutorials/build_application.md
@@ -15,7 +15,7 @@ Use CI/CD pipelines to automatically build, test, and deploy your code.
| [Create and run your first GitLab CI/CD pipeline](../ci/quick_start/index.md) | Create a `.gitlab-ci.yml` file and start a pipeline. | **{star}** |
| [Create a complex pipeline](../ci/quick_start/tutorial.md) | Learn about the most commonly used GitLab CI/CD keywords by building an increasingly complex pipeline. | |
| [Get started: Learn about CI/CD](https://www.youtube.com/watch?v=sIegJaLy2ug) (9m 02s) | Learn about the `.gitlab-ci.yml` file and how it's used. | **{star}** |
-| [GitLab CI/CD](https://levelup.gitlab.com/courses/continuous-integration-and-delivery-ci-cd-with-gitlab) | Learn about GitLab CI/CD and build a pipeline in this self-paced course. | **{star}** |
+| [GitLab CI Fundamentals](https://levelup.gitlab.com/learn/learning-path/gitlab-ci-fundamentals) | Learn about GitLab CI/CD and build a pipeline in this self-paced course. | **{star}** |
| [CI deep dive](https://www.youtube.com/watch?v=ZVUbmVac-m8&list=PL05JrBw4t0KorkxIFgZGnzzxjZRCGROt_&index=27) (22m 51s) | Take a closer look at pipelines and continuous integration concepts. | |
| [Set up CI/CD in the cloud](../ci/examples/index.md#cicd-in-the-cloud) | Learn how to set up CI/CD in different cloud-based environments. | |
| [Find CI/CD examples and templates](../ci/examples/index.md#cicd-examples) | Use these examples and templates to set up CI/CD for your use case. | |
diff --git a/lefthook.yml b/lefthook.yml
index 7272b64096f..fe8f5dd0466 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -4,7 +4,9 @@ pre-push:
- ref: master
commands:
danger:
- run: bundle exec rake danger_local
+ files: git diff --name-only $(git merge-base origin/master HEAD)..HEAD
+ # We need to specify {files} as part of the command, otherwise it won't execute the hook
+ run: echo {files} >/dev/null && FROM_LEFTHOOK=1 bundle exec rake danger_local
eslint:
tags: frontend style
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index 58a8c19c1ce..27b672ff6b6 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -103,7 +103,7 @@ module Backup
pg_env = backup_model.config[:pg_env]
success = with_transient_pg_env(pg_env) do
decompress_rd, decompress_wr = IO.pipe
- decompress_pid = spawn(*%w[gzip -cd], out: decompress_wr, in: db_file_name)
+ decompress_pid = spawn(decompress_cmd, out: decompress_wr, in: db_file_name)
decompress_wr.close
status, @errors =
diff --git a/lib/backup/dump/postgres.rb b/lib/backup/dump/postgres.rb
index 1a5128b5a6b..52690d242df 100644
--- a/lib/backup/dump/postgres.rb
+++ b/lib/backup/dump/postgres.rb
@@ -8,7 +8,7 @@ module Backup
def dump(database_name, output_file, pgsql_args)
compress_rd, compress_wr = IO.pipe
- compress_pid = spawn(gzip_cmd, in: compress_rd, out: [output_file, 'w', FILE_PERMISSION])
+ compress_pid = spawn(compress_cmd, in: compress_rd, out: [output_file, 'w', FILE_PERMISSION])
compress_rd.close
dump_pid = Process.spawn('pg_dump', *pgsql_args, database_name, out: compress_wr)
diff --git a/lib/backup/files.rb b/lib/backup/files.rb
index b8ff7fff591..e3a8290e2e3 100644
--- a/lib/backup/files.rb
+++ b/lib/backup/files.rb
@@ -40,14 +40,14 @@ module Backup
end
tar_cmd = [tar, exclude_dirs(:tar), %W[-C #{backup_files_realpath} -cf - .]].flatten
- status_list, output = run_pipeline!([tar_cmd, gzip_cmd], out: [backup_tarball, 'w', 0600])
+ status_list, output = run_pipeline!([tar_cmd, compress_cmd], out: [backup_tarball, 'w', 0600])
FileUtils.rm_rf(backup_files_realpath)
else
tar_cmd = [tar, exclude_dirs(:tar), %W[-C #{app_files_realpath} -cf - .]].flatten
- status_list, output = run_pipeline!([tar_cmd, gzip_cmd], out: [backup_tarball, 'w', 0600])
+ status_list, output = run_pipeline!([tar_cmd, compress_cmd], out: [backup_tarball, 'w', 0600])
end
- unless pipeline_succeeded?(tar_status: status_list[0], gzip_status: status_list[1], output: output)
+ unless pipeline_succeeded?(tar_status: status_list[0], compress_status: status_list[1], output: output)
raise_custom_error(backup_tarball)
end
end
@@ -56,9 +56,9 @@ module Backup
def restore(backup_tarball, backup_id)
backup_existing_files_dir(backup_tarball)
- cmd_list = [%w[gzip -cd], %W[#{tar} --unlink-first --recursive-unlink -C #{app_files_realpath} -xf -]]
+ cmd_list = [decompress_cmd, %W[#{tar} --unlink-first --recursive-unlink -C #{app_files_realpath} -xf -]]
status_list, output = run_pipeline!(cmd_list, in: backup_tarball)
- unless pipeline_succeeded?(gzip_status: status_list[0], tar_status: status_list[1], output: output)
+ unless pipeline_succeeded?(compress_status: status_list[0], tar_status: status_list[1], output: output)
raise Backup::Error, "Restore operation failed: #{output}"
end
end
@@ -108,8 +108,8 @@ module Backup
noncritical_warnings.map { |w| warning =~ w }.any?
end
- def pipeline_succeeded?(tar_status:, gzip_status:, output:)
- return false unless gzip_status&.success?
+ def pipeline_succeeded?(tar_status:, compress_status:, output:)
+ return false unless compress_status&.success?
tar_status&.success? || tar_ignore_non_success?(tar_status.exitstatus, output)
end
diff --git a/lib/backup/helper.rb b/lib/backup/helper.rb
index 2c2e35add0e..3af786654be 100644
--- a/lib/backup/helper.rb
+++ b/lib/backup/helper.rb
@@ -2,6 +2,8 @@
module Backup
module Helper
+ include ::Gitlab::Utils::StrongMemoize
+
def access_denied_error(path)
message = <<~EOS
@@ -30,12 +32,27 @@ module Backup
raise message
end
- def gzip_cmd
- @gzip_cmd ||= if ENV['GZIP_RSYNCABLE'] == 'yes'
- "gzip --rsyncable -c -1"
- else
- "gzip -c -1"
- end
+ def compress_cmd
+ if ENV['COMPRESS_CMD'].present?
+ puts "Using custom COMPRESS_CMD '#{ENV['COMPRESS_CMD']}'"
+ puts "Ignoring GZIP_RSYNCABLE" if ENV['GZIP_RSYNCABLE'] == 'yes'
+ ENV['COMPRESS_CMD']
+ elsif ENV['GZIP_RSYNCABLE'] == 'yes'
+ "gzip --rsyncable -c -1"
+ else
+ "gzip -c -1"
+ end
end
+ strong_memoize_attr :compress_cmd
+
+ def decompress_cmd
+ if ENV['DECOMPRESS_CMD'].present?
+ puts "Using custom DECOMPRESS_CMD '#{ENV['DECOMPRESS_CMD']}'"
+ ENV['DECOMPRESS_CMD']
+ else
+ "gzip -cd"
+ end
+ end
+ strong_memoize_attr :decompress_cmd
end
end
diff --git a/lib/gitlab/application_context.rb b/lib/gitlab/application_context.rb
index 67fc2ae2fcc..e46bbc2cfda 100644
--- a/lib/gitlab/application_context.rb
+++ b/lib/gitlab/application_context.rb
@@ -26,7 +26,8 @@ module Gitlab
:artifacts_dependencies_size,
:artifacts_dependencies_count,
:root_caller_id,
- :merge_action_status
+ :merge_action_status,
+ :bulk_import_entity_id
].freeze
private_constant :KNOWN_KEYS
@@ -45,7 +46,8 @@ module Gitlab
Attribute.new(:artifacts_dependencies_size, Integer),
Attribute.new(:artifacts_dependencies_count, Integer),
Attribute.new(:root_caller_id, String),
- Attribute.new(:merge_action_status, String)
+ Attribute.new(:merge_action_status, String),
+ Attribute.new(:bulk_import_entity_id, Integer)
].freeze
private_constant :APPLICATION_ATTRIBUTES
@@ -95,6 +97,7 @@ module Gitlab
# rubocop: disable Metrics/CyclomaticComplexity
# rubocop: disable Metrics/PerceivedComplexity
+ # rubocop: disable Metrics/AbcSize
def to_lazy_hash
{}.tap do |hash|
assign_hash_if_value(hash, :caller_id)
@@ -106,6 +109,7 @@ module Gitlab
assign_hash_if_value(hash, :artifacts_dependencies_size)
assign_hash_if_value(hash, :artifacts_dependencies_count)
assign_hash_if_value(hash, :merge_action_status)
+ assign_hash_if_value(hash, :bulk_import_entity_id)
hash[:user] = -> { username } if include_user?
hash[:user_id] = -> { user_id } if include_user?
@@ -115,10 +119,12 @@ module Gitlab
hash[:pipeline_id] = -> { job&.pipeline_id } if set_values.include?(:job)
hash[:job_id] = -> { job&.id } if set_values.include?(:job)
hash[:artifact_size] = -> { artifact&.size } if set_values.include?(:artifact)
+ hash[:bulk_import_entity_id] = -> { bulk_import_entity_id } if set_values.include?(:bulk_import_entity_id)
end
end
# rubocop: enable Metrics/CyclomaticComplexity
# rubocop: enable Metrics/PerceivedComplexity
+ # rubocop: enable Metrics/AbcSize
def use
Labkit::Context.with_context(to_lazy_hash) { yield }
diff --git a/lib/gitlab/usage/metrics/instrumentations/gitlab_config_metric.rb b/lib/gitlab/usage/metrics/instrumentations/gitlab_config_metric.rb
new file mode 100644
index 00000000000..daeef06e6c5
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/gitlab_config_metric.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class GitlabConfigMetric < GenericMetric
+ value do
+ method_name_array = config_hash_to_method_array(options[:config])
+
+ method_name_array.inject(Gitlab.config, :public_send)
+ end
+
+ private
+
+ def config_hash_to_method_array(object)
+ object.each_with_object([]) do |(key, value), result|
+ result.append(key)
+
+ if value.is_a?(Hash)
+ result.concat(config_hash_to_method_array(value))
+ else
+ result.append(value)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 930a637cdd4..2b8b7aedeb2 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -163,11 +163,6 @@ module Gitlab
def features_usage_data_ce
{
- container_registry_enabled: alt_usage_data(fallback: nil) { Gitlab.config.registry.enabled },
- dependency_proxy_enabled: Gitlab.config.try(:dependency_proxy)&.enabled,
- gitlab_shared_runners_enabled: alt_usage_data(fallback: nil) { Gitlab.config.gitlab_ci.shared_runners_enabled },
- ldap_enabled: alt_usage_data(fallback: nil) { Gitlab.config.ldap.enabled },
- mattermost_enabled: alt_usage_data(fallback: nil) { Gitlab.config.mattermost.enabled },
omniauth_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth.omniauth_enabled? },
prometheus_enabled: alt_usage_data(fallback: nil) { Gitlab::Prometheus::Internal.prometheus_enabled? },
prometheus_metrics_enabled: alt_usage_data(fallback: nil) { Gitlab::Metrics.prometheus_metrics_enabled? },
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 09caceda198..c7dfb517b45 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -43315,6 +43315,9 @@ msgstr ""
msgid "SecurityOrchestration|Security Scan"
msgstr ""
+msgid "SecurityOrchestration|Security policy overwrites this setting"
+msgstr ""
+
msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr ""
@@ -43465,6 +43468,9 @@ msgstr ""
msgid "SecurityOrchestration|You already have the maximum %{maximumAllowed} %{policyType} policies."
msgstr ""
+msgid "SecurityOrchestration|You can't change the default branch because its protection is enforced by one or more %{security_policies_link_start}security policies%{security_policies_link_end}. %{learn_more_link_start}Learn more%{learn_more_link_end}."
+msgstr ""
+
msgid "SecurityOrchestration|You can't unprotect this branch because its protection is enforced by one or more %{security_policies_link_start}security policies%{security_policies_link_end}. %{learn_more_link_start}Learn more%{learn_more_link_end}."
msgstr ""
diff --git a/rubocop/cop/gitlab/avoid_gitlab_instance_checks.rb b/rubocop/cop/gitlab/avoid_gitlab_instance_checks.rb
index 6aac3649b04..7cae7ab8549 100644
--- a/rubocop/cop/gitlab/avoid_gitlab_instance_checks.rb
+++ b/rubocop/cop/gitlab/avoid_gitlab_instance_checks.rb
@@ -27,7 +27,7 @@ module RuboCop
MSG = 'Avoid the use of `%{name}`. Use Gitlab::Saas.feature_available?. ' \
'See https://docs.gitlab.com/ee/development/ee_features.html#saas-only-feature'
RESTRICT_ON_SEND = %i[
- com? com_except_jh? com_and_canary? com_but_not_canary? org_or_com? should_check_namespace_plan?
+ com? com_except_jh? com_and_canary? com_but_not_canary? org_or_com? should_check_namespace_plan? enabled?
].freeze
# @!method gitlab?(node)
@@ -43,8 +43,16 @@ module RuboCop
{nil? (cbase)} :Gitlab) :CurrentSettings) :should_check_namespace_plan?)
PATTERN
+ # @!method saas_enabled?(node)
+ def_node_matcher :saas_enabled?, <<~PATTERN
+ (send
+ (const
+ (const
+ {nil? (cbase)} :Gitlab) :Saas) :enabled?)
+ PATTERN
+
def on_send(node)
- return unless gitlab?(node) || should_check_namespace_plan?(node)
+ return unless gitlab?(node) || should_check_namespace_plan?(node) || saas_enabled?(node)
add_offense(node, message: format(MSG, name: node.method_name))
end
diff --git a/spec/frontend/environments/folder/environments_folder_view_spec.js b/spec/frontend/environments/folder/environments_folder_view_spec.js
index 6a40c68397b..34eef1e89ab 100644
--- a/spec/frontend/environments/folder/environments_folder_view_spec.js
+++ b/spec/frontend/environments/folder/environments_folder_view_spec.js
@@ -3,6 +3,7 @@ import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { removeBreakLine, removeWhitespace } from 'helpers/text_helper';
import EnvironmentTable from '~/environments/components/environments_table.vue';
+import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue';
import EnvironmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
@@ -91,6 +92,10 @@ describe('Environments Folder View', () => {
).toContain('Environments / review');
});
+ it('should render the confirm rollback modal', () => {
+ expect(wrapper.findComponent(ConfirmRollbackModal).exists()).toBe(true);
+ });
+
describe('pagination', () => {
it('should render pagination', () => {
expect(wrapper.findComponent(GlPagination).exists()).toBe(true);
diff --git a/spec/frontend/projects/settings/components/default_branch_selector_spec.js b/spec/frontend/projects/settings/components/default_branch_selector_spec.js
index 9baea5c5517..aa50683b185 100644
--- a/spec/frontend/projects/settings/components/default_branch_selector_spec.js
+++ b/spec/frontend/projects/settings/components/default_branch_selector_spec.js
@@ -4,6 +4,7 @@ import RefSelector from '~/ref/components/ref_selector.vue';
import { REF_TYPE_BRANCHES } from '~/ref/constants';
describe('projects/settings/components/default_branch_selector', () => {
+ const disabled = true;
const persistedDefaultBranch = 'main';
const projectId = '123';
let wrapper;
@@ -13,6 +14,7 @@ describe('projects/settings/components/default_branch_selector', () => {
const buildWrapper = () => {
wrapper = shallowMount(DefaultBranchSelector, {
propsData: {
+ disabled,
persistedDefaultBranch,
projectId,
},
@@ -25,6 +27,7 @@ describe('projects/settings/components/default_branch_selector', () => {
it('displays a RefSelector component', () => {
expect(findRefSelector().props()).toEqual({
+ disabled,
value: persistedDefaultBranch,
enabledRefTypes: [REF_TYPE_BRANCHES],
projectId,
diff --git a/spec/frontend/ref/components/ref_selector_spec.js b/spec/frontend/ref/components/ref_selector_spec.js
index 26010a1cfa6..39924a3a77a 100644
--- a/spec/frontend/ref/components/ref_selector_spec.js
+++ b/spec/frontend/ref/components/ref_selector_spec.js
@@ -46,7 +46,7 @@ describe('Ref selector component', () => {
let commitApiCallSpy;
let requestSpies;
- const createComponent = (mountOverrides = {}, propsData = {}) => {
+ const createComponent = ({ overrides = {}, propsData = {} } = {}) => {
wrapper = mountExtended(
RefSelector,
merge(
@@ -64,7 +64,7 @@ describe('Ref selector component', () => {
},
store: createStore(),
},
- mountOverrides,
+ overrides,
),
);
};
@@ -211,7 +211,7 @@ describe('Ref selector component', () => {
const id = 'git-ref';
beforeEach(() => {
- createComponent({ attrs: { id } });
+ createComponent({ overrides: { attrs: { id } } });
return waitForRequests();
});
@@ -326,7 +326,7 @@ describe('Ref selector component', () => {
describe('branches', () => {
describe('when the branches search returns results', () => {
beforeEach(() => {
- createComponent({}, { useSymbolicRefNames: true });
+ createComponent({ propsData: { useSymbolicRefNames: true } });
return waitForRequests();
});
@@ -389,7 +389,7 @@ describe('Ref selector component', () => {
describe('tags', () => {
describe('when the tags search returns results', () => {
beforeEach(() => {
- createComponent({}, { useSymbolicRefNames: true });
+ createComponent({ propsData: { useSymbolicRefNames: true } });
return waitForRequests();
});
@@ -569,6 +569,20 @@ describe('Ref selector component', () => {
});
});
});
+
+ describe('disabled', () => {
+ it('does not disable the dropdown', () => {
+ createComponent();
+ expect(findListbox().props('disabled')).toBe(false);
+ });
+
+ it('disables the dropdown', async () => {
+ createComponent({ propsData: { disabled: true } });
+ expect(findListbox().props('disabled')).toBe(true);
+ await selectFirstBranch();
+ expect(wrapper.emitted('input')).toBeUndefined();
+ });
+ });
});
describe('with non-default ref types', () => {
@@ -691,9 +705,7 @@ describe('Ref selector component', () => {
});
beforeEach(() => {
- createComponent({
- scopedSlots: { footer: createFooter },
- });
+ createComponent({ overrides: { scopedSlots: { footer: createFooter } } });
updateQuery('abcd1234');
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js
index b210327aa31..65c4970bc76 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js
@@ -54,5 +54,12 @@ describe('MR widget status icon component', () => {
expect(findIcon().exists()).toBe(true);
expect(findIcon().props().name).toBe('merge-request-close');
});
+
+ it('renders empty status icon', () => {
+ createWrapper({ status: 'empty' });
+
+ expect(findStatusIcon().exists()).toBe(true);
+ expect(findStatusIcon().props().iconName).toBe('neutral');
+ });
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/new_ready_to_merge_spec.js.snap b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/new_ready_to_merge_spec.js.snap
index ecf4040cbda..ec0af7c8a7b 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/new_ready_to_merge_spec.js.snap
+++ b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/new_ready_to_merge_spec.js.snap
@@ -8,7 +8,7 @@ exports[`New ready to merge state component renders permission text if canMerge
status="success"
/>
Ready to merge by members who can write to the target branch.
@@ -23,7 +23,7 @@ exports[`New ready to merge state component renders permission text if canMerge
status="success"
/>
Ready to merge!
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge_spec.js
index 016eac05727..d8eec165395 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge_spec.js
@@ -1,5 +1,6 @@
-import { GlSprintf } from '@gitlab/ui';
+import { GlSprintf, GlLink } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { helpPagePath } from '~/helpers/help_page_helper';
import NothingToMerge from '~/vue_merge_request_widget/components/states/nothing_to_merge.vue';
describe('NothingToMerge', () => {
@@ -14,6 +15,7 @@ describe('NothingToMerge', () => {
};
const findNothingToMergeTextBody = () => wrapper.findByTestId('nothing-to-merge-body');
+ const findHelpLink = () => wrapper.findComponent(GlLink);
describe('With Blob link', () => {
beforeEach(() => {
@@ -26,5 +28,9 @@ describe('NothingToMerge', () => {
'Use merge requests to propose changes to your project and discuss them with your team. To make changes, use the Code dropdown list above, then test them with CI/CD before merging.',
);
});
+
+ it('renders text with link to CI Help Page', () => {
+ expect(findHelpLink().attributes('href')).toBe(helpPagePath('ci/quick_start/index.html'));
+ });
});
});
diff --git a/spec/lib/backup/database_spec.rb b/spec/lib/backup/database_spec.rb
index 073efbbbfcc..26d09c275c1 100644
--- a/spec/lib/backup/database_spec.rb
+++ b/spec/lib/backup/database_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Backup::Database, :reestablished_active_record_base, feature_category: :backup_restore do
let(:progress) { StringIO.new }
- let(:output) { progress.string }
+ let(:progress_output) { progress.string }
let(:backup_id) { 'some_id' }
let(:one_database_configured?) { base_models_for_backup.one? }
let(:timeout_service) do
@@ -223,7 +223,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
subject.restore(backup_dir, backup_id)
- expect(output).to include('Removing all tables. Press `Ctrl-C` within 5 seconds to abort')
+ expect(progress_output).to include('Removing all tables. Press `Ctrl-C` within 5 seconds to abort')
end
it 'has a pre restore warning' do
@@ -241,9 +241,21 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
subject.restore(backup_dir, backup_id)
- expect(output).to include("Restoring PostgreSQL database")
- expect(output).to include("[DONE]")
- expect(output).not_to include("ERRORS")
+ expect(progress_output).to include("Restoring PostgreSQL database")
+ expect(progress_output).to include("[DONE]")
+ expect(progress_output).not_to include("ERRORS")
+ end
+
+ context 'when DECOMPRESS_CMD is set to tee' do
+ before do
+ stub_env('DECOMPRESS_CMD', 'tee')
+ end
+
+ it 'outputs a message about DECOMPRESS_CMD' do
+ expect do
+ subject.restore(backup_dir, backup_id)
+ end.to output(/Using custom DECOMPRESS_CMD 'tee'/).to_stdout
+ end
end
end
@@ -277,9 +289,9 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
subject.restore(backup_dir, backup_id)
- expect(output).to include("ERRORS")
- expect(output).not_to include(noise)
- expect(output).to include(visible_error)
+ expect(progress_output).to include("ERRORS")
+ expect(progress_output).not_to include(noise)
+ expect(progress_output).to include(visible_error)
expect(subject.post_restore_warning).not_to be_nil
end
end
diff --git a/spec/lib/backup/dump/postgres_spec.rb b/spec/lib/backup/dump/postgres_spec.rb
index f6a68ab6db9..f7020c78c6c 100644
--- a/spec/lib/backup/dump/postgres_spec.rb
+++ b/spec/lib/backup/dump/postgres_spec.rb
@@ -32,5 +32,25 @@ RSpec.describe Backup::Dump::Postgres, feature_category: :backup_restore do
expect(File.exist?(db_file_name)).to eq(true)
end
+
+ context 'when COMPRESS_CMD is set to tee' do
+ let(:tee_pid) { spawn('tee', in: pipes[0], out: [db_file_name, 'w', 0o600]) }
+
+ before do
+ stub_env('COMPRESS_CMD', 'tee')
+ end
+
+ it 'passes through tee instead of gzip' do
+ expect(subject).to receive(:spawn).with('tee', in: pipes.first,
+ out: [db_file_name, 'w', 0o600]).and_return(tee_pid)
+ expect(Process).to receive(:spawn).with('pg_dump', *args, pg_database, out: pipes[1]).and_return(pg_dump_pid)
+
+ expect do
+ subject.dump(pg_database, db_file_name, args)
+ end.to output(/Using custom COMPRESS_CMD 'tee'/).to_stdout
+
+ expect(File.exist?(db_file_name)).to eq(true)
+ end
+ end
end
end
diff --git a/spec/lib/backup/files_spec.rb b/spec/lib/backup/files_spec.rb
index 48c89e06dfa..f0fc829764a 100644
--- a/spec/lib/backup/files_spec.rb
+++ b/spec/lib/backup/files_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
it 'calls tar command with unlink' do
expect(subject).to receive(:tar).and_return('blabla-tar')
- expect(subject).to receive(:run_pipeline!).with([%w[gzip -cd], %w[blabla-tar --unlink-first --recursive-unlink -C /var/gitlab-registry -xf -]], any_args)
+ expect(subject).to receive(:run_pipeline!).with(["gzip -cd", %w[blabla-tar --unlink-first --recursive-unlink -C /var/gitlab-registry -xf -]], any_args)
expect(subject).to receive(:pipeline_succeeded?).and_return(true)
subject.restore('registry.tar.gz', 'backup_id')
end
@@ -107,6 +107,21 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
expect { subject.restore('registry.tar.gz', 'backup_id') }.to raise_error(/is a mountpoint/)
end
end
+
+ describe 'with DECOMPRESS_CMD' do
+ before do
+ stub_env('DECOMPRESS_CMD', 'tee')
+ allow(subject).to receive(:pipeline_succeeded?).and_return(true)
+ end
+
+ it 'passes through tee instead of gzip' do
+ expect(subject).to receive(:run_pipeline!).with(['tee', anything], any_args).and_return([[true, true], ''])
+
+ expect do
+ subject.restore('registry.tar.gz', 'backup_id')
+ end.to output(/Using custom DECOMPRESS_CMD 'tee'/).to_stdout
+ end
+ end
end
describe '#dump' do
@@ -173,6 +188,37 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
.and raise_error(/Failed to create compressed file/)
end
end
+
+ describe 'with COMPRESS_CMD' do
+ before do
+ stub_env('COMPRESS_CMD', 'tee')
+ end
+
+ it 'passes through tee instead of gzip' do
+ expect(subject).to receive(:run_pipeline!).with([anything, 'tee'], any_args)
+ expect do
+ subject.dump('registry.tar.gz', 'backup_id')
+ end.to output(/Using custom COMPRESS_CMD 'tee'/).to_stdout
+ end
+ end
+
+ context 'when GZIP_RSYNCABLE is "yes"' do
+ before do
+ stub_env('GZIP_RSYNCABLE', 'yes')
+ end
+
+ it 'gzips the files with rsyncable option' do
+ expect(subject).to receive(:run_pipeline!).with([anything, 'gzip --rsyncable -c -1'], any_args)
+ subject.dump('registry.tar.gz', 'backup_id')
+ end
+ end
+
+ context 'when GZIP_RSYNCABLE is not set' do
+ it 'gzips the files without the rsyncable option' do
+ expect(subject).to receive(:run_pipeline!).with([anything, 'gzip -c -1'], any_args)
+ subject.dump('registry.tar.gz', 'backup_id')
+ end
+ end
end
describe '#exclude_dirs' do
@@ -226,13 +272,13 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
it 'returns true if both tar and gzip succeeeded' do
expect(
- subject.pipeline_succeeded?(tar_status: status_0, gzip_status: status_0, output: 'any_output')
+ subject.pipeline_succeeded?(tar_status: status_0, compress_status: status_0, output: 'any_output')
).to be_truthy
end
it 'returns false if gzip failed' do
expect(
- subject.pipeline_succeeded?(tar_status: status_1, gzip_status: status_1, output: 'any_output')
+ subject.pipeline_succeeded?(tar_status: status_1, compress_status: status_1, output: 'any_output')
).to be_falsey
end
@@ -243,7 +289,7 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
it 'returns true' do
expect(
- subject.pipeline_succeeded?(tar_status: status_1, gzip_status: status_0, output: 'any_output')
+ subject.pipeline_succeeded?(tar_status: status_1, compress_status: status_0, output: 'any_output')
).to be_truthy
end
end
@@ -255,7 +301,7 @@ RSpec.describe Backup::Files, feature_category: :backup_restore do
it 'returns false' do
expect(
- subject.pipeline_succeeded?(tar_status: status_1, gzip_status: status_0, output: 'any_output')
+ subject.pipeline_succeeded?(tar_status: status_1, compress_status: status_0, output: 'any_output')
).to be_falsey
end
end
diff --git a/spec/lib/gitlab/application_context_spec.rb b/spec/lib/gitlab/application_context_spec.rb
index 20c1536b9e6..99f932975d0 100644
--- a/spec/lib/gitlab/application_context_spec.rb
+++ b/spec/lib/gitlab/application_context_spec.rb
@@ -210,6 +210,14 @@ RSpec.describe Gitlab::ApplicationContext do
expect(result(context)).to include(job_id: job.id, project: project.full_path, pipeline_id: job.pipeline_id)
end
end
+
+ context 'when using bulk import context' do
+ it 'sets expected bulk_import_entity_id value' do
+ context = described_class.new(bulk_import_entity_id: 1)
+
+ expect(result(context)).to include(bulk_import_entity_id: 1)
+ end
+ end
end
describe '#use' do
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/gitlab_config_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/gitlab_config_metric_spec.rb
new file mode 100644
index 00000000000..e9814f0cb51
--- /dev/null
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/gitlab_config_metric_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::GitlabConfigMetric, feature_category: :service_ping do
+ describe 'config metric' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:config_value, :expected_value) do
+ false | false
+ true | true
+ end
+
+ with_them do
+ before do
+ stub_config(artifacts: { object_store: { enabled: config_value } })
+ end
+
+ it_behaves_like 'a correct instrumented metric value', {
+ time_frame: 'none',
+ options: {
+ config: {
+ artifacts: {
+ object_store: 'enabled'
+ }
+ }
+ }
+ }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 25a032ec183..06492056da2 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -571,13 +571,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures, feature_category: :servic
subject { described_class.features_usage_data_ce }
it 'gathers feature usage data', :aggregate_failures do
- expect(subject[:mattermost_enabled]).to eq(Gitlab.config.mattermost.enabled)
- expect(subject[:ldap_enabled]).to eq(Gitlab.config.ldap.enabled)
expect(subject[:omniauth_enabled]).to eq(Gitlab::Auth.omniauth_enabled?)
expect(subject[:reply_by_email_enabled]).to eq(Gitlab::Email::IncomingEmail.enabled?)
- expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled)
- expect(subject[:dependency_proxy_enabled]).to eq(Gitlab.config.dependency_proxy.enabled)
- expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled)
end
context 'with embedded Prometheus' do
diff --git a/spec/mailers/emails/service_desk_spec.rb b/spec/mailers/emails/service_desk_spec.rb
index b700819ed2c..05df1a643fd 100644
--- a/spec/mailers/emails/service_desk_spec.rb
+++ b/spec/mailers/emails/service_desk_spec.rb
@@ -179,6 +179,11 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
it 'uses SMTP delivery method and custom email settings' do
expect_service_desk_custom_email_delivery_options(service_desk_setting)
+ # Don't use ActionMailer::Base.smtp_settings, because it only contains explicitly set values.
+ merged_default_settings = Mail::SMTP.new({}).settings
+ # When forcibly used the configuration has a higher timeout. Ensure it's the default!
+ expect(subject.delivery_method.settings[:read_timeout]).to eq(merged_default_settings[:read_timeout])
+
expect(Gitlab::AppLogger).to have_received(:info).with({ category: 'custom_email' })
end
@@ -537,7 +542,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
}
end
- subject { Notify.service_desk_custom_email_verification_email(service_desk_setting) }
+ subject(:mail) { Notify.service_desk_custom_email_verification_email(service_desk_setting) }
it_behaves_like 'a custom email verification process email'
@@ -547,6 +552,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
it 'forcibly uses SMTP delivery method and has correct settings' do
expect_service_desk_custom_email_delivery_options(service_desk_setting)
+ expect(mail.delivery_method.settings[:read_timeout]).to eq(described_class::VERIFICATION_EMAIL_TIMEOUT)
# defaults are unchanged after email overrode settings
expect(Mail::SMTP.new({}).settings).to include(expected_delivery_method_defaults)
@@ -557,7 +563,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
end
it 'uses verification email address as recipient' do
- expect(subject.to).to eq([service_desk_setting.custom_email_address_for_verification])
+ expect(mail.to).to eq([service_desk_setting.custom_email_address_for_verification])
end
it 'contains verification token' do
diff --git a/spec/rubocop/cop/gitlab/avoid_gitlab_instance_checks_spec.rb b/spec/rubocop/cop/gitlab/avoid_gitlab_instance_checks_spec.rb
index 2dba6194d44..b3864c5495f 100644
--- a/spec/rubocop/cop/gitlab/avoid_gitlab_instance_checks_spec.rb
+++ b/spec/rubocop/cop/gitlab/avoid_gitlab_instance_checks_spec.rb
@@ -18,6 +18,8 @@ RSpec.describe RuboCop::Cop::Gitlab::AvoidGitlabInstanceChecks, feature_category
::Gitlab.com?
Gitlab::CurrentSettings.should_check_namespace_plan?
::Gitlab::CurrentSettings.should_check_namespace_plan?
+ Gitlab::Saas.enabled?
+ ::Gitlab::Saas.enabled?
]
end
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 8f17e44ed74..d8c4c5107c7 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -77,13 +77,8 @@ module UsageDataHelpers
USAGE_DATA_KEYS = %i[
counts
recorded_at
- mattermost_enabled
- ldap_enabled
omniauth_enabled
reply_by_email_enabled
- container_registry_enabled
- dependency_proxy_enabled
- gitlab_shared_runners_enabled
gitlab_pages
git
gitaly
diff --git a/spec/tooling/danger/outdated_todo_spec.rb b/spec/tooling/danger/outdated_todo_spec.rb
index 3a3909c69ac..3079c26bafc 100644
--- a/spec/tooling/danger/outdated_todo_spec.rb
+++ b/spec/tooling/danger/outdated_todo_spec.rb
@@ -15,69 +15,78 @@ RSpec.describe Tooling::Danger::OutdatedTodo, feature_category: :tooling do
]
end
- subject(:plugin) { described_class.new(filenames, context: fake_danger, todos: todos) }
+ subject(:plugin) { described_class.new(filenames, context: fake_danger, todos: todos, allow_fail: allow_fail) }
- context 'when the filenames are mentioned in single todo' do
- let(:filenames) { ['app/controllers/acme_challenges_controller.rb'] }
+ [true, false].each do |allow_failure|
+ context "with allow_fail set to #{allow_failure}" do
+ let(:allow_fail) { allow_failure }
+ let(:expected_method) do
+ allow_failure ? :fail : :warn
+ end
- it 'warns about mentions' do
- expect(fake_danger)
- .to receive(:warn)
- .with <<~MESSAGE
- `app/controllers/acme_challenges_controller.rb` was removed but is mentioned in:
- - `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:5`
- MESSAGE
+ context 'when the filenames are mentioned in single todo' do
+ let(:filenames) { ['app/controllers/acme_challenges_controller.rb'] }
- plugin.check
- end
- end
+ it 'warns about mentions' do
+ expect(fake_danger)
+ .to receive(expected_method)
+ .with <<~MESSAGE
+ `app/controllers/acme_challenges_controller.rb` was removed but is mentioned in:
+ - `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:5`
+ MESSAGE
- context 'when the filenames are mentioned in multiple todos' do
- let(:filenames) do
- [
- 'app/controllers/application_controller.rb',
- 'app/controllers/acme_challenges_controller.rb'
- ]
- end
+ plugin.check
+ end
+ end
- it 'warns about mentions' do
- expect(fake_danger)
- .to receive(:warn)
- .with(<<~FIRSTMESSAGE)
- `app/controllers/application_controller.rb` was removed but is mentioned in:
- - `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:4`
- - `spec/fixtures/tooling/danger/rubocop_todo/cop2.yml:4`
- FIRSTMESSAGE
+ context 'when the filenames are mentioned in multiple todos' do
+ let(:filenames) do
+ [
+ 'app/controllers/application_controller.rb',
+ 'app/controllers/acme_challenges_controller.rb'
+ ]
+ end
- expect(fake_danger)
- .to receive(:warn)
- .with(<<~SECONDMESSAGE)
- `app/controllers/acme_challenges_controller.rb` was removed but is mentioned in:
- - `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:5`
- SECONDMESSAGE
+ it 'warns about mentions' do
+ expect(fake_danger)
+ .to receive(expected_method)
+ .with(<<~FIRSTMESSAGE)
+ `app/controllers/application_controller.rb` was removed but is mentioned in:
+ - `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:4`
+ - `spec/fixtures/tooling/danger/rubocop_todo/cop2.yml:4`
+ FIRSTMESSAGE
- plugin.check
- end
- end
+ expect(fake_danger)
+ .to receive(expected_method)
+ .with(<<~SECONDMESSAGE)
+ `app/controllers/acme_challenges_controller.rb` was removed but is mentioned in:
+ - `spec/fixtures/tooling/danger/rubocop_todo/cop1.yml:5`
+ SECONDMESSAGE
- context 'when the filenames are not mentioned in todos' do
- let(:filenames) { ['any/inexisting/file.rb'] }
+ plugin.check
+ end
+ end
- it 'does not warn' do
- expect(fake_danger).not_to receive(:warn)
+ context 'when the filenames are not mentioned in todos' do
+ let(:filenames) { ['any/inexisting/file.rb'] }
- plugin.check
- end
- end
+ it 'does not warn' do
+ expect(fake_danger).not_to receive(expected_method)
- context 'when there is no todos' do
- let(:filenames) { ['app/controllers/acme_challenges_controller.rb'] }
- let(:todos) { [] }
+ plugin.check
+ end
+ end
- it 'does not warn' do
- expect(fake_danger).not_to receive(:warn)
+ context 'when there is no todos' do
+ let(:filenames) { ['app/controllers/acme_challenges_controller.rb'] }
+ let(:todos) { [] }
- plugin.check
+ it 'does not warn' do
+ expect(fake_danger).not_to receive(expected_method)
+
+ plugin.check
+ end
+ end
end
end
end
diff --git a/spec/workers/click_house/events_sync_worker_spec.rb b/spec/workers/click_house/events_sync_worker_spec.rb
index 01267db36a7..28a885629a4 100644
--- a/spec/workers/click_house/events_sync_worker_spec.rb
+++ b/spec/workers/click_house/events_sync_worker_spec.rb
@@ -63,11 +63,32 @@ RSpec.describe ClickHouse::EventsSyncWorker, feature_category: :value_stream_man
end
it 'inserts all records' do
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
+ { status: :processed, records_inserted: 4, reached_end_of_table: true })
+
worker.perform
events = ClickHouse::Client.select('SELECT * FROM events', :main)
expect(events.size).to eq(4)
end
+
+ context 'when new records are inserted while processing' do
+ it 'does not process new records created during the iteration' do
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
+ { status: :processed, records_inserted: 4,
+ reached_end_of_table: true })
+
+ # Simulating the case when there is an insert during the iteration
+ call_count = 0
+ allow(worker).to receive(:next_batch).and_wrap_original do |method|
+ call_count += 1
+ create(:event) if call_count == 3
+ method.call
+ end
+
+ worker.perform
+ end
+ end
end
context 'when time limit is reached' do
@@ -96,6 +117,9 @@ RSpec.describe ClickHouse::EventsSyncWorker, feature_category: :value_stream_man
end
it 'syncs records after the cursor' do
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:result,
+ { status: :processed, records_inserted: 3, reached_end_of_table: true })
+
worker.perform
events = ClickHouse::Client.select('SELECT id FROM events ORDER BY id', :main)
diff --git a/tooling/danger/outdated_todo.rb b/tooling/danger/outdated_todo.rb
index a5f5cc897a9..5ae0561010c 100644
--- a/tooling/danger/outdated_todo.rb
+++ b/tooling/danger/outdated_todo.rb
@@ -8,10 +8,11 @@ module Tooling
spec/support/rspec_order_todo.yml
].freeze
- def initialize(filenames, context:, todos: TODOS_GLOBS)
+ def initialize(filenames, context:, todos: TODOS_GLOBS, allow_fail: false)
@filenames = filenames
@context = context
@todos_globs = todos
+ @allow_fail = allow_fail
end
def check
@@ -22,17 +23,23 @@ module Tooling
private
- attr_reader :filenames, :context
+ attr_reader :filenames, :context, :allow_fail
def check_filename(filename)
mentions = all_mentions_for(filename)
return if mentions.empty?
- context.warn <<~MESSAGE
+ message = <<~MESSAGE
`#{filename}` was removed but is mentioned in:
#{mentions.join("\n")}
MESSAGE
+
+ if allow_fail
+ context.fail message
+ else
+ context.warn message
+ end
end
def all_mentions_for(filename)