diff --git a/.rubocop_todo/layout/line_continuation_spacing.yml b/.rubocop_todo/layout/line_continuation_spacing.yml
index 26ce7068a38..b63363c9a10 100644
--- a/.rubocop_todo/layout/line_continuation_spacing.yml
+++ b/.rubocop_todo/layout/line_continuation_spacing.yml
@@ -176,6 +176,5 @@ Layout/LineContinuationSpacing:
- 'spec/support/shared_examples/requests/api/notes_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb'
- 'spec/uploaders/file_mover_spec.rb'
- - 'spec/views/devise/shared/_signup_box.html.haml_spec.rb'
- 'spec/views/projects/issues/show.html.haml_spec.rb'
- 'spec/views/projects/pages/show.html.haml_spec.rb'
diff --git a/.rubocop_todo/rspec/named_subject.yml b/.rubocop_todo/rspec/named_subject.yml
index 660dc3a94f2..536591ff27f 100644
--- a/.rubocop_todo/rspec/named_subject.yml
+++ b/.rubocop_todo/rspec/named_subject.yml
@@ -1090,7 +1090,6 @@ RSpec/NamedSubject:
- 'ee/spec/tasks/gitlab/spdx_rake_spec.rb'
- 'ee/spec/validators/user_existence_validator_spec.rb'
- 'ee/spec/validators/user_id_existence_validator_spec.rb'
- - 'ee/spec/views/devise/registrations/new.html.haml_spec.rb'
- 'ee/spec/workers/active_user_count_threshold_worker_spec.rb'
- 'ee/spec/workers/admin_emails_worker_spec.rb'
- 'ee/spec/workers/app_sec/dast/profile_schedule_worker_spec.rb'
diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
index d356e168572..7e21e3617f4 100644
--- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb
+++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
@@ -61,6 +61,7 @@ module ResolvesMergeRequests
commit_count: [:metrics],
diff_stats_summary: [:metrics],
approved_by: [:approved_by_users],
+ merge_after: [:merge_schedule],
milestone: [:milestone],
security_auto_fix: [:author],
head_pipeline: [:merge_request_diff, { head_pipeline: [:merge_request] }],
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 8daef281b60..e261b55300f 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -100,6 +100,11 @@ module Types
method: :public_merge_status, null: true,
description: 'Merge status of the merge request.'
+ field :merge_after, ::Types::TimeType,
+ null: true,
+ description: 'Date after which the merge request can be merged.',
+ alpha: { milestone: '17.4' }
+
field :detailed_merge_status, ::Types::MergeRequests::DetailedMergeStatusEnum, null: true,
calls_gitaly: true,
description: 'Detailed merge status of the merge request.'
@@ -347,6 +352,10 @@ module Types
object.metrics&.merged_by || object.merge_user
end
+ def merge_after
+ object.merge_schedule&.merge_after
+ end
+
def detailed_merge_status
::MergeRequests::Mergeability::DetailedMergeStatusService.new(merge_request: object).execute
end
diff --git a/app/models/ci/build_trace_chunks/fog.rb b/app/models/ci/build_trace_chunks/fog.rb
index 1cae2279434..f7db76856ae 100644
--- a/app/models/ci/build_trace_chunks/fog.rb
+++ b/app/models/ci/build_trace_chunks/fog.rb
@@ -112,7 +112,9 @@ module Ci
def connection
return unless available?
- @connection ||= ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys)
+ ::Gitlab::SafeRequestStore.fetch(object_store_raw_config) do
+ ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys)
+ end
end
def fog_directory
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 93ec0430866..699af3ec43c 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -66,6 +66,8 @@ class MergeRequest < ApplicationRecord
has_one :predictions, inverse_of: :merge_request
delegate :suggested_reviewers, to: :predictions
+ has_one :merge_schedule, class_name: 'MergeRequests::MergeSchedule', inverse_of: :merge_request
+
belongs_to :latest_merge_request_diff, class_name: 'MergeRequestDiff'
manual_inverse_association :latest_merge_request_diff, :merge_request
@@ -378,6 +380,7 @@ class MergeRequest < ApplicationRecord
preload_routables.preload(
:assignees, :author, :unresolved_notes, :labels, :milestone,
:timelogs, :latest_merge_request_diff, :reviewers,
+ :merge_schedule,
target_project: :project_feature,
metrics: [:latest_closed_by, :merged_by]
)
diff --git a/app/models/merge_requests/merge_schedule.rb b/app/models/merge_requests/merge_schedule.rb
new file mode 100644
index 00000000000..5749768b5a1
--- /dev/null
+++ b/app/models/merge_requests/merge_schedule.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module MergeRequests
+ class MergeSchedule < ApplicationRecord
+ self.table_name = 'merge_request_merge_schedules'
+
+ belongs_to :merge_request, optional: false, inverse_of: :merge_schedule
+
+ before_validation :set_sharding_key
+
+ def set_sharding_key
+ self.project_id = merge_request&.target_project&.id
+ end
+ end
+end
diff --git a/app/models/onboarding/progress.rb b/app/models/onboarding/progress.rb
index 9c3dc8f4bf0..2f02e9b0b55 100644
--- a/app/models/onboarding/progress.rb
+++ b/app/models/onboarding/progress.rb
@@ -4,24 +4,6 @@ module Onboarding
class Progress < ApplicationRecord
self.table_name = 'onboarding_progresses'
- include IgnorableColumns
-
- ignore_columns %i[
- git_pull_at
- subscription_created_at
- scoped_label_created_at
- security_scan_enabled_at
- issue_auto_closed_at
- repository_imported_at
- repository_mirrored_at
- secure_container_scanning_run_at
- secure_secret_detection_run_at
- secure_coverage_fuzzing_run_at
- secure_cluster_image_scanning_run_at
- secure_api_fuzzing_run_at
- ],
- remove_with: '17.5', remove_after: '2024-09-14'
-
belongs_to :namespace, optional: false
validate :namespace_is_root_namespace
diff --git a/app/views/devise/shared/_signup_box_form.html.haml b/app/views/devise/registrations/_signup_box_form.html.haml
similarity index 68%
rename from app/views/devise/shared/_signup_box_form.html.haml
rename to app/views/devise/registrations/_signup_box_form.html.haml
index 8dc812d8813..9d3b98e3023 100644
--- a/app/views/devise/shared/_signup_box_form.html.haml
+++ b/app/views/devise/registrations/_signup_box_form.html.haml
@@ -2,7 +2,9 @@
- form_resource_name = "new_#{resource_name}"
- button_class = local_assigns[:button_class]
-= gitlab_ui_form_for(resource, as: form_resource_name, url: url, html: { class: 'gl-show-field-errors js-arkose-labs-form', aria: { live: 'assertive' }}, data: { testid: 'signup-form' }) do |f|
+= gitlab_ui_form_for(resource, as: form_resource_name, url: url,
+ html: { class: 'gl-show-field-errors js-arkose-labs-form', aria: { live: 'assertive' }},
+ data: { testid: 'signup-form' }) do |f|
.devise-errors
= render 'devise/shared/error_messages', resource: resource
- if Gitlab::CurrentSettings.invisible_captcha_enabled
@@ -21,8 +23,9 @@
= f.text_field :first_name,
class: 'form-control gl-form-input top js-block-emoji js-validate-length js-track-error',
data: { max_length: max_first_name_length,
- max_length_message: s_('SignUp|First name is too long (maximum is %{max_length} characters).') % { max_length: max_first_name_length },
- track_action_for_errors: tracking_label,
+ max_length_message: safe_format(s_('SignUp|First name is too long (maximum is %{max_length} characters).'),
+ max_length: max_first_name_length),
+ track_action_for_errors: preregistration_tracking_label,
testid: 'new-user-first-name-field' },
required: true,
title: _('This field is required.')
@@ -31,8 +34,9 @@
= f.text_field :last_name,
class: 'form-control gl-form-input top js-block-emoji js-validate-length js-track-error',
data: { max_length: max_last_name_length,
- max_length_message: s_('SignUp|Last name is too long (maximum is %{max_length} characters).') % { max_length: max_last_name_length },
- track_action_for_errors: tracking_label,
+ max_length_message: safe_format(s_('SignUp|Last name is too long (maximum is %{max_length} characters).'),
+ max_length: max_last_name_length),
+ track_action_for_errors: preregistration_tracking_label,
testid: 'new-user-last-name-field' },
required: true,
title: _('This field is required.')
@@ -40,36 +44,45 @@
= f.label :username, _('Username')
= f.text_field :username,
class: 'form-control gl-form-input middle js-block-emoji js-validate-length js-validate-username js-track-error',
- data: signup_username_data_attributes.merge(track_action_for_errors: tracking_label),
+ data: signup_username_data_attributes.merge(track_action_for_errors: preregistration_tracking_label),
pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
required: true,
title: _('Please create a username with only alphanumeric characters.')
- %p.validation-error.gl-text-red-500.gl-field-error-ignore.gl-mt-2.field-validation.hide= _('Username is already taken.')
- %p.validation-success.gl-text-green-600.gl-field-error-ignore.gl-mt-2.field-validation.hide= _('Username is available.')
- %p.validation-pending.gl-field-error-ignore.gl-mt-2.field-validation.hide= _('Checking username availability...')
+ %p.validation-error.gl-text-red-500.gl-field-error-ignore.gl-mt-2.field-validation.hide
+ = _('Username is already taken.')
+ %p.validation-success.gl-text-green-600.gl-field-error-ignore.gl-mt-2.field-validation.hide
+ = _('Username is available.')
+ %p.validation-pending.gl-field-error-ignore.gl-mt-2.field-validation.hide
+ = _('Checking username availability...')
- unless @invite_email.present?
.form-group
= f.label :email, _('Email')
= f.email_field :email,
class: 'form-control gl-form-input middle js-validate-email js-track-error',
- data: { testid: 'new-user-email-field', track_action_for_errors: tracking_label },
+ data: { testid: 'new-user-email-field', track_action_for_errors: preregistration_tracking_label },
required: true,
title: _('Please provide a valid email address.')
- %p.validation-hint.gl-field-hint.text-secondary= _('We recommend a work email address.')
- %p.validation-warning.gl-field-error-ignore.text-secondary.hide= _('This email address does not look right, are you sure you typed it correctly?')
+ %p.validation-hint.gl-field-hint.text-secondary
+ = _('We recommend a work email address.')
+ %p.validation-warning.gl-field-error-ignore.text-secondary.hide
+ = _('This email address does not look right, are you sure you typed it correctly?')
-# This is used for providing entry to Jihu on email verification
= render_if_exists 'devise/shared/signup_email_additional_info'
.form-group.gl-mb-5
= f.label :password, _('Password')
%input.form-control.gl-form-input.js-password{ data: { id: "#{form_resource_name}_password",
- title: s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length },
+ title: safe_format(s_('SignUp|Minimum length is %{minimum_password_length} characters.'),
+ minimum_password_length: @minimum_password_length),
minimum_password_length: @minimum_password_length,
testid: 'new-user-password-field',
- track_action_for_errors: tracking_label,
+ track_action_for_errors: preregistration_tracking_label,
autocomplete: 'new-password',
name: "#{form_resource_name}[password]" } }
- %p.gl-field-hint-valid.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
+ %p.gl-field-hint-valid.text-secondary
+ = safe_format(s_('SignUp|Minimum length is %{minimum_password_length} characters.'),
+ minimum_password_length: @minimum_password_length)
= render_if_exists 'shared/password_requirements_list', show_basic_requirements: true
+ -# This is used for providing entry to Jihu phone verification
= render_if_exists 'devise/shared/phone_verification', form: f
.form-group
@@ -78,11 +91,9 @@
- elsif show_recaptcha_sign_up?
= recaptcha_tags nonce: content_security_policy_nonce
- = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, block: true,
- button_options: { class: button_class,
- data: { testid: 'new-user-register-button', track_action: 'register', track_label: tracking_label }}) do
- = button_text
-
= render 'devise/shared/terms_of_service_notice', button_text: button_text
-= yield :omniauth_providers_bottom if show_omniauth_providers
+ = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, block: true,
+ button_options: { class: "#{button_class} gl-mt-4",
+ data: { testid: 'new-user-register-button', track_action: 'register', track_label: preregistration_tracking_label }}) do
+ = button_text
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index 88270cd58b1..8583840c35c 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -7,16 +7,23 @@
= render "layouts/bizible"
= render_if_exists "layouts/google_tag_manager_body"
-- content_for :omniauth_providers_bottom do
- = render 'devise/shared/signup_omniauth_providers',
- tracking_label: preregistration_tracking_label
-
.signup-page
- = render 'devise/shared/signup_box',
- url: registration_path(resource_name, registration_path_params),
- button_text: _('Register'),
- show_omniauth_providers: omniauth_enabled? && button_based_providers_enabled?,
- tracking_label: preregistration_tracking_label
+ .gl-mb-3.gl-p-4
+ = render 'signup_box_form',
+ url: registration_path(resource_name, registration_path_params),
+ button_text: _('Continue')
+
+ - if omniauth_enabled? && button_based_providers_enabled?
+ .gl-text-center.gl-pt-5
+ %label.gl-font-normal
+ = _('Continue with:')
+ .gl-flex.gl-flex-col.gl-gap-3
+ - enabled_button_based_providers.each do |provider|
+ = render 'devise/shared/omniauth_provider_button',
+ href: omniauth_authorize_path(:user, provider, register_omniauth_params({})),
+ provider: provider,
+ classes: 'js-track-omni-auth',
+ data: { provider: provider, track_action: "#{provider}_sso", track_label: preregistration_tracking_label }
= render 'devise/shared/sign_in_link',
tracking_action: 'click_sign_in_from_registration_page',
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
deleted file mode 100644
index b875d245fec..00000000000
--- a/app/views/devise/shared/_signup_box.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-.gl-mb-3.gl-p-4
- = yield :omniauth_providers_top if show_omniauth_providers
-
- = render 'devise/shared/signup_box_form',
- button_text: button_text,
- url: url,
- show_omniauth_providers: omniauth_enabled? && button_based_providers_enabled?,
- tracking_label: tracking_label
diff --git a/app/views/devise/shared/_signup_omniauth_provider_button.haml b/app/views/devise/shared/_signup_omniauth_provider_button.haml
deleted file mode 100644
index 0c08940f4a8..00000000000
--- a/app/views/devise/shared/_signup_omniauth_provider_button.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-= render 'devise/shared/omniauth_provider_button',
- href: href,
- provider: provider,
- classes: 'js-track-omni-auth',
- data: { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label }
diff --git a/app/views/devise/shared/_signup_omniauth_provider_list.haml b/app/views/devise/shared/_signup_omniauth_provider_list.haml
deleted file mode 100644
index 03b9817fec6..00000000000
--- a/app/views/devise/shared/_signup_omniauth_provider_list.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-.gl-text-center.gl-pt-5
- %label.gl-font-normal
- = _("Register with:")
- .gl-flex.gl-flex-col.gl-gap-3
- - providers.each do |provider|
- = render 'devise/shared/signup_omniauth_provider_button',
- href: omniauth_authorize_path(:user, provider, register_omniauth_params(local_assigns)),
- provider: provider,
- tracking_label: tracking_label
diff --git a/app/views/devise/shared/_signup_omniauth_providers.haml b/app/views/devise/shared/_signup_omniauth_providers.haml
deleted file mode 100644
index 37790cec808..00000000000
--- a/app/views/devise/shared/_signup_omniauth_providers.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-= render 'devise/shared/signup_omniauth_provider_list',
- providers: enabled_button_based_providers,
- tracking_label: local_assigns[:tracking_label]
diff --git a/config/gitleaks.toml b/config/gitleaks.toml
index 7b2065a025a..bd1bf82e214 100644
--- a/config/gitleaks.toml
+++ b/config/gitleaks.toml
@@ -10,6 +10,8 @@ path = "/gitleaks.toml"
regexes = [
# spotted in doc/user/application_security/secret_detection/index.md and some frontend specs
"glpat-1234567890abcdefghij",
+ # doc/user/application_security/secret_detection/remove_secrets_tutorial.md
+ "glpat-12345678901234567890",
# spec/frontend/lib/utils/secret_detection_spec.js
"glpat-cgyKc1k_AsnEpmP-5fRL",
"gldt-cgyKc1k_AsnEpmP-5fRL",
diff --git a/db/docs/merge_request_merge_schedules.yml b/db/docs/merge_request_merge_schedules.yml
new file mode 100644
index 00000000000..7923501b06b
--- /dev/null
+++ b/db/docs/merge_request_merge_schedules.yml
@@ -0,0 +1,12 @@
+---
+table_name: merge_request_merge_schedules
+classes:
+- MergeRequests::MergeSchedule
+feature_categories:
+- code_review_workflow
+description: Stores timestamps for scheduled merges
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/165092
+milestone: '17.4'
+gitlab_schema: gitlab_main_cell
+sharding_key:
+ project_id: projects
diff --git a/db/migrate/20240911181854_create_merge_request_merge_schedules.rb b/db/migrate/20240911181854_create_merge_request_merge_schedules.rb
new file mode 100644
index 00000000000..2c694ceead3
--- /dev/null
+++ b/db/migrate/20240911181854_create_merge_request_merge_schedules.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class CreateMergeRequestMergeSchedules < Gitlab::Database::Migration[2.2]
+ milestone '17.4'
+
+ def change
+ create_table :merge_request_merge_schedules do |t| # rubocop:disable Migration/EnsureFactoryForTable -- factory exists in spec/factories/merge_request_merge_schedule.rb
+ t.references :merge_request, foreign_key: { on_delete: :cascade }, index: false, null: false
+ t.datetime_with_timezone :merge_after
+
+ t.bigint :project_id, null: false
+
+ t.index :merge_request_id, unique: true
+ t.index :project_id
+ end
+ end
+end
diff --git a/db/migrate/20240911181855_create_merge_request_merge_schedules_sharding_key_fk.rb b/db/migrate/20240911181855_create_merge_request_merge_schedules_sharding_key_fk.rb
new file mode 100644
index 00000000000..143248ee3c2
--- /dev/null
+++ b/db/migrate/20240911181855_create_merge_request_merge_schedules_sharding_key_fk.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class CreateMergeRequestMergeSchedulesShardingKeyFk < Gitlab::Database::Migration[2.2]
+ milestone '17.4'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :merge_request_merge_schedules, :projects, column: :project_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :merge_request_merge_schedules, column: :project_id
+ end
+ end
+end
diff --git a/db/schema_migrations/20240911181854 b/db/schema_migrations/20240911181854
new file mode 100644
index 00000000000..75e2bbe9721
--- /dev/null
+++ b/db/schema_migrations/20240911181854
@@ -0,0 +1 @@
+6dd9484b5c7d61943dae2ed684c8d84d0c0319e009c26d1bcbfcbe0ae69939e7
\ No newline at end of file
diff --git a/db/schema_migrations/20240911181855 b/db/schema_migrations/20240911181855
new file mode 100644
index 00000000000..b3cf9c49b4c
--- /dev/null
+++ b/db/schema_migrations/20240911181855
@@ -0,0 +1 @@
+4e56708b920c40ba9c6050f2441f313715e3db134ad6114fb9091829506736c1
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index f4a64ef0386..bdbe2ba67b1 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -13199,6 +13199,22 @@ CREATE SEQUENCE merge_request_diffs_id_seq
ALTER SEQUENCE merge_request_diffs_id_seq OWNED BY merge_request_diffs.id;
+CREATE TABLE merge_request_merge_schedules (
+ id bigint NOT NULL,
+ merge_request_id bigint NOT NULL,
+ merge_after timestamp with time zone,
+ project_id bigint NOT NULL
+);
+
+CREATE SEQUENCE merge_request_merge_schedules_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE merge_request_merge_schedules_id_seq OWNED BY merge_request_merge_schedules.id;
+
CREATE TABLE merge_request_metrics (
merge_request_id bigint NOT NULL,
latest_build_started_at timestamp without time zone,
@@ -21922,6 +21938,8 @@ ALTER TABLE ONLY merge_request_diff_details ALTER COLUMN merge_request_diff_id S
ALTER TABLE ONLY merge_request_diffs ALTER COLUMN id SET DEFAULT nextval('merge_request_diffs_id_seq'::regclass);
+ALTER TABLE ONLY merge_request_merge_schedules ALTER COLUMN id SET DEFAULT nextval('merge_request_merge_schedules_id_seq'::regclass);
+
ALTER TABLE ONLY merge_request_metrics ALTER COLUMN id SET DEFAULT nextval('merge_request_metrics_id_seq'::regclass);
ALTER TABLE ONLY merge_request_predictions ALTER COLUMN merge_request_id SET DEFAULT nextval('merge_request_predictions_merge_request_id_seq'::regclass);
@@ -24263,6 +24281,9 @@ ALTER TABLE ONLY merge_request_diff_files
ALTER TABLE ONLY merge_request_diffs
ADD CONSTRAINT merge_request_diffs_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY merge_request_merge_schedules
+ ADD CONSTRAINT merge_request_merge_schedules_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY merge_request_metrics
ADD CONSTRAINT merge_request_metrics_pkey PRIMARY KEY (id);
@@ -28945,6 +28966,10 @@ CREATE INDEX index_merge_request_diffs_on_project_id ON merge_request_diffs USIN
CREATE UNIQUE INDEX index_merge_request_diffs_on_unique_merge_request_id ON merge_request_diffs USING btree (merge_request_id) WHERE (diff_type = 2);
+CREATE UNIQUE INDEX index_merge_request_merge_schedules_on_merge_request_id ON merge_request_merge_schedules USING btree (merge_request_id);
+
+CREATE INDEX index_merge_request_merge_schedules_on_project_id ON merge_request_merge_schedules USING btree (project_id);
+
CREATE INDEX index_merge_request_metrics_on_first_deployed_to_production_at ON merge_request_metrics USING btree (first_deployed_to_production_at);
CREATE INDEX index_merge_request_metrics_on_latest_closed_at ON merge_request_metrics USING btree (latest_closed_at) WHERE (latest_closed_at IS NOT NULL);
@@ -34063,6 +34088,9 @@ ALTER TABLE ONLY operations_strategies
ALTER TABLE ONLY lfs_objects_projects
ADD CONSTRAINT fk_a56e02279c FOREIGN KEY (lfs_object_id) REFERENCES lfs_objects(id) ON DELETE RESTRICT NOT VALID;
+ALTER TABLE ONLY merge_request_merge_schedules
+ ADD CONSTRAINT fk_a5ff9339a9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY merge_requests
ADD CONSTRAINT fk_a6963e8447 FOREIGN KEY (target_project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -35242,6 +35270,9 @@ ALTER TABLE zoekt_tasks
ALTER TABLE ONLY ml_models
ADD CONSTRAINT fk_rails_51e87f7c50 FOREIGN KEY (project_id) REFERENCES projects(id);
+ALTER TABLE ONLY merge_request_merge_schedules
+ ADD CONSTRAINT fk_rails_5294434bc3 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY elastic_group_index_statuses
ADD CONSTRAINT fk_rails_52b9969b12 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index a05d25e4da4..e131f7bd125 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -25665,6 +25665,7 @@ Defines which user roles, users, or groups can merge into a protected branch.
| `iid` | [`String!`](#string) | Internal ID of the merge request. |
| `inProgressMergeCommitSha` | [`String`](#string) | Commit SHA of the merge request if merge is in progress. |
| `labels` | [`LabelConnection`](#labelconnection) | Labels of the merge request. (see [Connections](#connections)) |
+| `mergeAfter` **{warning-solid}** | [`Time`](#time) | **Introduced** in GitLab 17.4. **Status**: Experiment. Date after which the merge request can be merged. |
| `mergeCommitSha` | [`String`](#string) | SHA of the merge request commit (set once merged). |
| `mergeError` | [`String`](#string) | Error message due to a merge error. |
| `mergeOngoing` | [`Boolean!`](#boolean) | Indicates if a merge is currently occurring. |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 22404f2332d..b61888e6e28 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -17,6 +17,7 @@ DETAILS:
> - `with_merge_status_recheck` [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115948) in GitLab 15.11 [with a flag](../administration/feature_flags.md) named `restrict_merge_status_recheck` to be ignored for requests from users insufficient permissions. Disabled by default.
> - `approvals_before_merge` [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119503) in GitLab 16.0.
> - `prepared_at` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122001) in GitLab 16.1.
+> - `merge_after` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/165092) in GitLab 17.4.
All API calls to non-public information require authentication.
@@ -118,6 +119,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -367,6 +369,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -553,6 +556,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -747,6 +751,7 @@ Example response:
"merged_by": null, // Deprecated and will be removed in API v5. Use `merge_user` instead.
"merge_user": null,
"merged_at": null,
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -1134,6 +1139,7 @@ Example response:
"merged_by": null,
"merge_user": null,
"merged_at": null,
+ "merge_after": "2018-09-07T11:16:00.000Z",
"closed_by": null,
"closed_at": null,
"target_branch": "master",
@@ -1243,6 +1249,7 @@ Example response:
"merged_by": null,
"merge_user": null,
"merged_at": null,
+ "merge_after": "2018-09-07T11:16:00.000Z",
"closed_by": null,
"closed_at": null,
"target_branch": "master",
@@ -1787,6 +1794,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -1960,6 +1968,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -2153,6 +2162,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -2354,6 +2364,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -2826,6 +2837,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -2990,6 +3002,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
index a91fcf61334..9e8c9d4c072 100644
--- a/doc/development/documentation/workflow.md
+++ b/doc/development/documentation/workflow.md
@@ -33,6 +33,27 @@ to make the shorter pipeline run, you must follow these guidelines when naming y
| Starting with `docs-` | `docs-update-api-issues` |
| Ending in `-docs` | `123-update-api-issues-docs` |
+### Moving content
+
+When you move content to a new location, and edit the content in the same merge request,
+use separate commits.
+
+Separate commits help the reviewer, because the MR diff for moved content
+does not clearly highlight edits.
+When you use separate commits, the reviewer can verify the location change
+in the first commit diff, then the content changes in subsequent commits.
+
+For example, if you move a page, but also update the content of the page:
+
+1. In the first commit: Move the content to its new location and put [redirects](redirects.md) in place if required.
+ If you can, fix broken links in this commit.
+1. In subsequent commits: Make content changes. Fix broken links if you haven't already.
+1. In the merge request: Explain the commits in the MR description and in a
+ comment to the reviewer.
+
+You can add as many commits as you want, but make sure the first commit only moves the content,
+and does not edit it.
+
## Documentation labels
When you author an issue or merge request, choose the
diff --git a/doc/install/docker/backup_restore.md b/doc/install/docker/backup_restore.md
index c89d5cfb12d..aefe071cf5b 100644
--- a/doc/install/docker/backup_restore.md
+++ b/doc/install/docker/backup_restore.md
@@ -32,7 +32,7 @@ GitLab from backup. The secrets file is stored at `/etc/gitlab/gitlab-secrets.js
## Create a database backup
-A database backup is required to roll back a GitLab upgrade if you encounter issues.
+Before upgrading GitLab, you should create a database-only backup. If you encounter issues during the GitLab upgrade, you can restore the database backup to roll back the upgrade. To create a database backup, run this command:
```shell
docker exec -t gitlab-backup create SKIP=artifacts,repositories,registry,uploads,builds,pages,lfs,packages,terraform_state
@@ -40,3 +40,5 @@ docker exec -t gitlab-backup create SKIP=artifacts,repositories
The backup is written to `/var/opt/gitlab/backups` which should be on a
[volume mounted by Docker](installation.md#set-up-the-volumes-location).
+
+For more information on using the backup to roll back an upgrade, see [Downgrade GitLab](upgrade.md#downgrade-gitlab).
diff --git a/lib/api/entities/merge_request_basic.rb b/lib/api/entities/merge_request_basic.rb
index fce85ff1c49..4348d292eb4 100644
--- a/lib/api/entities/merge_request_basic.rb
+++ b/lib/api/entities/merge_request_basic.rb
@@ -64,6 +64,11 @@ module API
merge_request.public_merge_status
end
expose :detailed_merge_status
+
+ expose :merge_after do |merge_request, _options|
+ merge_request.merge_schedule&.merge_after
+ end
+
expose :diff_head_sha, as: :sha
expose :merge_commit_sha
expose :squash_commit_sha
diff --git a/lib/gitlab/import_export/base/relation_factory.rb b/lib/gitlab/import_export/base/relation_factory.rb
index a48d2823074..952aef56255 100644
--- a/lib/gitlab/import_export/base/relation_factory.rb
+++ b/lib/gitlab/import_export/base/relation_factory.rb
@@ -9,7 +9,7 @@ module Gitlab
IMPORTED_OBJECT_MAX_RETRIES = 5
- OVERRIDES = { user_contributions: :user }.freeze
+ OVERRIDES = { user_contributions: :user, merge_schedule: 'MergeRequests::MergeSchedule' }.freeze
EXISTING_OBJECT_RELATIONS = %i[].freeze
# This represents all relations that have unique key on `project_id` or `group_id`
diff --git a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
index 39d9560c0fb..8e4cb66d850 100644
--- a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
@@ -3,10 +3,6 @@
module Gitlab
module UsageDataCounters
module IssueActivityUniqueCounter
- ISSUE_CATEGORY = 'issues_edit'
- ISSUE_ACTION = 'perform_issue_action'
- ISSUE_LABEL = 'redis_hll_counters.issues_edit.issues_edit_total_unique_counts_monthly'
-
ISSUE_ASSIGNEE_CHANGED = 'g_project_management_issue_assignee_changed'
ISSUE_CREATED = 'g_project_management_issue_created'
ISSUE_CLOSED = 'g_project_management_issue_closed'
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a8a4e7c7f5c..65f51af98e2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -15277,6 +15277,9 @@ msgstr ""
msgid "Continue with overages"
msgstr ""
+msgid "Continue with:"
+msgstr ""
+
msgid "Continue…"
msgstr ""
@@ -37966,9 +37969,6 @@ msgstr ""
msgid "Options"
msgstr ""
-msgid "Or create your own GitLab account"
-msgstr ""
-
msgid "Ordered list"
msgstr ""
@@ -44870,9 +44870,6 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
-msgid "Register with:"
-msgstr ""
-
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -51467,9 +51464,6 @@ msgstr ""
msgid "Sign up"
msgstr ""
-msgid "Sign up for your free trial with:"
-msgstr ""
-
msgid "Sign-in count:"
msgstr ""
diff --git a/qa/qa/page/registration/sign_up.rb b/qa/qa/page/registration/sign_up.rb
index 7d501bafe3b..46d2c1b8398 100644
--- a/qa/qa/page/registration/sign_up.rb
+++ b/qa/qa/page/registration/sign_up.rb
@@ -4,7 +4,7 @@ module QA
module Page
module Registration
class SignUp < Page::Base
- view 'app/views/devise/shared/_signup_box_form.html.haml' do
+ view 'app/views/devise/registrations/_signup_box_form.html.haml' do
element 'new-user-first-name-field'
element 'new-user-last-name-field'
element 'new-user-email-field'
diff --git a/spec/factories/merge_request_merge_schedule.rb b/spec/factories/merge_request_merge_schedule.rb
new file mode 100644
index 00000000000..2be331efe15
--- /dev/null
+++ b/spec/factories/merge_request_merge_schedule.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :merge_request_merge_schedule, class: 'MergeRequests::MergeSchedule' do
+ merge_request
+ end
+end
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index 63694d1254a..11cce78b06a 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -228,7 +228,7 @@ RSpec.describe 'Group or Project invitations', :with_current_organization, :aggr
expect(page).to have_current_path(new_user_registration_path, ignore_query: true)
- fill_in_sign_up_form(new_user, 'Register', invite: true)
+ fill_in_sign_up_form(new_user, invite: true)
expect(page).to have_current_path(group_path(group))
expect(page)
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 4abe90dc463..a363c1b50ac 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe 'Signup', :with_current_organization, :js, feature_category: :use
let(:terms_text) do
<<~TEXT.squish
- By clicking Register or registering through a third party you accept the
+ By clicking Continue or registering through a third party you accept the
Terms of Use and acknowledge the Privacy Statement and Cookie Policy
TEXT
end
@@ -130,7 +130,7 @@ RSpec.describe 'Signup', :with_current_organization, :js, feature_category: :use
fill_in 'new_user_username', with: 'new$user!username'
wait_for_requests
- click_button "Register"
+ click_button _('Continue')
expect(page).to have_content("Please create a username with only alphanumeric characters.")
end
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index ebd3cd5f4fc..773565955f7 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe GitlabSchema.types['MergeRequest'], feature_category: :code_revie
squash_on_merge available_auto_merge_strategies
has_ci mergeable commits committers commits_without_merge_commits squash security_auto_fix default_squash_commit_message
auto_merge_strategy merge_user award_emoji prepared_at codequality_reports_comparer supports_lock_on_merge
- mergeability_checks
+ mergeability_checks merge_after
allows_multiple_assignees allows_multiple_reviewers retargeted name
]
diff --git a/spec/lib/api/entities/merge_request_basic_spec.rb b/spec/lib/api/entities/merge_request_basic_spec.rb
index 09f064c4cdd..d0a056547f7 100644
--- a/spec/lib/api/entities/merge_request_basic_spec.rb
+++ b/spec/lib/api/entities/merge_request_basic_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe ::API::Entities::MergeRequestBasic, feature_category: :code_revie
expected_fields = %i[
merged_by merge_user merged_at closed_by closed_at target_branch user_notes_count upvotes downvotes
author assignees assignee reviewers source_project_id target_project_id labels draft work_in_progress
- milestone merge_when_pipeline_succeeds merge_status detailed_merge_status sha merge_commit_sha
+ milestone merge_when_pipeline_succeeds merge_status detailed_merge_status merge_after sha merge_commit_sha
squash_commit_sha discussion_locked should_remove_source_branch force_remove_source_branch prepared_at
reference references web_url time_stats squash task_completion_status has_conflicts blocking_discussions_resolved
imported imported_from
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 54ce7055be4..f567e15ca59 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -227,6 +227,7 @@ merge_requests:
- merge_request_context_commits
- merge_request_context_commit_diff_files
- merge_request_stage_events
+- merge_schedule
- events
- merge_requests_closing_issues
- cached_closes_issues
@@ -293,6 +294,8 @@ merge_request_diff_files:
merge_request_context_commits:
- merge_request
- diff_files
+merge_schedule:
+- merge_request
cleanup_schedule:
- merge_request
ci_pipelines:
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 355905cdabd..b56c0608488 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_gitlab_redis_trace_chunks do
+RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_gitlab_redis_trace_chunks, feature_category: :pipeline_execution do
include ExclusiveLeaseHelpers
let_it_be(:build) { create(:ci_build, :running) }
@@ -142,18 +142,29 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
it { is_expected.to eq('Sample data in database') }
end
- context 'when data_store is fog' do
+ context 'when data_store is fog', :request_store do
let(:data_store) { :fog }
+ let(:sample_data) { +'Sample data in fog' }
before do
- build_trace_chunk.send(:unsafe_set_data!, +'Sample data in fog')
+ build_trace_chunk.send(:unsafe_set_data!, sample_data)
end
- it { is_expected.to eq('Sample data in fog') }
+ it { is_expected.to eq(sample_data) }
it 'returns a new Fog store' do
expect(described_class.get_store_class(data_store)).to be_a(Ci::BuildTraceChunks::Fog)
end
+
+ it 'only initializes Fog::Storage once' do
+ RequestStore.clear!
+
+ expect(Fog::Storage).to receive(:new).and_call_original
+
+ 2.times do
+ expect(build_trace_chunk.reload.build.trace_chunks.first.data).to eq(sample_data)
+ end
+ end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 8ef72206675..2fab1f07887 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -36,6 +36,7 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
it { is_expected.to have_many(:reviews).inverse_of(:merge_request) }
it { is_expected.to have_many(:reviewed_by_users).through(:reviews).source(:author) }
it { is_expected.to have_one(:cleanup_schedule).inverse_of(:merge_request) }
+ it { is_expected.to have_one(:merge_schedule).class_name('MergeRequests::MergeSchedule').inverse_of(:merge_request) }
it { is_expected.to have_many(:created_environments).class_name('Environment').inverse_of(:merge_request) }
it { is_expected.to have_many(:assignment_events).class_name('ResourceEvents::MergeRequestAssignmentEvent').inverse_of(:merge_request) }
diff --git a/spec/models/merge_requests/merge_schedule_spec.rb b/spec/models/merge_requests/merge_schedule_spec.rb
new file mode 100644
index 00000000000..a869032f118
--- /dev/null
+++ b/spec/models/merge_requests/merge_schedule_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MergeRequests::MergeSchedule, feature_category: :code_review_workflow do
+ subject { create(:merge_request_merge_schedule) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:merge_request).required }
+ end
+
+ describe 'callbacks' do
+ let(:merge_request) { create(:merge_request) }
+ let(:schedule) do
+ create(:merge_request_merge_schedule, merge_request: merge_request,
+ project_id: merge_request.target_project.id + 1)
+ end
+
+ it 'overrides project_id to the correct sharding key' do
+ expect(schedule.project_id).to eq(merge_request.target_project.id)
+ end
+ end
+end
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index a99774dc89b..53adc11adfe 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -137,7 +137,7 @@ module LoginHelpers
def register_via(provider, uid, email, additional_info: {})
mock_auth_hash(provider, uid, email, additional_info: additional_info)
visit new_user_registration_path
- expect(page).to have_content('Create an account using').or(have_content('Register with'))
+ expect(page).to have_content('Create an account using').or(have_content('Continue with'))
click_button Gitlab::Auth::OAuth::Provider.label_for(provider)
end
diff --git a/spec/support/helpers/sign_up_helpers.rb b/spec/support/helpers/sign_up_helpers.rb
index 8712a8c1b35..a8b1701db03 100644
--- a/spec/support/helpers/sign_up_helpers.rb
+++ b/spec/support/helpers/sign_up_helpers.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
module SignUpHelpers
- def fill_in_sign_up_form(new_user, submit_button_text = 'Register', invite: false)
+ def fill_in_sign_up_form(new_user, invite: false)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
fill_in 'new_user_username', with: new_user.username
@@ -16,7 +16,7 @@ module SignUpHelpers
yield if block_given?
- click_button submit_button_text
+ click_button _('Continue')
end
def confirm_email(new_user)
diff --git a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb b/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
deleted file mode 100644
index d3b3434b339..00000000000
--- a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'issue_edit snowplow tracking' do
- let(:category) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CATEGORY }
- let(:action) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_ACTION }
- let(:label) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_LABEL }
- let(:namespace) { project.namespace }
-
- it_behaves_like 'Snowplow event tracking with RedisHLL context'
-end
diff --git a/spec/views/devise/shared/_signup_box.html.haml_spec.rb b/spec/views/devise/registrations/_signup_box_form.html.haml_spec.rb
similarity index 76%
rename from spec/views/devise/shared/_signup_box.html.haml_spec.rb
rename to spec/views/devise/registrations/_signup_box_form.html.haml_spec.rb
index 43ca2af87e8..15b570c364d 100644
--- a/spec/views/devise/shared/_signup_box.html.haml_spec.rb
+++ b/spec/views/devise/registrations/_signup_box_form.html.haml_spec.rb
@@ -2,14 +2,13 @@
require 'spec_helper'
-RSpec.describe 'devise/shared/_signup_box', feature_category: :system_access do
+RSpec.describe 'devise/registrations/_signup_box_form', feature_category: :system_access do
before do
stub_devise
allow(view).to receive(:arkose_labs_enabled?).and_return(false)
- allow(view).to receive(:show_omniauth_providers).and_return(false)
allow(view).to receive(:url).and_return('_url_')
allow(view).to receive(:button_text).and_return('')
- allow(view).to receive(:tracking_label).and_return('')
+ allow(view).to receive(:preregistration_tracking_label).and_return('')
stub_template 'devise/shared/_error_messages.html.haml' => ''
end
diff --git a/spec/views/devise/registrations/new.html.haml_spec.rb b/spec/views/devise/registrations/new.html.haml_spec.rb
new file mode 100644
index 00000000000..0961da8d1cd
--- /dev/null
+++ b/spec/views/devise/registrations/new.html.haml_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'devise/registrations/new', feature_category: :system_access do
+ let(:resource) { Users::RegistrationsBuildService.new(nil, {}).execute }
+ let(:tracking_label) { '_some_registration_' }
+
+ subject { render && rendered }
+
+ before do
+ allow(view).to receive(:resource).and_return(resource)
+ allow(view).to receive(:resource_name).and_return(:user)
+ allow(view).to receive(:glm_tracking_params).and_return({})
+ allow(view).to receive(:registration_path_params).and_return({})
+ allow(view).to receive(:preregistration_tracking_label).and_return(tracking_label)
+ allow(view).to receive(:arkose_labs_enabled?)
+ end
+
+ context 'for omniauth provider buttons' do
+ let(:provider_label) { :github }
+ let(:tracking_action) { "#{provider_label}_sso" }
+
+ before do
+ allow(view).to receive(:providers).and_return([provider_label])
+ end
+
+ it { is_expected.to have_css("[data-track-action='#{tracking_action}'][data-track-label='#{tracking_label}']") }
+ it { is_expected.to have_content(_('Continue with:')) }
+ it { is_expected.to have_css('form[action="/users/auth/github"]') }
+ end
+
+ context 'without broadcast messaging' do
+ it { is_expected.not_to render_template('layouts/_broadcast') }
+ end
+end
diff --git a/spec/views/devise/shared/_signup_omniauth_provider_list_spec.rb b/spec/views/devise/shared/_signup_omniauth_provider_list_spec.rb
deleted file mode 100644
index 988f2e2df8a..00000000000
--- a/spec/views/devise/shared/_signup_omniauth_provider_list_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'devise/shared/_signup_omniauth_provider_list', feature_category: :system_access do
- let_it_be(:provider_label) { :github }.freeze
- let_it_be(:tracking_label) { 'free_registration' }.freeze
- let_it_be(:tracking_action) { "#{provider_label}_sso" }.freeze
-
- subject { rendered }
-
- before do
- allow(view).to receive(:providers).and_return([provider_label])
- allow(view).to receive(:tracking_label).and_return(tracking_label)
- allow(view).to receive(:glm_tracking_params).and_return({})
- render
- end
-
- shared_examples 'sso buttons have snowplow tracking' do
- it 'contains tracking attributes' do
- css = "[data-track-action='#{tracking_action}']"
- css += "[data-track-label='#{tracking_label}']"
-
- expect(rendered).to have_css(css)
- end
- end
-
- it { is_expected.to have_content(_("Register with:")) }
-
- it_behaves_like 'sso buttons have snowplow tracking'
-
- it 'renders button in form' do
- expect(rendered).to have_css('form[action="/users/auth/github"]')
- end
-end