Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									ea413f31cf
								
							
						
					
					
						commit
						277496b843
					
				|  | @ -63,7 +63,7 @@ and cross-posted (with the command results) to the responsible team's Slack chan | |||
|   Cross link the issue here if it does. | ||||
| - [ ] Ensure that you or a representative in development can be available for at least 2 hours after feature flag updates in production. | ||||
|   If a different developer will be covering, or an exception is needed, please inform the oncall SRE by using the `@sre-oncall` Slack alias. | ||||
| - [ ] Ensure that [documentation has been updated](https://docs.gitlab.com/ee/development/documentation/feature_flags.html). | ||||
| - [ ] Ensure that documentation exists for the feature, and the [version history text](https://docs.gitlab.com/ee/development/documentation/feature_flags.html#add-version-history-text) has been updated. | ||||
| - [ ] Leave a comment on [the feature issue][main-issue] announcing estimated time when this feature flag will be enabled on GitLab.com. | ||||
| - [ ] Ensure that any breaking changes have been announced following the [release post process](https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecations-removals-and-breaking-changes) to ensure GitLab customers are aware. | ||||
| - [ ] Notify the [`#support_gitlab-com` Slack channel](https://gitlab.slack.com/archives/C4XFU81LG) and your team channel ([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#communicate-the-change)). | ||||
|  |  | |||
|  | @ -483,9 +483,9 @@ BackgroundMigration/FeatureCategory: | |||
|   Include: | ||||
|     - 'lib/gitlab/background_migration/*.rb' | ||||
| 
 | ||||
| BackgroundMigration/MissingDictionaryFile: | ||||
| BackgroundMigration/DictionaryFile: | ||||
|   Enabled: true | ||||
|   EnforcedSince: 20230307160251 | ||||
|   EnforcedSince: 20231018100907 | ||||
|   Include: | ||||
|     - 'db/post_migrate/*.rb' | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,4 @@ | |||
| --- | ||||
| # Grace period will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/428931 | ||||
| BackgroundMigration/DictionaryFile: | ||||
|   Details: grace period | ||||
|  | @ -36,8 +36,7 @@ export default function initDiffsApp(store = notesStore) { | |||
|         iid: dataset.iid || '', | ||||
|         endpointCoverage: dataset.endpointCoverage || '', | ||||
|         endpointCodequality: dataset.endpointCodequality || '', | ||||
|         // This is a workaround which will be solved in: https://gitlab.com/gitlab-org/gitlab/-/issues/428758
 | ||||
|         sastReportAvailable: Boolean(dataset.endpointSast), | ||||
|         sastReportAvailable: dataset.endpointSast, | ||||
|         helpPagePath: dataset.helpPagePath, | ||||
|         currentUser: JSON.parse(dataset.currentUserData) || {}, | ||||
|         changesEmptyStateIllustration: dataset.changesEmptyStateIllustration, | ||||
|  |  | |||
|  | @ -657,7 +657,6 @@ $discord: #5865f2; | |||
| $linkedin: #2867b2; | ||||
| $mastodon: #6364ff; | ||||
| $skype: #0078d7; | ||||
| $twitter: #1d9bf0; | ||||
| 
 | ||||
| /* | ||||
|  *  Award emoji | ||||
|  |  | |||
|  | @ -235,7 +235,7 @@ | |||
| } | ||||
| 
 | ||||
| .twitter-icon { | ||||
|   color: $twitter; | ||||
|   color: var(--gl-text-color, $gl-text-color); | ||||
| } | ||||
| 
 | ||||
| .discord-icon { | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AcmeChallengesController < BaseActionController | ||||
| # rubocop:disable Rails/ApplicationController | ||||
| class AcmeChallengesController < ActionController::Base | ||||
|   def show | ||||
|     if acme_order | ||||
|       render plain: acme_order.challenge_file_content, content_type: 'text/plain' | ||||
|  | @ -15,3 +16,4 @@ class AcmeChallengesController < BaseActionController | |||
|     @acme_order ||= PagesDomainAcmeOrder.find_by_domain_and_token(params[:domain], params[:token]) | ||||
|   end | ||||
| end | ||||
| # rubocop:enable Rails/ApplicationController | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| require 'gon' | ||||
| require 'fogbugz' | ||||
| 
 | ||||
| class ApplicationController < BaseActionController | ||||
| class ApplicationController < ActionController::Base | ||||
|   include Gitlab::GonHelper | ||||
|   include Gitlab::NoCacheHeaders | ||||
|   include GitlabRoutingHelper | ||||
|  |  | |||
|  | @ -1,31 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| # GitLab lightweight base action controller | ||||
| # | ||||
| # This class should be limited to content that | ||||
| # is desired/required for *all* controllers in | ||||
| # GitLab. | ||||
| # | ||||
| # Most controllers inherit from `ApplicationController`. | ||||
| # Some controllers don't want or need all of that | ||||
| # logic and instead inherit from `ActionController::Base`. | ||||
| # This makes it difficult to set security headers and | ||||
| # handle other critical logic across *all* controllers. | ||||
| # | ||||
| # Between this controller and `ApplicationController` | ||||
| # no controller should ever inherit directly from | ||||
| # `ActionController::Base` | ||||
| # | ||||
| # rubocop:disable Rails/ApplicationController | ||||
| # rubocop:disable Gitlab/NamespacedClass | ||||
| class BaseActionController < ActionController::Base | ||||
|   before_action :security_headers | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def security_headers | ||||
|     headers['Cross-Origin-Opener-Policy'] = 'same-origin' if ::Feature.enabled?(:coop_header) | ||||
|   end | ||||
| end | ||||
| # rubocop:enable Gitlab/NamespacedClass | ||||
| # rubocop:enable Rails/ApplicationController | ||||
|  | @ -1,6 +1,7 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class ChaosController < BaseActionController | ||||
| # rubocop:disable Rails/ApplicationController | ||||
| class ChaosController < ActionController::Base | ||||
|   before_action :validate_chaos_secret, unless: :development_or_test? | ||||
| 
 | ||||
|   def leakmem | ||||
|  | @ -94,3 +95,4 @@ class ChaosController < BaseActionController | |||
|     Rails.env.development? || Rails.env.test? | ||||
|   end | ||||
| end | ||||
| # rubocop:enable Rails/ApplicationController | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class HealthController < BaseActionController | ||||
| # rubocop:disable Rails/ApplicationController | ||||
| class HealthController < ActionController::Base | ||||
|   protect_from_forgery with: :exception, prepend: true | ||||
|   include RequiresAllowlistedMonitoringClient | ||||
| 
 | ||||
|  | @ -39,3 +40,4 @@ class HealthController < BaseActionController | |||
|     render json: result.json, status: result.http_status | ||||
|   end | ||||
| end | ||||
| # rubocop:enable Rails/ApplicationController | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class MetricsController < BaseActionController | ||||
| # rubocop:disable Rails/ApplicationController | ||||
| class MetricsController < ActionController::Base | ||||
|   include RequiresAllowlistedMonitoringClient | ||||
| 
 | ||||
|   protect_from_forgery with: :exception, prepend: true | ||||
|  | @ -35,3 +36,4 @@ class MetricsController < BaseActionController | |||
|     ) | ||||
|   end | ||||
| end | ||||
| # rubocop:enable Rails/ApplicationController | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| %p.text-center | ||||
| %p{ class: local_assigns.fetch(:wrapper_class, 'gl-text-center') } | ||||
|   %span.light | ||||
|     = _('Already have an account?') | ||||
|     - path_params = { redirect_to_referer: 'yes' } | ||||
|  |  | |||
|  | @ -1,77 +1,10 @@ | |||
| - max_first_name_length = max_last_name_length = 127 | ||||
| - borderless ||= false | ||||
| - form_resource_name = "new_#{resource_name}" | ||||
| 
 | ||||
| .gl-mb-3.gl-p-4{ class: (borderless ? '' : 'gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-base') } | ||||
|   = yield :omniauth_providers_top if show_omniauth_providers | ||||
| 
 | ||||
|   = 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 | ||||
|       = invisible_captcha nonce: true, autocomplete: SecureRandom.alphanumeric(12) | ||||
|     .name.form-row | ||||
|       .col.form-group | ||||
|         = f.label :first_name, _('First name'), for: 'new_user_first_name' | ||||
|         = f.text_field :first_name, | ||||
|           class: 'form-control gl-form-input top js-block-emoji js-validate-length', | ||||
|           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 }, | ||||
|           testid: 'new-user-first-name-field' }, | ||||
|           required: true, | ||||
|           title: _('This field is required.') | ||||
|       .col.form-group | ||||
|         = f.label :last_name, _('Last name'), for: 'new_user_last_name' | ||||
|         = f.text_field :last_name, | ||||
|           class: 'form-control gl-form-input top js-block-emoji js-validate-length', | ||||
|           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 }, | ||||
|           testid: 'new-user-last-name-field' }, | ||||
|           required: true, | ||||
|           title: _('This field is required.') | ||||
|     .username.form-group | ||||
|       = f.label :username, _('Username') | ||||
|       = f.text_field :username, | ||||
|         class: 'form-control gl-form-input middle js-block-emoji js-validate-length js-validate-username', | ||||
|         data: signup_username_data_attributes, | ||||
|         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...') | ||||
|     .form-group | ||||
|       = f.label :email, _('Email') | ||||
|       = f.email_field :email, | ||||
|         class: 'form-control gl-form-input middle js-validate-email', | ||||
|         data: { testid: 'new-user-email-field' }, | ||||
|         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?') | ||||
|       -# 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 }, | ||||
|         minimum_password_length: @minimum_password_length, | ||||
|         testid: 'new-user-password-field', | ||||
|         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 } | ||||
|       = render_if_exists 'shared/password_requirements_list' | ||||
|     = render_if_exists 'devise/shared/phone_verification', form: f | ||||
|   = render 'devise/shared/signup_box_form', | ||||
|     button_text: button_text, | ||||
|     url: url, | ||||
|     show_omniauth_providers: omniauth_enabled? && button_based_providers_enabled? | ||||
| 
 | ||||
|     .form-group | ||||
|       - if arkose_labs_enabled? | ||||
|         = render_if_exists 'devise/registrations/arkose_labs' | ||||
|       - elsif show_recaptcha_sign_up? | ||||
|         = recaptcha_tags nonce: content_security_policy_nonce | ||||
| 
 | ||||
|     = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, block: true, button_options: { data: { testid: 'new-user-register-button' }}) do | ||||
|       = button_text | ||||
| 
 | ||||
|     = render 'devise/shared/terms_of_service_notice', button_text: button_text | ||||
| 
 | ||||
|   = yield :omniauth_providers_bottom if show_omniauth_providers | ||||
|  |  | |||
|  | @ -0,0 +1,73 @@ | |||
| - max_first_name_length = max_last_name_length = 127 | ||||
| - form_resource_name = "new_#{resource_name}" | ||||
| 
 | ||||
| = 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 | ||||
|     = invisible_captcha nonce: true, autocomplete: SecureRandom.alphanumeric(12) | ||||
|   .name.form-row | ||||
|     .col.form-group | ||||
|       = f.label :first_name, _('First name'), for: 'new_user_first_name' | ||||
|       = f.text_field :first_name, | ||||
|         class: 'form-control gl-form-input top js-block-emoji js-validate-length', | ||||
|         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 }, | ||||
|         testid: 'new-user-first-name-field' }, | ||||
|         required: true, | ||||
|         title: _('This field is required.') | ||||
|     .col.form-group | ||||
|       = f.label :last_name, _('Last name'), for: 'new_user_last_name' | ||||
|       = f.text_field :last_name, | ||||
|         class: 'form-control gl-form-input top js-block-emoji js-validate-length', | ||||
|         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 }, | ||||
|         testid: 'new-user-last-name-field' }, | ||||
|         required: true, | ||||
|         title: _('This field is required.') | ||||
|   .username.form-group | ||||
|     = f.label :username, _('Username') | ||||
|     = f.text_field :username, | ||||
|       class: 'form-control gl-form-input middle js-block-emoji js-validate-length js-validate-username', | ||||
|       data: signup_username_data_attributes, | ||||
|       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...') | ||||
|   .form-group | ||||
|     = f.label :email, _('Email') | ||||
|     = f.email_field :email, | ||||
|       class: 'form-control gl-form-input middle js-validate-email', | ||||
|       data: { testid: 'new-user-email-field' }, | ||||
|       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?') | ||||
|     -# 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 }, | ||||
|       minimum_password_length: @minimum_password_length, | ||||
|       testid: 'new-user-password-field', | ||||
|       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 } | ||||
|     = render_if_exists 'shared/password_requirements_list' | ||||
|   = render_if_exists 'devise/shared/phone_verification', form: f | ||||
| 
 | ||||
|   .form-group | ||||
|     - if arkose_labs_enabled? | ||||
|       = render_if_exists 'devise/registrations/arkose_labs' | ||||
|     - elsif show_recaptcha_sign_up? | ||||
|       = recaptcha_tags nonce: content_security_policy_nonce | ||||
| 
 | ||||
|   = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, block: true, button_options: { data: { testid: 'new-user-register-button' }}) do | ||||
|     = button_text | ||||
| 
 | ||||
|   = render 'devise/shared/terms_of_service_notice', button_text: button_text | ||||
| 
 | ||||
| = yield :omniauth_providers_bottom if show_omniauth_providers | ||||
|  | @ -14,7 +14,10 @@ | |||
|     = _("Create an account using:") | ||||
|   .gl-display-flex.gl-justify-content-between.gl-flex-wrap | ||||
|     - providers.each do |provider| | ||||
|       = button_to omniauth_authorize_path(:user, provider, register_omniauth_params(local_assigns)), class: "btn gl-button btn-default gl-w-full gl-mb-4 js-oauth-login #{qa_selector_for_provider(provider)}", data: { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label }, id: "oauth-login-#{provider}" do | ||||
|       = button_to omniauth_authorize_path(:user, provider, register_omniauth_params(local_assigns)), | ||||
|         class: "btn gl-button btn-default gl-w-full gl-mb-4 js-oauth-login #{qa_selector_for_provider(provider)}", | ||||
|         data: { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label }, | ||||
|         id: "oauth-login-#{provider}" do | ||||
|         - if provider_has_icon?(provider) | ||||
|           = provider_image_tag(provider) | ||||
|         %span.gl-button-text | ||||
|  |  | |||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: coop_header | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131571 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/425701 | ||||
| milestone: '16.5' | ||||
| type: development | ||||
| group: group::authentication | ||||
| default_enabled: false | ||||
|  | @ -1,6 +1,6 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| # rubocop: disable BackgroundMigration/MissingDictionaryFile | ||||
| # rubocop: disable BackgroundMigration/DictionaryFile | ||||
| 
 | ||||
| class RescheduleMigrationForRemediation < Gitlab::Database::Migration[2.1] | ||||
|   MIGRATION = 'MigrateRemediationsForVulnerabilityFindings' | ||||
|  | @ -29,4 +29,4 @@ class RescheduleMigrationForRemediation < Gitlab::Database::Migration[2.1] | |||
|     delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, []) | ||||
|   end | ||||
| end | ||||
| # rubocop: enable BackgroundMigration/MissingDictionaryFile | ||||
| # rubocop: enable BackgroundMigration/DictionaryFile | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| # rubocop:disable BackgroundMigration/MissingDictionaryFile | ||||
| # rubocop:disable BackgroundMigration/DictionaryFile | ||||
| class RequeueBackfillProjectWikiRepositories < Gitlab::Database::Migration[2.1] | ||||
|   MIGRATION = "BackfillProjectWikiRepositories" | ||||
|   DELAY_INTERVAL = 2.minutes | ||||
|  | @ -26,4 +26,4 @@ class RequeueBackfillProjectWikiRepositories < Gitlab::Database::Migration[2.1] | |||
|     delete_batched_background_migration(MIGRATION, :projects, :id, []) | ||||
|   end | ||||
| end | ||||
| # rubocop:enable BackgroundMigration/MissingDictionaryFile | ||||
| # rubocop:enable BackgroundMigration/DictionaryFile | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| # rubocop:disable BackgroundMigration/MissingDictionaryFile | ||||
| # rubocop:disable BackgroundMigration/DictionaryFile | ||||
| 
 | ||||
| class RescheduleEvidencesHandlingUnicode < Gitlab::Database::Migration[2.1] | ||||
|   restrict_gitlab_migration gitlab_schema: :gitlab_main | ||||
|  | @ -29,4 +29,4 @@ class RescheduleEvidencesHandlingUnicode < Gitlab::Database::Migration[2.1] | |||
|     delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, []) | ||||
|   end | ||||
| end | ||||
| # rubocop:enable BackgroundMigration/MissingDictionaryFile | ||||
| # rubocop:enable BackgroundMigration/DictionaryFile | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| # rubocop: disable BackgroundMigration/MissingDictionaryFile | ||||
| # rubocop: disable BackgroundMigration/DictionaryFile | ||||
| 
 | ||||
| class RescheduleMigrationForLinksFromMetadata < Gitlab::Database::Migration[2.1] | ||||
|   MIGRATION = 'MigrateLinksForVulnerabilityFindings' | ||||
|  | @ -29,4 +29,4 @@ class RescheduleMigrationForLinksFromMetadata < Gitlab::Database::Migration[2.1] | |||
|     delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, []) | ||||
|   end | ||||
| end | ||||
| # rubocop: enable BackgroundMigration/MissingDictionaryFile | ||||
| # rubocop: enable BackgroundMigration/DictionaryFile | ||||
|  |  | |||
|  | @ -0,0 +1,16 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class CleanupBigintConversionForCiProjectMonthlyUsagesSharedRunnersDuration < Gitlab::Database::Migration[2.1] | ||||
|   enable_lock_retries! | ||||
| 
 | ||||
|   TABLE = :ci_project_monthly_usages | ||||
|   COLUMNS = [:shared_runners_duration] | ||||
| 
 | ||||
|   def up | ||||
|     cleanup_conversion_of_integer_to_bigint(TABLE, COLUMNS) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     restore_conversion_of_integer_to_bigint(TABLE, COLUMNS) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,16 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class CleanupBigintConversionForCiNamespaceMonthlyUsagesSharedRunnersDuration < Gitlab::Database::Migration[2.1] | ||||
|   enable_lock_retries! | ||||
| 
 | ||||
|   TABLE = :ci_namespace_monthly_usages | ||||
|   COLUMNS = [:shared_runners_duration] | ||||
| 
 | ||||
|   def up | ||||
|     cleanup_conversion_of_integer_to_bigint(TABLE, COLUMNS) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     restore_conversion_of_integer_to_bigint(TABLE, COLUMNS) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,17 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class RemoveRedundantGroupStagesIndex < Gitlab::Database::Migration[2.2] | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   milestone '16.6' | ||||
| 
 | ||||
|   INDEX_NAME = 'index_analytics_ca_group_stages_on_group_id' | ||||
| 
 | ||||
|   def up | ||||
|     remove_concurrent_index_by_name(:analytics_cycle_analytics_group_stages, INDEX_NAME) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     add_concurrent_index(:analytics_cycle_analytics_group_stages, :group_id, name: INDEX_NAME) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,17 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class RemoveRedundantMrMetricsIndexOnTargetProjectId < Gitlab::Database::Migration[2.2] | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   milestone '16.6' | ||||
| 
 | ||||
|   INDEX_NAME = 'index_merge_request_metrics_on_target_project_id' | ||||
| 
 | ||||
|   def up | ||||
|     remove_concurrent_index_by_name(:merge_request_metrics, INDEX_NAME) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     add_concurrent_index(:merge_request_metrics, :target_project_id, name: INDEX_NAME) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1 @@ | |||
| 1bd136e7d4fb7c34030cea6c915a2eeae619ea5ae1a701cb4d5d4bb069df7113 | ||||
|  | @ -0,0 +1 @@ | |||
| bf03b09c6247d2f5c3543f4046b48763dfc7e6fb2cdaedc52d8cfc8777f70e71 | ||||
|  | @ -0,0 +1 @@ | |||
| add7ce4f9fb56221512227d5aa3697245d537cd5c975978b7dc6dab992890e4e | ||||
|  | @ -0,0 +1 @@ | |||
| 08275dacbe6b1bd44cc67834fc77d6615e43ebd1b9a85edc9e7237cbecc57315 | ||||
|  | @ -335,15 +335,6 @@ BEGIN | |||
| END; | ||||
| $$; | ||||
| 
 | ||||
| CREATE FUNCTION trigger_bbb95b2d6929() RETURNS trigger | ||||
|     LANGUAGE plpgsql | ||||
|     AS $$ | ||||
| BEGIN | ||||
|   NEW."shared_runners_duration_convert_to_bigint" := NEW."shared_runners_duration"; | ||||
|   RETURN NEW; | ||||
| END; | ||||
| $$; | ||||
| 
 | ||||
| CREATE FUNCTION trigger_bfad0e2b9c86() RETURNS trigger | ||||
|     LANGUAGE plpgsql | ||||
|     AS $$ | ||||
|  | @ -353,15 +344,6 @@ BEGIN | |||
| END; | ||||
| $$; | ||||
| 
 | ||||
| CREATE FUNCTION trigger_c0353bbb6145() RETURNS trigger | ||||
|     LANGUAGE plpgsql | ||||
|     AS $$ | ||||
| BEGIN | ||||
|   NEW."shared_runners_duration_convert_to_bigint" := NEW."shared_runners_duration"; | ||||
|   RETURN NEW; | ||||
| END; | ||||
| $$; | ||||
| 
 | ||||
| CREATE FUNCTION unset_has_issues_on_vulnerability_reads() RETURNS trigger | ||||
|     LANGUAGE plpgsql | ||||
|     AS $$ | ||||
|  | @ -13788,7 +13770,6 @@ CREATE TABLE ci_namespace_monthly_usages ( | |||
|     namespace_id bigint NOT NULL, | ||||
|     date date NOT NULL, | ||||
|     notification_level smallint DEFAULT 100 NOT NULL, | ||||
|     shared_runners_duration_convert_to_bigint integer DEFAULT 0 NOT NULL, | ||||
|     created_at timestamp with time zone, | ||||
|     amount_used numeric(18,4) DEFAULT 0.0 NOT NULL, | ||||
|     shared_runners_duration bigint DEFAULT 0 NOT NULL, | ||||
|  | @ -14074,7 +14055,6 @@ CREATE TABLE ci_project_monthly_usages ( | |||
|     id bigint NOT NULL, | ||||
|     project_id bigint NOT NULL, | ||||
|     date date NOT NULL, | ||||
|     shared_runners_duration_convert_to_bigint integer DEFAULT 0 NOT NULL, | ||||
|     created_at timestamp with time zone, | ||||
|     amount_used numeric(18,4) DEFAULT 0.0 NOT NULL, | ||||
|     shared_runners_duration bigint DEFAULT 0 NOT NULL, | ||||
|  | @ -31413,8 +31393,6 @@ CREATE INDEX index_allowed_email_domains_on_group_id ON allowed_email_domains US | |||
| 
 | ||||
| CREATE INDEX index_analytics_ca_group_stages_on_end_event_label_id ON analytics_cycle_analytics_group_stages USING btree (end_event_label_id); | ||||
| 
 | ||||
| CREATE INDEX index_analytics_ca_group_stages_on_group_id ON analytics_cycle_analytics_group_stages USING btree (group_id); | ||||
| 
 | ||||
| CREATE INDEX index_analytics_ca_group_stages_on_relative_position ON analytics_cycle_analytics_group_stages USING btree (relative_position); | ||||
| 
 | ||||
| CREATE INDEX index_analytics_ca_group_stages_on_start_event_label_id ON analytics_cycle_analytics_group_stages USING btree (start_event_label_id); | ||||
|  | @ -33129,8 +33107,6 @@ CREATE INDEX index_merge_request_metrics_on_merged_by_id ON merge_request_metric | |||
| 
 | ||||
| CREATE INDEX index_merge_request_metrics_on_pipeline_id ON merge_request_metrics USING btree (pipeline_id); | ||||
| 
 | ||||
| CREATE INDEX index_merge_request_metrics_on_target_project_id ON merge_request_metrics USING btree (target_project_id); | ||||
| 
 | ||||
| CREATE INDEX index_merge_request_review_llm_summaries_on_mr_diff_id ON merge_request_review_llm_summaries USING btree (merge_request_diff_id); | ||||
| 
 | ||||
| CREATE INDEX index_merge_request_review_llm_summaries_on_review_id ON merge_request_review_llm_summaries USING btree (review_id); | ||||
|  | @ -36779,12 +36755,8 @@ CREATE TRIGGER trigger_7f3d66a7d7f5 BEFORE INSERT OR UPDATE ON ci_pipeline_varia | |||
| 
 | ||||
| CREATE TRIGGER trigger_b2d852e1e2cb BEFORE INSERT OR UPDATE ON ci_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_b2d852e1e2cb(); | ||||
| 
 | ||||
| CREATE TRIGGER trigger_bbb95b2d6929 BEFORE INSERT OR UPDATE ON ci_project_monthly_usages FOR EACH ROW EXECUTE FUNCTION trigger_bbb95b2d6929(); | ||||
| 
 | ||||
| CREATE TRIGGER trigger_bfad0e2b9c86 BEFORE INSERT OR UPDATE ON ci_pipeline_messages FOR EACH ROW EXECUTE FUNCTION trigger_bfad0e2b9c86(); | ||||
| 
 | ||||
| CREATE TRIGGER trigger_c0353bbb6145 BEFORE INSERT OR UPDATE ON ci_namespace_monthly_usages FOR EACH ROW EXECUTE FUNCTION trigger_c0353bbb6145(); | ||||
| 
 | ||||
| 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(); | ||||
| 
 | ||||
| CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON integrations FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker(); | ||||
|  |  | |||
|  | @ -121,6 +121,7 @@ To install `agentk`: | |||
|    kind: Secret | ||||
|    metadata: | ||||
|      name: gitlab-agent-token | ||||
|      namespace: gitlab | ||||
|    type: Opaque | ||||
|    stringData: | ||||
|       token: "<your-token-here>" | ||||
|  |  | |||
|  | @ -94,37 +94,6 @@ module API | |||
|       # rubocop: enable CodeReuse/ActiveRecord | ||||
| 
 | ||||
|       helpers do | ||||
|         def commit | ||||
|           strong_memoize(:commit) do | ||||
|             user_project.commit(params[:sha]) | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def all_matching_pipelines | ||||
|           pipelines = user_project.ci_pipelines.newest_first(sha: commit.sha) | ||||
|           pipelines = pipelines.for_ref(params[:ref]) if params[:ref] | ||||
|           pipelines = pipelines.id_in(params[:pipeline_id]) if params[:pipeline_id] | ||||
|           pipelines | ||||
|         end | ||||
| 
 | ||||
|         def apply_job_state!(job) | ||||
|           case params[:state] | ||||
|           when 'pending' | ||||
|             job.enqueue! | ||||
|           when 'running' | ||||
|             job.enqueue | ||||
|             job.run! | ||||
|           when 'success' | ||||
|             job.success! | ||||
|           when 'failed' | ||||
|             job.drop!(:api_failure) | ||||
|           when 'canceled' | ||||
|             job.cancel! | ||||
|           else | ||||
|             render_api_error!('invalid state', 400) | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def optional_commit_status_params | ||||
|           updatable_optional_attributes = %w[target_url description coverage] | ||||
|           attributes_for_keys(updatable_optional_attributes) | ||||
|  |  | |||
|  | @ -3,7 +3,8 @@ | |||
| # This is a base controller for doorkeeper. | ||||
| # It adds the `can?` helper used in the views. | ||||
| module Gitlab | ||||
|   class BaseDoorkeeperController < BaseActionController | ||||
|   # rubocop:disable Rails/ApplicationController | ||||
|   class BaseDoorkeeperController < ActionController::Base | ||||
|     include Gitlab::Allowable | ||||
|     include EnforcesTwoFactorAuthentication | ||||
|     include SessionsHelper | ||||
|  | @ -12,4 +13,5 @@ module Gitlab | |||
| 
 | ||||
|     helper_method :can? | ||||
|   end | ||||
|   # rubocop:enable Rails/ApplicationController | ||||
| end | ||||
|  |  | |||
|  | @ -6,7 +6,8 @@ | |||
| 
 | ||||
| module Gitlab | ||||
|   module RequestForgeryProtection | ||||
|     class Controller < BaseActionController | ||||
|     # rubocop:disable Rails/ApplicationController | ||||
|     class Controller < ActionController::Base | ||||
|       protect_from_forgery with: :exception, prepend: true | ||||
| 
 | ||||
|       def initialize | ||||
|  | @ -39,5 +40,6 @@ module Gitlab | |||
|     rescue ActionController::InvalidAuthenticityToken | ||||
|       false | ||||
|     end | ||||
|     # rubocop:enable Rails/ApplicationController | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -24739,12 +24739,21 @@ msgstr "" | |||
| msgid "InProductMarketing|%{strong_start}GitLab Inc.%{strong_end} 268 Bush Street, #350, San Francisco, CA 94104, USA" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|%{upper_start}Start your 30-day free trial of%{upper_end} %{lower_start}GitLab Ultimate%{lower_end}" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|Accelerate your digital transform" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|Blog" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|Built-in security" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|Deliver software faster" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|Ensure compliance" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -24760,12 +24769,18 @@ msgstr "" | |||
| msgid "InProductMarketing|If you no longer wish to receive marketing emails from us," | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|Improve collaboration and visibility" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|Invite unlimited colleagues" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|No credit card required" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|No credit card required." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "InProductMarketing|Start a Self-Managed trial" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -33224,6 +33239,9 @@ msgstr "" | |||
| msgid "Options" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Or create your own GitLab account:" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Ordered list" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -42651,6 +42669,9 @@ msgstr "" | |||
| msgid "SecurityOrchestration|Branch types don't match any existing branches." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "SecurityOrchestration|Cannot create an empty policy" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "SecurityOrchestration|Choose a project" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -42726,6 +42747,9 @@ msgstr "" | |||
| msgid "SecurityOrchestration|Failed to load images." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "SecurityOrchestration|For any MR that matches this policy's rules, only the override project approval settings apply. No additional approvals are required." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "SecurityOrchestration|For any merge request on %{branches}%{commitType}%{branchExceptionsString}" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -42806,6 +42830,9 @@ msgid_plural "SecurityOrchestration|On runners with the tags:" | |||
| msgstr[0] "" | ||||
| msgstr[1] "" | ||||
| 
 | ||||
| msgid "SecurityOrchestration|Only overriding settings will take effect" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "SecurityOrchestration|Only owners can update Security Policy Project" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -42971,6 +42998,9 @@ msgstr "" | |||
| msgid "SecurityOrchestration|This is a project-level policy" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "SecurityOrchestration|This policy doesn't contain any actions or override project approval settings. You cannot create an empty policy." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "SecurityOrchestration|This policy is inherited" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -44720,6 +44750,9 @@ msgstr "" | |||
| msgid "Sign up" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Sign up for your free trial with:" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Sign up was successful! Please confirm your email to sign in." | ||||
| msgstr "" | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ ENV GITLAB_LICENSE_MODE=test \ | |||
| 
 | ||||
| # Clone GDK at specific sha and bootstrap packages | ||||
| # | ||||
| ARG GDK_SHA=65cf4576208b9f79c54c0042c44024c0008deafc | ||||
| ARG GDK_SHA=d843a4d237bbb9c2f04d2cbddc89fd6dadeb86cf | ||||
| RUN set -eux; \ | ||||
|     git clone --depth 1 https://gitlab.com/gitlab-org/gitlab-development-kit.git && cd gitlab-development-kit; \ | ||||
|     git fetch --depth 1 origin ${GDK_SHA} && git -c advice.detachedHead=false checkout ${GDK_SHA}; \ | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ module QA | |||
|   module Page | ||||
|     module Registration | ||||
|       class SignUp < Page::Base | ||||
|         view 'app/views/devise/shared/_signup_box.html.haml' do | ||||
|         view 'app/views/devise/shared/_signup_box_form.html.haml' do | ||||
|           element 'new-user-first-name-field' | ||||
|           element 'new-user-last-name-field' | ||||
|           element 'new-user-email-field' | ||||
|  |  | |||
|  | @ -11,8 +11,11 @@ module QA | |||
|       # This *could* be different than the api_client.user or the api_user provided by the QA::Resource::ApiFabricator | ||||
|       attr_writer :user | ||||
| 
 | ||||
|       attribute :id | ||||
|       attribute :token | ||||
|       attributes :id, :token | ||||
| 
 | ||||
|       attribute :expires_at do | ||||
|         Time.now.utc.to_date + 2 | ||||
|       end | ||||
| 
 | ||||
|       # Only Admins can create PAT via the API. | ||||
|       # If Runtime::Env.admin_personal_access_token is provided, fabricate via the API, | ||||
|  | @ -49,7 +52,7 @@ module QA | |||
|         api_client = Runtime::API::Client.new(:gitlab, | ||||
|           is_new_session: false, | ||||
|           user: user, | ||||
|           personal_access_token: self.token) | ||||
|           personal_access_token: token) | ||||
|         request_url = Runtime::API::Request.new(api_client, | ||||
|           "/personal_access_tokens?user_id=#{user.id}", | ||||
|           per_page: '100').url | ||||
|  | @ -88,12 +91,7 @@ module QA | |||
|       end | ||||
| 
 | ||||
|       def cache_token | ||||
|         QA::Resource::PersonalAccessTokenCache.set_token_for_username(user.username, self.token) if @user && self.token | ||||
|       end | ||||
| 
 | ||||
|       # Expire in 2 days just in case the token is created just before midnight | ||||
|       def expires_at | ||||
|         @expires_at || Time.now.utc.to_date + 2 | ||||
|         QA::Resource::PersonalAccessTokenCache.set_token_for_username(user.username, token) if @user && token | ||||
|       end | ||||
| 
 | ||||
|       def fabricate! | ||||
|  | @ -115,7 +113,7 @@ module QA | |||
| 
 | ||||
|           cache_token | ||||
| 
 | ||||
|           self.token | ||||
|           token | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -18,6 +18,21 @@ module QA | |||
|       tags: { import_type: ENV["QA_IMPORT_TYPE"], import_repo: ENV["QA_LARGE_IMPORT_REPO"] || "rspec/rspec-core" } | ||||
|     } do | ||||
|     describe 'Project import', product_group: :import_and_integrate do # rubocop:disable RSpec/MultipleMemoizedHelpers | ||||
|       let!(:api_client) { Runtime::API::Client.as_admin } | ||||
|       let!(:user) { create(:user, api_client: api_client) } | ||||
|       let!(:user_api_client) do | ||||
|         Runtime::API::Client.new( | ||||
|           user: user, | ||||
|           is_new_session: false, | ||||
|           personal_access_token: Resource::PersonalAccessToken.fabricate_via_api! do |pat| | ||||
|             pat.user = user | ||||
|             # importing very large project can take multiple days | ||||
|             # token must not expire while we still poll for import result | ||||
|             pat.expires_at = (Time.now.to_date + 5) | ||||
|           end.token | ||||
|         ) | ||||
|       end | ||||
| 
 | ||||
|       # Full object comparison is a fairly heavy operation | ||||
|       # Importer itself returns counts of objects it fetched and counts it imported | ||||
|       # We can use that for a lightweight comparison for very large projects | ||||
|  | @ -103,10 +118,6 @@ module QA | |||
|         ] | ||||
|       end | ||||
| 
 | ||||
|       let(:api_client) { Runtime::API::Client.as_admin } | ||||
| 
 | ||||
|       let(:user) { create(:user, api_client: api_client) } | ||||
| 
 | ||||
|       let(:github_client) do | ||||
|         Octokit::Client.new( | ||||
|           access_token: ENV['QA_LARGE_IMPORT_GH_TOKEN'] || Runtime::Env.github_access_token, | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module RuboCop | ||||
|   class BatchedBackgroundMigrations | ||||
|   class BatchedBackgroundMigrationsDictionary | ||||
|     DICTIONARY_BASE_DIR = 'db/docs/batched_background_migrations' | ||||
| 
 | ||||
|     attr_reader :queued_migration_version | ||||
|  | @ -14,6 +14,7 @@ module RuboCop | |||
|           next unless dictionary['queued_migration_version'].present? | ||||
| 
 | ||||
|           data[dictionary['queued_migration_version'].to_s] = { | ||||
|             introduced_by_url: dictionary['introduced_by_url'], | ||||
|             finalize_after: dictionary['finalize_after'], | ||||
|             finalized_by: dictionary['finalized_by'].to_s | ||||
|           } | ||||
|  | @ -26,7 +27,21 @@ module RuboCop | |||
|     end | ||||
| 
 | ||||
|     def finalized_by | ||||
|       self.class.dictionary_data.dig(queued_migration_version.to_s, :finalized_by) | ||||
|       dictionary_data&.dig(:finalized_by) | ||||
|     end | ||||
| 
 | ||||
|     def finalize_after | ||||
|       dictionary_data&.dig(:finalize_after) | ||||
|     end | ||||
| 
 | ||||
|     def introduced_by_url | ||||
|       dictionary_data&.dig(:introduced_by_url) | ||||
|     end | ||||
| 
 | ||||
|     private | ||||
| 
 | ||||
|     def dictionary_data | ||||
|       @dictionary_data ||= self.class.dictionary_data[queued_migration_version.to_s] | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,17 +1,23 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require_relative '../../migration_helpers' | ||||
| require_relative '../../batched_background_migrations_dictionary' | ||||
| 
 | ||||
| module RuboCop | ||||
|   module Cop | ||||
|     module BackgroundMigration | ||||
|       # Checks the batched background migration has the corresponding dictionary file | ||||
|       class MissingDictionaryFile < RuboCop::Cop::Base | ||||
|       class DictionaryFile < RuboCop::Cop::Base | ||||
|         include MigrationHelpers | ||||
| 
 | ||||
|         MSG = "Missing %{file_name}. " \ | ||||
|               "Use the generator 'batched_background_migration' to create dictionary files automatically. " \ | ||||
|               "For more details refer: https://docs.gitlab.com/ee/development/database/batched_background_migrations.html#generator" | ||||
|         MSG = { | ||||
|           missing_key: "Mandatory key '%{key}' is missing from the dictionary. Please add with an appropriate value.", | ||||
|           missing_dictionary: <<-MESSAGE.delete("\n").squeeze(' ').strip | ||||
|             Missing %{file_name}. | ||||
|             Use the generator 'batched_background_migration' to create dictionary files automatically. | ||||
|             For more details refer: https://docs.gitlab.com/ee/development/database/batched_background_migrations.html#generator | ||||
|           MESSAGE | ||||
|         }.freeze | ||||
| 
 | ||||
|         DICTIONARY_DIR = "db/docs/batched_background_migrations" | ||||
| 
 | ||||
|  | @ -35,9 +41,10 @@ module RuboCop | |||
|                              migration_name_node.value | ||||
|                            end | ||||
| 
 | ||||
|           return if dictionary_file?(migration_name) | ||||
|           error_code, msg_params = validate_dictionary_file(migration_name, node) | ||||
|           return unless error_code.present? | ||||
| 
 | ||||
|           add_offense(node, message: format(MSG, file_name: dictionary_file_path(migration_name))) | ||||
|           add_offense(node, message: format(MSG[error_code], msg_params)) | ||||
|         end | ||||
| 
 | ||||
|         private | ||||
|  | @ -50,6 +57,18 @@ module RuboCop | |||
|           File.join(rails_root, DICTIONARY_DIR, "#{migration_class_name.underscore}.yml") | ||||
|         end | ||||
| 
 | ||||
|         def validate_dictionary_file(migration_name, node) | ||||
|           unless dictionary_file?(migration_name) | ||||
|             return [:missing_dictionary, { file_name: dictionary_file_path(migration_name) }] | ||||
|           end | ||||
| 
 | ||||
|           bbm_dictionary = RuboCop::BatchedBackgroundMigrationsDictionary.new(version(node)) | ||||
| 
 | ||||
|           return [:missing_key, { key: :finalize_after }] unless bbm_dictionary.finalize_after.present? | ||||
| 
 | ||||
|           return [:missing_key, { key: :introduced_by_url }] unless bbm_dictionary.introduced_by_url.present? | ||||
|         end | ||||
| 
 | ||||
|         def rails_root | ||||
|           @rails_root ||= File.expand_path('../../..', __dir__) | ||||
|         end | ||||
|  | @ -1,7 +1,7 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require_relative '../../migration_helpers' | ||||
| require_relative '../../batched_background_migrations' | ||||
| require_relative '../../batched_background_migrations_dictionary' | ||||
| 
 | ||||
| module RuboCop | ||||
|   module Cop | ||||
|  | @ -43,7 +43,7 @@ module RuboCop | |||
|         private | ||||
| 
 | ||||
|         def fetch_finalized_by(queued_migration_version) | ||||
|           BatchedBackgroundMigrations.new(queued_migration_version).finalized_by | ||||
|           BatchedBackgroundMigrationsDictionary.new(queued_migration_version).finalized_by | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe AcmeChallengesController, type: :request, feature_category: :pages do | ||||
|   it_behaves_like 'Base action controller' do | ||||
|     subject(:request) { get acme_challenge_path } | ||||
|   end | ||||
| end | ||||
|  | @ -1,15 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe ApplicationController, type: :request, feature_category: :shared do | ||||
|   let_it_be(:user) { create(:user) } | ||||
| 
 | ||||
|   before do | ||||
|     sign_in(user) | ||||
|   end | ||||
| 
 | ||||
|   it_behaves_like 'Base action controller' do | ||||
|     subject(:request) { get root_path } | ||||
|   end | ||||
| end | ||||
|  | @ -1,14 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe ChaosController, type: :request, feature_category: :tooling do | ||||
|   it_behaves_like 'Base action controller' do | ||||
|     before do | ||||
|       # Stub leak_mem so we don't actually leak memory for the base action controller tests. | ||||
|       allow(Gitlab::Chaos).to receive(:leak_mem).with(100, 30.seconds) | ||||
|     end | ||||
| 
 | ||||
|     subject(:request) { get leakmem_chaos_path } | ||||
|   end | ||||
| end | ||||
|  | @ -73,9 +73,7 @@ RSpec.describe HealthController, feature_category: :database do | |||
|   end | ||||
| 
 | ||||
|   describe 'GET /-/readiness' do | ||||
|     subject(:request) { get readiness_path, params: params, headers: headers } | ||||
| 
 | ||||
|     it_behaves_like 'Base action controller' | ||||
|     subject { get '/-/readiness', params: params, headers: headers } | ||||
| 
 | ||||
|     shared_context 'endpoint responding with readiness data' do | ||||
|       context 'when requesting instance-checks' do | ||||
|  |  | |||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe MetricsController, type: :request, feature_category: :metrics do | ||||
|   it_behaves_like 'Base action controller' do | ||||
|     subject(:request) { get metrics_path } | ||||
|   end | ||||
| end | ||||
|  | @ -20,10 +20,6 @@ RSpec.describe Oauth::AuthorizationsController, feature_category: :system_access | |||
|   end | ||||
| 
 | ||||
|   describe 'GET #new' do | ||||
|     it_behaves_like 'Base action controller' do | ||||
|       subject(:request) { get oauth_authorization_path } | ||||
|     end | ||||
| 
 | ||||
|     context 'when application redirect URI has a custom scheme' do | ||||
|       context 'when CSP is disabled' do | ||||
|         before do | ||||
|  |  | |||
|  | @ -6,9 +6,7 @@ RSpec.describe RegistrationsController, type: :request, feature_category: :syste | |||
|   describe 'POST #create' do | ||||
|     let_it_be(:user_attrs) { build_stubbed(:user).slice(:first_name, :last_name, :username, :email, :password) } | ||||
| 
 | ||||
|     subject(:request) { post user_registration_path, params: { user: user_attrs } } | ||||
| 
 | ||||
|     it_behaves_like 'Base action controller' | ||||
|     subject(:create_user) { post user_registration_path, params: { user: user_attrs } } | ||||
| 
 | ||||
|     context 'when email confirmation is required' do | ||||
|       before do | ||||
|  | @ -17,7 +15,7 @@ RSpec.describe RegistrationsController, type: :request, feature_category: :syste | |||
|       end | ||||
| 
 | ||||
|       it 'redirects to the `users_almost_there_path`', unless: Gitlab.ee? do | ||||
|         request | ||||
|         create_user | ||||
| 
 | ||||
|         expect(response).to redirect_to(users_almost_there_path(email: user_attrs[:email])) | ||||
|       end | ||||
|  |  | |||
|  | @ -7,10 +7,6 @@ RSpec.describe 'Sessions', feature_category: :system_access do | |||
| 
 | ||||
|   let(:user) { create(:user) } | ||||
| 
 | ||||
|   it_behaves_like 'Base action controller' do | ||||
|     subject(:request) { get new_user_session_path } | ||||
|   end | ||||
| 
 | ||||
|   context 'for authentication', :allow_forgery_protection do | ||||
|     it 'logout does not require a csrf token' do | ||||
|       login_as(user) | ||||
|  |  | |||
|  | @ -2,20 +2,24 @@ | |||
| 
 | ||||
| require 'rubocop_spec_helper' | ||||
| 
 | ||||
| require_relative '../../rubocop/batched_background_migrations' | ||||
| require_relative '../../rubocop/batched_background_migrations_dictionary' | ||||
| 
 | ||||
| RSpec.describe RuboCop::BatchedBackgroundMigrations, feature_category: :database do | ||||
| RSpec.describe RuboCop::BatchedBackgroundMigrationsDictionary, feature_category: :database do | ||||
|   let(:bbm_dictionary_file_name) { "#{described_class::DICTIONARY_BASE_DIR}/test_migration.yml" } | ||||
|   let(:migration_version) { 20230307160250 } | ||||
|   let(:finalized_by_version) { 20230307160255 } | ||||
|   let(:introduced_by_url) { 'https://test_url' } | ||||
|   let(:finalize_after) { '202312011212' } | ||||
| 
 | ||||
|   let(:bbm_dictionary_data) do | ||||
|     { | ||||
|       migration_job_name: 'TestMigration', | ||||
|       feature_category: :database, | ||||
|       introduced_by_url: 'https://test_url', | ||||
|       introduced_by_url: introduced_by_url, | ||||
|       milestone: 16.5, | ||||
|       queued_migration_version: migration_version, | ||||
|       finalized_by: finalized_by_version | ||||
|       finalized_by: finalized_by_version, | ||||
|       finalize_after: finalize_after | ||||
|     } | ||||
|   end | ||||
| 
 | ||||
|  | @ -40,4 +44,24 @@ RSpec.describe RuboCop::BatchedBackgroundMigrations, feature_category: :database | |||
|       expect(described_class.new('random').finalized_by).to be_nil | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#introduced_by_url' do | ||||
|     it 'returns the introduced_by_url of the bbm with given version' do | ||||
|       expect(batched_background_migration.introduced_by_url).to eq(introduced_by_url) | ||||
|     end | ||||
| 
 | ||||
|     it 'returns nothing for non-existing bbm dictionary' do | ||||
|       expect(described_class.new('random').introduced_by_url).to be_nil | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#finalize_after' do | ||||
|     it 'returns the finalize_after timestamp of the bbm with given version' do | ||||
|       expect(batched_background_migration.finalize_after).to eq(finalize_after) | ||||
|     end | ||||
| 
 | ||||
|     it 'returns nothing for non-existing bbm dictionary' do | ||||
|       expect(described_class.new('random').finalize_after).to be_nil | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,17 +1,36 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rubocop_spec_helper' | ||||
| require_relative '../../../../rubocop/cop/background_migration/missing_dictionary_file' | ||||
| require_relative '../../../../rubocop/cop/background_migration/dictionary_file' | ||||
| 
 | ||||
| RSpec.describe RuboCop::Cop::BackgroundMigration::MissingDictionaryFile, feature_category: :database do | ||||
| RSpec.describe RuboCop::Cop::BackgroundMigration::DictionaryFile, feature_category: :database do | ||||
|   let(:config) do | ||||
|     RuboCop::Config.new( | ||||
|       'BackgroundMigration/MissingDictionaryFile' => { | ||||
|         'EnforcedSince' => 20230307160251 | ||||
|       'BackgroundMigration/DictionaryFile' => { | ||||
|         'EnforcedSince' => 20231018100907 | ||||
|       } | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   shared_examples 'migration with missing dictionary keys offense' do |missing_key| | ||||
|     it 'registers an offense' do | ||||
|       expect_offense(<<~RUBY) | ||||
|         class QueueMyMigration < Gitlab::Database::Migration[2.1] | ||||
|         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{format(described_class::MSG[:missing_key], key: missing_key)} | ||||
|           MIGRATION = 'MyMigration' | ||||
| 
 | ||||
|           def up | ||||
|             queue_batched_background_migration( | ||||
|               MIGRATION, | ||||
|               :users, | ||||
|               :id | ||||
|             ) | ||||
|           end | ||||
|         end | ||||
|       RUBY | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   context 'for non post migrations' do | ||||
|     before do | ||||
|       allow(cop).to receive(:in_post_deployment_migration?).and_return(false) | ||||
|  | @ -57,7 +76,7 @@ RSpec.describe RuboCop::Cop::BackgroundMigration::MissingDictionaryFile, feature | |||
| 
 | ||||
|       context 'for migrations before enforced time' do | ||||
|         before do | ||||
|           allow(cop).to receive(:version).and_return(20230307160250) | ||||
|           allow(cop).to receive(:version).and_return(20230918100907) | ||||
|         end | ||||
| 
 | ||||
|         it 'does not throw any offenses' do | ||||
|  | @ -79,7 +98,7 @@ RSpec.describe RuboCop::Cop::BackgroundMigration::MissingDictionaryFile, feature | |||
| 
 | ||||
|       context 'for migrations after enforced time' do | ||||
|         before do | ||||
|           allow(cop).to receive(:version).and_return(20230307160252) | ||||
|           allow(cop).to receive(:version).and_return(20231118100907) | ||||
|         end | ||||
| 
 | ||||
|         it 'throws offense on not having the appropriate dictionary file with migration name as a constant' do | ||||
|  | @ -114,9 +133,33 @@ RSpec.describe RuboCop::Cop::BackgroundMigration::MissingDictionaryFile, feature | |||
|           RUBY | ||||
|         end | ||||
| 
 | ||||
|         it 'does not throw offense with appropriate dictionary file' do | ||||
|           expect(File).to receive(:exist?).with(dictionary_file_path).and_return(true) | ||||
|         context 'with dictionary file' do | ||||
|           let(:introduced_by_url) { 'https://test_url' } | ||||
|           let(:finalize_after) { '20230507160251' } | ||||
| 
 | ||||
|           before do | ||||
|             allow(File).to receive(:exist?).with(dictionary_file_path).and_return(true) | ||||
| 
 | ||||
|             allow_next_instance_of(RuboCop::BatchedBackgroundMigrationsDictionary) do |dictionary| | ||||
|               allow(dictionary).to receive(:finalize_after).and_return(finalize_after) | ||||
|               allow(dictionary).to receive(:introduced_by_url).and_return(introduced_by_url) | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           context 'without introduced_by_url' do | ||||
|             it_behaves_like 'migration with missing dictionary keys offense', :introduced_by_url do | ||||
|               let(:introduced_by_url) { nil } | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           context 'without finalize_after' do | ||||
|             it_behaves_like 'migration with missing dictionary keys offense', :finalize_after do | ||||
|               let(:finalize_after) { nil } | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           context 'with required dictionary keys' do | ||||
|             it 'does not throw offense with appropriate dictionary file' do | ||||
|               expect_no_offenses(<<~RUBY) | ||||
|                 class QueueMyMigration < Gitlab::Database::Migration[2.1] | ||||
|                   MIGRATION = 'MyMigration' | ||||
|  | @ -135,3 +178,5 @@ RSpec.describe RuboCop::Cop::BackgroundMigration::MissingDictionaryFile, feature | |||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -99,7 +99,7 @@ RSpec.describe RuboCop::Cop::Migration::UnfinishedDependencies, feature_category | |||
| 
 | ||||
|     context 'with properly finalized dependent background migrations' do | ||||
|       before do | ||||
|         allow_next_instance_of(RuboCop::BatchedBackgroundMigrations) do |bbms| | ||||
|         allow_next_instance_of(RuboCop::BatchedBackgroundMigrationsDictionary) do |bbms| | ||||
|           allow(bbms).to receive(:finalized_by).and_return(version - 5) | ||||
|         end | ||||
|       end | ||||
|  |  | |||
|  | @ -6,9 +6,6 @@ abuse_reports: | |||
| alert_management_http_integrations: | ||||
|   index_http_integrations_on_project_and_endpoint: | ||||
|   - index_alert_management_http_integrations_on_project_id | ||||
| analytics_cycle_analytics_group_stages: | ||||
|   index_group_stages_on_group_id_group_value_stream_id_and_name: | ||||
|   - index_analytics_ca_group_stages_on_group_id | ||||
| approval_project_rules_users: | ||||
|   index_approval_project_rules_users_1: | ||||
|   - index_approval_project_rules_users_on_approval_project_rule_id | ||||
|  | @ -111,9 +108,6 @@ members: | |||
| merge_request_assignees: | ||||
|   index_merge_request_assignees_on_merge_request_id_and_user_id: | ||||
|   - index_merge_request_assignees_on_merge_request_id | ||||
| merge_request_metrics: | ||||
|   index_mr_metrics_on_target_project_id_merged_at_nulls_last: | ||||
|   - index_merge_request_metrics_on_target_project_id | ||||
| merge_requests: | ||||
|   index_merge_requests_on_author_id_and_created_at: | ||||
|   - index_merge_requests_on_author_id | ||||
|  |  | |||
|  | @ -1,26 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| # Requires `request` subject to be defined | ||||
| # | ||||
| # subject(:request) { get root_path } | ||||
| RSpec.shared_examples 'Base action controller' do | ||||
|   describe 'security headers' do | ||||
|     describe 'Cross-Origin-Opener-Policy' do | ||||
|       it 'sets the header' do | ||||
|         request | ||||
| 
 | ||||
|         expect(response.headers['Cross-Origin-Opener-Policy']).to eq('same-origin') | ||||
|       end | ||||
| 
 | ||||
|       context 'when coop_header feature flag is disabled' do | ||||
|         it 'does not set the header' do | ||||
|           stub_feature_flags(coop_header: false) | ||||
| 
 | ||||
|           request | ||||
| 
 | ||||
|           expect(response.headers['Cross-Origin-Opener-Policy']).to be_nil | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
		Loading…
	
		Reference in New Issue