diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index 5be43f039c1..004cc2e7794 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-7e94489bc9d892e3cb25f9f9e7f4f7ce15ac0ee8
+d19af9f22edb454a615b9bb90fdcaa24f72eb488
diff --git a/app/assets/javascripts/admin/application_settings/setup_metrics_and_profiling.js b/app/assets/javascripts/admin/application_settings/setup_metrics_and_profiling.js
index 3da138c256c..022ac0b8a73 100644
--- a/app/assets/javascripts/admin/application_settings/setup_metrics_and_profiling.js
+++ b/app/assets/javascripts/admin/application_settings/setup_metrics_and_profiling.js
@@ -2,6 +2,7 @@ import initSetHelperText, {
initOptionMetricsState,
} from '~/pages/admin/application_settings/metrics_and_profiling/usage_statistics';
import PayloadPreviewer from '~/pages/admin/application_settings/payload_previewer';
+import initProductUsageData from '~/pages/admin/application_settings/metrics_and_profiling/product_usage_data';
export default () => {
Array.from(document.querySelectorAll('.js-payload-preview-trigger')).forEach((trigger) => {
@@ -11,3 +12,4 @@ export default () => {
initSetHelperText();
initOptionMetricsState();
+initProductUsageData();
diff --git a/app/assets/javascripts/ci/pipeline_details/graph/components/job_group_dropdown.vue b/app/assets/javascripts/ci/pipeline_details/graph/components/job_group_dropdown.vue
index 53fff67cf09..31867d5f9b3 100644
--- a/app/assets/javascripts/ci/pipeline_details/graph/components/job_group_dropdown.vue
+++ b/app/assets/javascripts/ci/pipeline_details/graph/components/job_group_dropdown.vue
@@ -2,11 +2,13 @@
import {
GlBadge,
GlDisclosureDropdown,
+ GlDisclosureDropdownGroup,
GlTooltipDirective,
GlResizeObserverDirective,
} from '@gitlab/ui';
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
import JobDropdownItem from '~/ci/common/private/job_dropdown_item.vue';
+import { FAILED_STATUS } from '~/ci/constants';
import { JOB_DROPDOWN } from '../constants';
import JobItem from './job_item.vue';
@@ -22,6 +24,7 @@ export default {
JobItem,
GlBadge,
GlDisclosureDropdown,
+ GlDisclosureDropdownGroup,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -75,6 +78,15 @@ export default {
isFailed() {
return this.group?.status?.group === 'failed';
},
+ nonFailedJobs() {
+ return this.group?.jobs.filter((job) => job.status?.group !== FAILED_STATUS);
+ },
+ failedJobs() {
+ return this.group?.jobs.filter((job) => job.status?.group === FAILED_STATUS);
+ },
+ hasFailedJobs() {
+ return this.failedJobs.length > 0;
+ },
},
methods: {
handleResize() {
@@ -122,12 +134,26 @@ export default {
-
+
+
+ {{ s__('Pipelines|Failed jobs') }}
+
+
+
+
+
+
+
diff --git a/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/constants.js b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/constants.js
index e76bed66ced..f5967c041e2 100644
--- a/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/constants.js
+++ b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/constants.js
@@ -22,4 +22,6 @@ export const ELEMENT_IDS = Object.freeze({
USAGE_PING_FEATURES_ENABLED: 'application_setting_usage_ping_features_enabled',
USAGE_PING_ENABLED: 'application_setting_usage_ping_enabled',
OPTIONAL_METRICS_IN_SERVICE_PING: 'application_setting_include_optional_metrics_in_service_ping',
+ PRODUCT_USAGE_DATA: 'application_setting_gitlab_product_usage_data_enabled',
+ SNOWPLOW_ENABLED: 'application_setting_snowplow_enabled',
});
diff --git a/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/product_usage_data.js b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/product_usage_data.js
new file mode 100644
index 00000000000..e0e98a1d83d
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/product_usage_data.js
@@ -0,0 +1,33 @@
+import { ELEMENT_IDS } from './constants';
+
+export default function initProductUsageData() {
+ const productUsageCheckbox = document.getElementById(ELEMENT_IDS.PRODUCT_USAGE_DATA);
+ const snowplowCheckbox = document.getElementById(ELEMENT_IDS.SNOWPLOW_ENABLED);
+ const snowplowSettings = document.getElementById('js-snowplow-settings');
+
+ const toggleSnowplowSettings = () => {
+ if (!snowplowCheckbox || !snowplowSettings) return;
+
+ if (snowplowCheckbox.checked) {
+ snowplowSettings.style.display = 'block';
+ } else {
+ snowplowSettings.style.display = 'none';
+ }
+ };
+
+ toggleSnowplowSettings();
+
+ productUsageCheckbox?.addEventListener('change', () => {
+ if (productUsageCheckbox.checked) {
+ snowplowCheckbox.checked = false;
+ toggleSnowplowSettings();
+ }
+ });
+
+ snowplowCheckbox?.addEventListener('change', () => {
+ if (snowplowCheckbox.checked) {
+ productUsageCheckbox.checked = false;
+ }
+ toggleSnowplowSettings();
+ });
+}
diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue
index 77f23dec6b7..b8107053a37 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -340,6 +340,9 @@ export default {
canSummarizeComments() {
return this.workItem.userPermissions?.summarizeComments;
},
+ hasBlockedWorkItemsFeature() {
+ return this.workItem.userPermissions?.blockedWorkItems;
+ },
isDiscussionLocked() {
return this.workItemNotes?.discussionLocked;
},
@@ -1208,6 +1211,7 @@ export default {
:work-item-type="workItem.workItemType.name"
:can-admin-work-item-link="canAdminWorkItemLink"
:active-child-item-id="activeChildItemId"
+ :has-blocked-work-items-feature="hasBlockedWorkItemsFeature"
@showModal="openContextualView"
/>
diff --git a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_add_relationship_form.vue b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_add_relationship_form.vue
index 7d04217aa4a..09ee19b2843 100644
--- a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_add_relationship_form.vue
+++ b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_add_relationship_form.vue
@@ -52,6 +52,11 @@ export default {
required: false,
default: () => [],
},
+ hasBlockedWorkItemsFeature: {
+ type: Boolean,
+ required: true,
+ default: false,
+ },
},
data() {
return {
@@ -182,22 +187,24 @@ export default {
{{ error }}
-
-
-
-
- {{ $options.i18n.linkItemInputLabel }}
-
+
+
+
+
+
+ {{ $options.i18n.linkItemInputLabel }}
+
+
- {{ $options.i18n.emptyStateMessage }}
+ {{ emptyStateMessage }}
diff --git a/app/assets/stylesheets/page_bundles/pipeline.scss b/app/assets/stylesheets/page_bundles/pipeline.scss
index 0814381f9b6..d31d553bc13 100644
--- a/app/assets/stylesheets/page_bundles/pipeline.scss
+++ b/app/assets/stylesheets/page_bundles/pipeline.scss
@@ -223,7 +223,8 @@
border-color: var(--gl-control-border-color-error);
}
-.ci-job-item-failed {
+.ci-job-component > .ci-job-item-failed,
+.ci-job-component.ci-job-item-failed:not(:hover):not(:focus) > a {
@apply gl-bg-feedback-danger;
// stylelint-disable-next-line gitlab/no-gl-class
diff --git a/app/components/rapid_diffs/viewers/no_preview_component.html.haml b/app/components/rapid_diffs/viewers/no_preview_component.html.haml
index 27d71c9036b..8390d9e18ce 100644
--- a/app/components/rapid_diffs/viewers/no_preview_component.html.haml
+++ b/app/components/rapid_diffs/viewers/no_preview_component.html.haml
@@ -6,8 +6,9 @@
%p.rd-no-preview-paragraph
= no_preview_reason
.rd-no-preview-actions
- - if @diff_file.collapsed? && expandable?
- = action_button(button_options: { data: { click: 'showChanges' } }) do
+ - if @diff_file.collapsed? && expandable? || @diff_file.whitespace_only?
+ - click = @diff_file.whitespace_only? ? 'showWhitespaceChanges' : 'showChanges'
+ = action_button(button_options: { data: { click: click } }) do
= _('Show changes')
- elsif expandable?
= action_button(button_options: { data: { click: 'showFileContents' } }) do
diff --git a/app/components/rapid_diffs/viewers/no_preview_component.rb b/app/components/rapid_diffs/viewers/no_preview_component.rb
index 9b2fcb68e51..b7ad2bff798 100644
--- a/app/components/rapid_diffs/viewers/no_preview_component.rb
+++ b/app/components/rapid_diffs/viewers/no_preview_component.rb
@@ -37,6 +37,8 @@ module RapidDiffs
_("Preview size limit exceeded, changes collapsed.")
elsif !@diff_file.diffable?
_("Preview suppressed by a .gitattributes entry or the file's encoding is unsupported.")
+ elsif @diff_file.whitespace_only?
+ _("Contains only whitespace changes.")
elsif @diff_file.new_file? || @diff_file.content_changed?
_("No diff preview for this file type.")
end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 8ec26c6dbbc..d4dc75a523d 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -140,7 +140,11 @@ class SearchController < ApplicationController
def authenticate?
return false if action_name == 'opensearch'
return true if public_visibility_restricted?
- return true if search_service.global_search? && ::Feature.enabled?(:block_anonymous_global_searches, type: :ops)
+
+ if search_service.global_search? && ::Gitlab::CurrentSettings.global_search_block_anonymous_searches_enabled?
+ return true
+ end
+
return true if ::Feature.disabled?(:allow_anonymous_searches, type: :ops)
false
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 3e87b92f884..f02e1928355 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -74,6 +74,13 @@ module ApplicationSettingsHelper
def global_search_settings_checkboxes(form)
[
+ form.gitlab_ui_checkbox_component(
+ :global_search_block_anonymous_searches_enabled,
+ _("Enable blocking of anonymous global search requests"),
+ checkbox_options: {
+ checked: @application_setting.global_search_block_anonymous_searches_enabled, multiple: false
+ }
+ ),
form.gitlab_ui_checkbox_component(
:global_search_issues_enabled,
_("Enable issues tab in global search results"),
@@ -488,6 +495,7 @@ module ApplicationSettingsHelper
:snowplow_database_collector_hostname,
:snowplow_enabled,
:snowplow_app_id,
+ :gitlab_product_usage_data_enabled,
:push_event_hooks_limit,
:push_event_activities_limit,
:custom_http_clone_url_root,
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index d9f241cf7a3..e34d7d99fe6 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -28,6 +28,8 @@ class ApplicationSetting < ApplicationRecord
lock_pypi_package_requests_forwarding
], remove_with: '18.1', remove_after: '2025-05-20'
+ ignore_column :duo_nano_features_enabled, remove_with: '18.1', remove_after: '2025-06-19'
+
KROKI_URL_ERROR_MESSAGE = 'Please check your Kroki URL setting in ' \
'Admin area > Settings > General > Kroki'
@@ -460,6 +462,8 @@ class ApplicationSetting < ApplicationRecord
validate :check_valid_runner_registrars
+ validate :snowplow_and_product_usage_data_are_mutually_exclusive
+
validate :terms_exist, if: :enforce_terms?
validates :external_authorization_service_default_label,
@@ -680,7 +684,8 @@ class ApplicationSetting < ApplicationRecord
validates :clickhouse, json_schema: { filename: "application_setting_clickhouse" }
jsonb_accessor :service_ping_settings,
- gitlab_environment_toolkit_instance: [:boolean, { default: false }]
+ gitlab_environment_toolkit_instance: [:boolean, { default: false }],
+ gitlab_product_usage_data_enabled: [:boolean, { default: true }]
jsonb_accessor :rate_limits_unauthenticated_git_http,
throttle_unauthenticated_git_http_enabled: [:boolean, { default: false }],
@@ -1157,6 +1162,15 @@ class ApplicationSetting < ApplicationRecord
)
end
+ def snowplow_and_product_usage_data_are_mutually_exclusive
+ return unless gitlab_product_usage_data_enabled_changed? || snowplow_enabled_changed?
+ return unless snowplow_enabled && gitlab_product_usage_data_enabled
+
+ message = _('Snowplow tracking and Product event tracking cannot be enabled at the same time. ' \
+ 'Please disable one of them.')
+ errors.add(:base, message)
+ end
+
def validate_url(parsed_url, name, error_message)
return if parsed_url
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index 3446a654ba5..5ef076d30bc 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -103,6 +103,7 @@ module ApplicationSettingImplementation
gitaly_timeout_default: 55,
gitaly_timeout_fast: 10,
gitaly_timeout_medium: 30,
+ gitlab_product_usage_data_enabled: Settings.gitlab['initial_gitlab_product_usage_data'],
gitpod_enabled: false,
gitpod_url: 'https://gitpod.io/',
gravatar_enabled: Settings.gravatar['enabled'],
@@ -593,10 +594,6 @@ module ApplicationSettingImplementation
signup_enabled? && password_authentication_enabled_for_web?
end
- def product_usage_data_enabled?
- true
- end
-
def password_authentication_enabled?
password_authentication_enabled_for_web? || password_authentication_enabled_for_git?
end
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index 83fe1e4abb9..c04199d4517 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -5,6 +5,7 @@ module Ci
include Presentable
include Limitable
include Expirable
+ include Gitlab::EncryptedAttribute
TRIGGER_TOKEN_PREFIX = 'glptt-'
@@ -26,7 +27,7 @@ module Ci
attribute: :encrypted_token,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
- key: Settings.attr_encrypted_db_key_base_32,
+ key: :db_key_base_32,
encode: false
before_validation :set_default_values
diff --git a/app/models/concerns/ci/has_variable.rb b/app/models/concerns/ci/has_variable.rb
index 811fd265b04..db16e2a3874 100644
--- a/app/models/concerns/ci/has_variable.rb
+++ b/app/models/concerns/ci/has_variable.rb
@@ -5,6 +5,8 @@ module Ci
extend ActiveSupport::Concern
included do
+ include Gitlab::EncryptedAttribute
+
enum variable_type: {
env_var: 1,
file: 2
@@ -23,7 +25,7 @@ module Ci
attr_encrypted :value,
mode: :per_attribute_iv_and_salt,
insecure_mode: true,
- key: Settings.attr_encrypted_db_key_base,
+ key: :db_key_base,
algorithm: 'aes-256-cbc'
def key=(new_key)
diff --git a/app/models/concerns/ci/new_has_variable.rb b/app/models/concerns/ci/new_has_variable.rb
index 546d243e5de..664244291c7 100644
--- a/app/models/concerns/ci/new_has_variable.rb
+++ b/app/models/concerns/ci/new_has_variable.rb
@@ -6,10 +6,12 @@ module Ci
include Ci::HasVariable
included do
+ include Gitlab::EncryptedAttribute
+
attr_encrypted :value,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
- key: Settings.attr_encrypted_db_key_base_32,
+ key: :db_key_base_32,
insecure_mode: false
end
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 33b8a51f581..67e12978789 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -49,7 +49,7 @@ class Event < ApplicationRecord
RESET_PROJECT_ACTIVITY_INTERVAL = 1.hour
REPOSITORY_UPDATED_AT_INTERVAL = 5.minutes
- CONTRIBUTABLE_TARGET_TYPES = %w[MergeRequest Issue WorkItem].freeze
+ CONTRIBUTABLE_TARGET_TYPES = %w[MergeRequest Issue WorkItem DesignManagement::Design].freeze
sha_attribute :fingerprint
@@ -98,7 +98,9 @@ class Event < ApplicationRecord
scope :contributions, -> do
contribution_actions = [actions[:pushed], actions[:commented]]
- target_contribution_actions = [actions[:created], actions[:closed], actions[:merged], actions[:approved]]
+ target_contribution_actions = [
+ actions[:created], actions[:closed], actions[:merged], actions[:approved], actions[:updated], actions[:destroyed]
+ ]
where(
'action IN (?) OR (target_type IN (?) AND action IN (?))',
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index 0478ec8fa07..846e75d9bd2 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -4,16 +4,22 @@ class SystemHook < WebHook
extend ::Gitlab::Utils::Override
include TriggerableHooks
- self.allow_legacy_sti_class = true
-
- has_many :web_hook_logs, foreign_key: 'web_hook_id', inverse_of: :web_hook
-
- triggerable_hooks [
+ AVAILABLE_HOOKS = [
:repository_update_hooks,
:push_hooks,
:tag_push_hooks,
:merge_request_hooks
- ]
+ ].freeze
+
+ self.allow_legacy_sti_class = true
+
+ has_many :web_hook_logs, foreign_key: 'web_hook_id', inverse_of: :web_hook
+
+ def self.available_hooks
+ AVAILABLE_HOOKS
+ end
+
+ triggerable_hooks available_hooks
attribute :push_events, default: false
attribute :repository_update_events, default: true
@@ -39,3 +45,5 @@ class SystemHook < WebHook
false
end
end
+
+SystemHook.prepend_mod_with('SystemHook')
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index cf5e9c85cf8..c97618f771d 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -186,6 +186,8 @@ class PersonalAccessToken < ApplicationRecord
def expires_at_before_instance_max_expiry_date
return unless expires_at
+ return unless Gitlab::CurrentSettings.require_personal_access_token_expiry?
+
max_expiry_date = Date.current.advance(days: max_expiration_lifetime_in_days)
return unless expires_at > max_expiry_date
diff --git a/app/models/project.rb b/app/models/project.rb
index 0be103c8e61..3ca346d11e7 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1378,6 +1378,7 @@ class Project < ApplicationRecord
def ancestors(hierarchy_order: nil)
group&.self_and_ancestors(hierarchy_order: hierarchy_order) || Group.none
end
+ alias_method :group_and_ancestors, :ancestors
def ancestors_upto_ids(...)
ancestors_upto(...).pluck(:id)
diff --git a/app/policies/application_setting_policy.rb b/app/policies/application_setting_policy.rb
index a7f6d5b95a2..dc21c45ab78 100644
--- a/app/policies/application_setting_policy.rb
+++ b/app/policies/application_setting_policy.rb
@@ -5,8 +5,13 @@ class ApplicationSettingPolicy < BasePolicy # rubocop:disable Gitlab/NamespacedC
rule { admin }.policy do
enable :read_application_setting
+
+ enable :read_runners_registration_token
enable :update_runners_registration_token
end
- rule { ~runner_registration_token_enabled }.prevent :update_runners_registration_token
+ rule { ~runner_registration_token_enabled }.policy do
+ prevent :read_runners_registration_token
+ prevent :update_runners_registration_token
+ end
end
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 1f313284c78..293303fcd75 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -303,6 +303,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :update_default_branch_protection
enable :create_deploy_token
enable :destroy_deploy_token
+ enable :read_runners_registration_token
enable :update_runners_registration_token
enable :owner_access
enable :update_git_access_protocol
@@ -432,6 +433,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
rule { ~runner_registration_token_enabled }.policy do
prevent :register_group_runners
+ prevent :read_runners_registration_token
prevent :update_runners_registration_token
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 1e5cf13c80e..74177fa035c 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -643,6 +643,7 @@ class ProjectPolicy < BasePolicy
enable :create_runner
enable :admin_project_runners
enable :read_project_runners
+ enable :read_runners_registration_token
enable :update_runners_registration_token
enable :admin_project_google_cloud
enable :admin_project_aws
@@ -1041,6 +1042,7 @@ class ProjectPolicy < BasePolicy
rule { ~runner_registration_token_enabled }.policy do
prevent :register_project_runners
+ prevent :read_runners_registration_token
prevent :update_runners_registration_token
end
diff --git a/app/services/bulk_imports/process_service.rb b/app/services/bulk_imports/process_service.rb
index 01ee2d61252..ce9717a9013 100644
--- a/app/services/bulk_imports/process_service.rb
+++ b/app/services/bulk_imports/process_service.rb
@@ -25,7 +25,12 @@ module BulkImports
private
def process_bulk_import
- bulk_import.start! if bulk_import.created?
+ if bulk_import.created?
+ bulk_import.start!
+ # Fetch and cache the source ghost user id to avoid repeated API calls.
+ # This also avoids inconsistent ghost user mapping if concurrent API responses occasionally fail.
+ BulkImports::SourceInternalUserFinder.new(bulk_import.configuration).set_ghost_user_id
+ end
created_entities.first(next_batch_size).each do |entity|
create_tracker(entity)
diff --git a/app/validators/json_schemas/application_setting_duo_chat.json b/app/validators/json_schemas/application_setting_duo_chat.json
new file mode 100644
index 00000000000..79b987c3aba
--- /dev/null
+++ b/app/validators/json_schemas/application_setting_duo_chat.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "description": "Application Setting Duo Chat",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "duo_chat_expiration_days": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 30,
+ "description": "Number of days thread should count as expired"
+ },
+ "duo_chat_expiration_column": {
+ "type": "string",
+ "description": "Column used to determine expired thread"
+ }
+ }
+}
diff --git a/app/views/admin/application_settings/_product_usage_data.html.haml b/app/views/admin/application_settings/_product_usage_data.html.haml
index 2217bf22101..a8e9aedee2a 100644
--- a/app/views/admin/application_settings/_product_usage_data.html.haml
+++ b/app/views/admin/application_settings/_product_usage_data.html.haml
@@ -3,11 +3,29 @@
%fieldset
.form-group
- - label = s_('AdminSettings|Enable product usage tracking')
- = f.gitlab_ui_checkbox_component :product_usage_data_enabled?, '%{label}'.html_safe % { label: label },
- checkbox_options: { id: 'application_setting_product_usage_data_enabled' },
- label_options: { id: 'service_ping_features_label' }
+ - label = s_('AdminSettings|Enable event tracking')
+ = f.gitlab_ui_checkbox_component :gitlab_product_usage_data_enabled, '%{label}'.html_safe % { label: label },
+ help_text: s_('AdminSettings|Send event data to GitLab.')
+ .form-group
+ = f.gitlab_ui_checkbox_component :snowplow_enabled, _('Enable Snowplow tracking'),
+ help_text: s_('AdminSettings|Send event data to your own Snowplow collector.'),
+ checkbox_options: { data: { testid: 'snowplow-enabled-checkbox' } }
+ #js-snowplow-settings.gl-ml-6
+ .form-group
+ = f.label :snowplow_collector_hostname, _('Collector hostname')
+ = f.text_field :snowplow_collector_hostname, class: 'form-control gl-form-input', placeholder: 'snowplow.example.com'
+ .form-text.gl-text-subtle
+ = _('The hostname of your Snowplow collector.')
+ .form-group
+ = f.label :snowplow_app_id, _('App ID')
+ = f.text_field :snowplow_app_id, class: 'form-control gl-form-input', placeholder: 'gitlab'
+ .form-text.gl-text-subtle
+ = _('The ID of the application.')
+ .form-group
+ = f.label :snowplow_cookie_domain, _('Cookie domain')
+ = f.text_field :snowplow_cookie_domain, class: 'form-control gl-form-input', placeholder: '.your-gitlab-instance.com'
+ .form-text.gl-text-subtle
+ = _('The Snowplow cookie domain.')
= f.submit _('Save changes'), pajamas_button: true
-
diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml
index 60aa567a62c..b030359775d 100644
--- a/app/views/admin/application_settings/general.html.haml
+++ b/app/views/admin/application_settings/general.html.haml
@@ -100,7 +100,7 @@
-# this partial is from JiHu, see details in https://jihulab.com/gitlab-cn/gitlab/-/merge_requests/640
= render_if_exists 'admin/application_settings/feishu_integration'
= render 'admin/application_settings/third_party_offers'
-= render 'admin/application_settings/snowplow'
+= render 'admin/application_settings/snowplow' if Feature.disabled?(:product_usage_data, :instance)
= render_if_exists 'admin/application_settings/product_analytics'
= render 'admin/application_settings/error_tracking' if Feature.enabled?(:gitlab_error_tracking)
= render 'admin/application_settings/eks'
diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml
index b0e77b410a2..fb8b8ca79f9 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -43,12 +43,15 @@
= render 'usage'
- if Feature.enabled?(:product_usage_data, :instance)
- = render ::Layouts::SettingsBlockComponent.new(_('Product usage data tracking'),
+ = render ::Layouts::SettingsBlockComponent.new(_('Event tracking'),
id: 'js-product-usage-data-settings',
testid: 'product-usage-data-settings-content',
expanded: expanded_by_default?) do |c|
- c.with_description do
- = _('Control whether events are sent to GitLab.')
+ - snowplow_link = link_to(_('Snowplow'), 'https://snowplow.io/', target: '_blank', rel: 'noopener noreferrer')
+ - help_link = link_to(_('Learn more.'), help_page_path('development/internal_analytics/internal_event_instrumentation/_index.md'), target: '_blank', rel: 'noopener noreferrer')
+ = safe_format(_('Control whether events are sent to GitLab or your own %{snowplow_link_start}Snowplow%{snowplow_link_end} collector. Only one can be selected at a time and enabling one will disable the other. %{help_link_start}Learn more.%{help_link_end}'),
+ tag_pair(snowplow_link, :snowplow_link_start, :snowplow_link_end), tag_pair(help_link, :help_link_start, :help_link_end))
- c.with_body do
= render 'product_usage_data'
diff --git a/config/application_setting_columns/duo_chat.yml b/config/application_setting_columns/duo_chat.yml
new file mode 100644
index 00000000000..abe010de244
--- /dev/null
+++ b/config/application_setting_columns/duo_chat.yml
@@ -0,0 +1,12 @@
+---
+api_type:
+attr: duo_chat
+clusterwide: true
+column: duo_chat
+db_type: jsonb
+default: "'{}'::jsonb"
+description: A hash containing Duo Chat related settings.
+encrypted: false
+gitlab_com_different_than_default: false
+jihu: false
+not_null: true
diff --git a/config/feature_flags/beta/fireworks_qwen_code_completion.yml b/config/feature_flags/beta/fireworks_qwen_code_completion.yml
deleted file mode 100644
index f80a6476111..00000000000
--- a/config/feature_flags/beta/fireworks_qwen_code_completion.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-name: fireworks_qwen_code_completion
-feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/500742
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/170503
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/500744
-milestone: '17.6'
-group: group::code creation
-type: beta
-default_enabled: false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 1af38f49647..8866d2cb709 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -206,6 +206,12 @@ production: &base
# plaintext. This can be a security risk.
# display_initial_root_password: false
+ ## Product Usage Data
+ # This setting enables or disables product usage data in the GitLab instance.
+ # It will be read on initial installation only. Once GitLab is up and running,
+ # this setting should be toggled from the admin pages in the UI.
+ # initial_gitlab_product_usage_data: true
+
# Allows delivery of emails using Microsoft Graph API with OAuth 2.0 client credentials flow.
microsoft_graph_mailer:
enabled: false
diff --git a/config/gitlab_loose_foreign_keys.yml b/config/gitlab_loose_foreign_keys.yml
index b2f4afbe72f..12722f6786f 100644
--- a/config/gitlab_loose_foreign_keys.yml
+++ b/config/gitlab_loose_foreign_keys.yml
@@ -328,6 +328,10 @@ geo_node_namespace_links:
- table: namespaces
column: namespace_id
on_delete: async_delete
+group_push_rules:
+ - table: namespaces
+ column: group_id
+ on_delete: async_delete
group_security_exclusions:
- table: namespaces
column: group_id
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 64bbe5265f3..56d78dba855 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -251,6 +251,7 @@ Settings.gitlab['max_request_duration_seconds'] ||= 57
Settings.gitlab['display_initial_root_password'] = false if Settings.gitlab['display_initial_root_password'].nil?
Settings.gitlab['weak_passwords_digest_set'] ||= YAML.safe_load(File.open(Rails.root.join('config', 'weak_password_digests.yml')), permitted_classes: [String]).to_set.freeze
Settings.gitlab['log_decompressed_response_bytesize'] = ENV["GITLAB_LOG_DECOMPRESSED_RESPONSE_BYTESIZE"].to_i > 0 ? ENV["GITLAB_LOG_DECOMPRESSED_RESPONSE_BYTESIZE"].to_i : 0
+Settings.gitlab['initial_gitlab_product_usage_data'] = true if Settings.gitlab['initial_gitlab_product_usage_data'].nil?
Gitlab.ee do
Settings.gitlab['mirror_max_delay'] ||= 300
@@ -996,7 +997,7 @@ Gitlab.ee do
Settings.cron_jobs['members_schedule_prune_deletions_worker']['cron'] ||= "*/5 * * * *"
Settings.cron_jobs['members_schedule_prune_deletions_worker']['job_class'] = 'Members::SchedulePruneDeletionsWorker'
Settings.cron_jobs['ai_conversation_cleanup_cron_worker'] ||= {}
- Settings.cron_jobs['ai_conversation_cleanup_cron_worker']['cron'] ||= '30 2 * * *'
+ Settings.cron_jobs['ai_conversation_cleanup_cron_worker']['cron'] ||= '0 * * * *'
Settings.cron_jobs['ai_conversation_cleanup_cron_worker']['job_class'] = 'Ai::Conversation::CleanupCronWorker'
Settings.cron_jobs['ai_active_context_bulk_process_worker'] ||= {}
Settings.cron_jobs['ai_active_context_bulk_process_worker']['cron'] ||= '*/1 * * * *'
diff --git a/config/metrics/settings/product_usage_data_enabled.yml b/config/metrics/settings/product_usage_data_enabled.yml
index 1cd0b6bb7d0..1bc5e9e0d63 100644
--- a/config/metrics/settings/product_usage_data_enabled.yml
+++ b/config/metrics/settings/product_usage_data_enabled.yml
@@ -9,7 +9,7 @@ time_frame: none
data_source: system
instrumentation_class: GitlabSettingsMetric
options:
- setting_method: product_usage_data_enabled?
+ setting_method: gitlab_product_usage_data_enabled?
tiers:
- free
- premium
diff --git a/db/docs/ci_daily_build_group_report_results.yml b/db/docs/ci_daily_build_group_report_results.yml
index b9818c7424c..02fefa43d0b 100644
--- a/db/docs/ci_daily_build_group_report_results.yml
+++ b/db/docs/ci_daily_build_group_report_results.yml
@@ -11,4 +11,4 @@ milestone: '13.0'
gitlab_schema: gitlab_ci
sharding_key:
project_id: projects
-table_size: small
+table_size: medium
diff --git a/db/docs/ci_job_artifact_states.yml b/db/docs/ci_job_artifact_states.yml
index d893fde7569..fe6865188cb 100644
--- a/db/docs/ci_job_artifact_states.yml
+++ b/db/docs/ci_job_artifact_states.yml
@@ -9,4 +9,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75264
milestone: '14.8'
gitlab_schema: gitlab_ci_cell_local
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/493768
-table_size: medium
+table_size: large
diff --git a/db/docs/ci_pipeline_messages.yml b/db/docs/ci_pipeline_messages.yml
index 0049ba103e7..cfa823899e6 100644
--- a/db/docs/ci_pipeline_messages.yml
+++ b/db/docs/ci_pipeline_messages.yml
@@ -10,4 +10,4 @@ milestone: '13.2'
gitlab_schema: gitlab_ci
sharding_key:
project_id: projects
-table_size: large
+table_size: over_limit
diff --git a/db/docs/ci_runner_machines_archived.yml b/db/docs/ci_runner_machines_archived.yml
index 80fa955b031..d688c628c3d 100644
--- a/db/docs/ci_runner_machines_archived.yml
+++ b/db/docs/ci_runner_machines_archived.yml
@@ -8,4 +8,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107801
milestone: '15.8'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/460084
-table_size: small
+table_size: medium
diff --git a/db/docs/ci_runners_archived.yml b/db/docs/ci_runners_archived.yml
index 941426550a4..ed92082d771 100644
--- a/db/docs/ci_runners_archived.yml
+++ b/db/docs/ci_runners_archived.yml
@@ -10,4 +10,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/046b28312704f31
milestone: '8.0'
gitlab_schema: gitlab_ci
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442395
-table_size: small
+table_size: medium
diff --git a/db/docs/group_push_rules.yml b/db/docs/group_push_rules.yml
new file mode 100644
index 00000000000..c705fc7128b
--- /dev/null
+++ b/db/docs/group_push_rules.yml
@@ -0,0 +1,12 @@
+---
+table_name: group_push_rules
+classes:
+- GroupPushRule
+feature_categories:
+- source_code_management
+description: Store push rules for groups
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/185982
+milestone: '17.11'
+gitlab_schema: gitlab_main_cell
+sharding_key:
+ group_id: namespaces
diff --git a/db/docs/group_type_ci_runner_machines.yml b/db/docs/group_type_ci_runner_machines.yml
index 61ac7bf3dcd..860c7a5bf80 100644
--- a/db/docs/group_type_ci_runner_machines.yml
+++ b/db/docs/group_type_ci_runner_machines.yml
@@ -11,4 +11,4 @@ milestone: '17.6'
gitlab_schema: gitlab_ci
sharding_key:
sharding_key_id: namespaces
-table_size: small
+table_size: medium
diff --git a/db/migrate/20240516133122_add_partition_id_to_ci_pipeline_message.rb b/db/migrate/20240516133122_add_partition_id_to_ci_pipeline_message.rb
index 1b29d339e63..c4c7c0d1a15 100644
--- a/db/migrate/20240516133122_add_partition_id_to_ci_pipeline_message.rb
+++ b/db/migrate/20240516133122_add_partition_id_to_ci_pipeline_message.rb
@@ -4,6 +4,8 @@ class AddPartitionIdToCiPipelineMessage < Gitlab::Database::Migration[2.2]
milestone '17.1'
def change
+ # rubocop:disable Migration/PreventAddingColumns -- Legacy migration
add_column(:ci_pipeline_messages, :partition_id, :bigint, default: 100, null: false)
+ # rubocop:enable Migration/PreventAddingColumns -- Legacy migration
end
end
diff --git a/db/migrate/20241014081022_add_project_id_to_ci_pipeline_messages.rb b/db/migrate/20241014081022_add_project_id_to_ci_pipeline_messages.rb
index 804ada9dcfb..432bf1c8036 100644
--- a/db/migrate/20241014081022_add_project_id_to_ci_pipeline_messages.rb
+++ b/db/migrate/20241014081022_add_project_id_to_ci_pipeline_messages.rb
@@ -4,6 +4,8 @@ class AddProjectIdToCiPipelineMessages < Gitlab::Database::Migration[2.2]
milestone '17.6'
def change
+ # rubocop:disable Migration/PreventAddingColumns -- Legacy migration
add_column(:ci_pipeline_messages, :project_id, :bigint)
+ # rubocop:enable Migration/PreventAddingColumns -- Legacy migration
end
end
diff --git a/db/migrate/20250401040207_index_ai_conversation_threads_on_created_at.rb b/db/migrate/20250401040207_index_ai_conversation_threads_on_created_at.rb
new file mode 100644
index 00000000000..c192b938000
--- /dev/null
+++ b/db/migrate/20250401040207_index_ai_conversation_threads_on_created_at.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class IndexAiConversationThreadsOnCreatedAt < Gitlab::Database::Migration[2.2]
+ INDEX_NAME = 'index_ai_conversation_threads_on_created_at'
+
+ disable_ddl_transaction!
+ milestone '17.11'
+
+ def up
+ add_concurrent_index :ai_conversation_threads, :created_at, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index :ai_conversation_threads, :created_at, name: INDEX_NAME
+ end
+end
diff --git a/db/migrate/20250401052407_add_duo_chat_to_application_settings.rb b/db/migrate/20250401052407_add_duo_chat_to_application_settings.rb
new file mode 100644
index 00000000000..7d0c000b7b4
--- /dev/null
+++ b/db/migrate/20250401052407_add_duo_chat_to_application_settings.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddDuoChatToApplicationSettings < Gitlab::Database::Migration[2.2]
+ milestone '17.11'
+ disable_ddl_transaction!
+
+ CONSTRAINT_NAME = 'check_application_settings_duo_chat_is_hash'
+
+ def up
+ add_column :application_settings, :duo_chat, :jsonb, default: {}, null: false
+
+ add_check_constraint(
+ :application_settings,
+ "(jsonb_typeof(duo_chat) = 'object')",
+ CONSTRAINT_NAME
+ )
+ end
+
+ def down
+ remove_column :application_settings, :duo_chat
+ end
+end
diff --git a/db/migrate/20250403155029_create_group_push_rules.rb b/db/migrate/20250403155029_create_group_push_rules.rb
new file mode 100644
index 00000000000..7e9f03ec729
--- /dev/null
+++ b/db/migrate/20250403155029_create_group_push_rules.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class CreateGroupPushRules < Gitlab::Database::Migration[2.2]
+ milestone '17.11'
+
+ def up
+ create_table :group_push_rules, if_not_exists: true do |t|
+ t.timestamps_with_timezone null: false
+
+ t.references :group, foreign_key: { to_table: :namespaces, on_delete: :cascade }, null: false
+
+ t.integer :max_file_size, null: false, default: 0
+
+ t.boolean :member_check, null: false, default: false
+ t.boolean :prevent_secrets, null: false, default: false
+ t.boolean :commit_committer_name_check, null: false, default: false
+ t.boolean :deny_delete_tag
+ t.boolean :reject_unsigned_commits
+ t.boolean :commit_committer_check
+ t.boolean :reject_non_dco_commits
+
+ t.text :commit_message_regex, limit: 511
+ t.text :branch_name_regex, limit: 511
+ t.text :commit_message_negative_regex, limit: 2047
+ t.text :author_email_regex, limit: 511
+ t.text :file_name_regex, limit: 511
+ end
+ end
+
+ def down
+ drop_table :group_push_rules, if_exists: true
+ end
+end
diff --git a/db/post_migrate/20250326034951_ensure_gitlab_product_usage_data_enabled_in_service_ping_settings.rb b/db/post_migrate/20250326034951_ensure_gitlab_product_usage_data_enabled_in_service_ping_settings.rb
new file mode 100644
index 00000000000..e324c9f4a15
--- /dev/null
+++ b/db/post_migrate/20250326034951_ensure_gitlab_product_usage_data_enabled_in_service_ping_settings.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+# See https://docs.gitlab.com/ee/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class EnsureGitlabProductUsageDataEnabledInServicePingSettings < Gitlab::Database::Migration[2.2]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ milestone '17.11'
+
+ def up
+ default_value = true
+
+ execute <<~SQL
+ UPDATE application_settings
+ SET service_ping_settings =
+ CASE
+ WHEN snowplow_enabled = TRUE THEN
+ COALESCE(service_ping_settings, '{}'::jsonb) ||
+ jsonb_build_object('gitlab_product_usage_data_enabled', FALSE)
+ ELSE
+ COALESCE(service_ping_settings, '{}'::jsonb) ||
+ jsonb_build_object('gitlab_product_usage_data_enabled', #{default_value})
+ END
+ SQL
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20250326034951 b/db/schema_migrations/20250326034951
new file mode 100644
index 00000000000..926282fec81
--- /dev/null
+++ b/db/schema_migrations/20250326034951
@@ -0,0 +1 @@
+dc1fbc25f53ef725eb4b05220486d5452c9377699bd914ee55c567dfab6674b0
\ No newline at end of file
diff --git a/db/schema_migrations/20250401040207 b/db/schema_migrations/20250401040207
new file mode 100644
index 00000000000..c9efb6b61b0
--- /dev/null
+++ b/db/schema_migrations/20250401040207
@@ -0,0 +1 @@
+a3b4525eea40fde40c92803d5368f4ada21abe79d5de01b5e5507b2e108fc197
\ No newline at end of file
diff --git a/db/schema_migrations/20250401052407 b/db/schema_migrations/20250401052407
new file mode 100644
index 00000000000..92e60db3cf9
--- /dev/null
+++ b/db/schema_migrations/20250401052407
@@ -0,0 +1 @@
+ff0832e10754451488a921cfb0bfe68390448c1cd4c53d5175d6a378c418a6ce
\ No newline at end of file
diff --git a/db/schema_migrations/20250403155029 b/db/schema_migrations/20250403155029
new file mode 100644
index 00000000000..c1cfe02fa10
--- /dev/null
+++ b/db/schema_migrations/20250403155029
@@ -0,0 +1 @@
+b506cb61c1b71884da28f9e10c9ba164f9f76de8bc608019235cd1a85a38bea2
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index b332e240f43..a1d6db212c2 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -9093,6 +9093,7 @@ CREATE TABLE application_settings (
ci_cd_settings jsonb DEFAULT '{}'::jsonb NOT NULL,
duo_nano_features_enabled boolean,
database_reindexing jsonb DEFAULT '{}'::jsonb NOT NULL,
+ duo_chat jsonb DEFAULT '{}'::jsonb NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
@@ -9148,6 +9149,7 @@ CREATE TABLE application_settings (
CONSTRAINT check_application_settings_cluster_agents_is_hash CHECK ((jsonb_typeof(cluster_agents) = 'object'::text)),
CONSTRAINT check_application_settings_code_creation_is_hash CHECK ((jsonb_typeof(code_creation) = 'object'::text)),
CONSTRAINT check_application_settings_database_reindexing_is_hash CHECK ((jsonb_typeof(database_reindexing) = 'object'::text)),
+ CONSTRAINT check_application_settings_duo_chat_is_hash CHECK ((jsonb_typeof(duo_chat) = 'object'::text)),
CONSTRAINT check_application_settings_duo_workflow_is_hash CHECK ((jsonb_typeof(duo_workflow) = 'object'::text)),
CONSTRAINT check_application_settings_elasticsearch_is_hash CHECK ((jsonb_typeof(elasticsearch) = 'object'::text)),
CONSTRAINT check_application_settings_importers_is_hash CHECK ((jsonb_typeof(importers) = 'object'::text)),
@@ -15136,6 +15138,40 @@ CREATE TABLE group_merge_request_approval_settings (
require_reauthentication_to_approve boolean DEFAULT false NOT NULL
);
+CREATE TABLE group_push_rules (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ group_id bigint NOT NULL,
+ max_file_size integer DEFAULT 0 NOT NULL,
+ member_check boolean DEFAULT false NOT NULL,
+ prevent_secrets boolean DEFAULT false NOT NULL,
+ commit_committer_name_check boolean DEFAULT false NOT NULL,
+ deny_delete_tag boolean,
+ reject_unsigned_commits boolean,
+ commit_committer_check boolean,
+ reject_non_dco_commits boolean,
+ commit_message_regex text,
+ branch_name_regex text,
+ commit_message_negative_regex text,
+ author_email_regex text,
+ file_name_regex text,
+ CONSTRAINT check_0bba2c16da CHECK ((char_length(commit_message_negative_regex) <= 2047)),
+ CONSTRAINT check_41c1a11ab8 CHECK ((char_length(file_name_regex) <= 511)),
+ CONSTRAINT check_6f0da85c6c CHECK ((char_length(commit_message_regex) <= 511)),
+ CONSTRAINT check_710cf4213a CHECK ((char_length(author_email_regex) <= 511)),
+ CONSTRAINT check_b02376d0ad CHECK ((char_length(branch_name_regex) <= 511))
+);
+
+CREATE SEQUENCE group_push_rules_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE group_push_rules_id_seq OWNED BY group_push_rules.id;
+
CREATE TABLE group_repository_storage_moves (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -27156,6 +27192,8 @@ ALTER TABLE ONLY group_group_links ALTER COLUMN id SET DEFAULT nextval('group_gr
ALTER TABLE ONLY group_import_states ALTER COLUMN group_id SET DEFAULT nextval('group_import_states_group_id_seq'::regclass);
+ALTER TABLE ONLY group_push_rules ALTER COLUMN id SET DEFAULT nextval('group_push_rules_id_seq'::regclass);
+
ALTER TABLE ONLY group_repository_storage_moves ALTER COLUMN id SET DEFAULT nextval('group_repository_storage_moves_id_seq'::regclass);
ALTER TABLE ONLY group_saved_replies ALTER COLUMN id SET DEFAULT nextval('group_saved_replies_id_seq'::regclass);
@@ -29650,6 +29688,9 @@ ALTER TABLE ONLY group_import_states
ALTER TABLE ONLY group_merge_request_approval_settings
ADD CONSTRAINT group_merge_request_approval_settings_pkey PRIMARY KEY (group_id);
+ALTER TABLE ONLY group_push_rules
+ ADD CONSTRAINT group_push_rules_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY group_repository_storage_moves
ADD CONSTRAINT group_repository_storage_moves_pkey PRIMARY KEY (id);
@@ -33537,6 +33578,8 @@ CREATE INDEX index_ai_conversation_messages_on_organization_id ON ai_conversatio
CREATE INDEX index_ai_conversation_messages_on_thread_id_and_created_at ON ai_conversation_messages USING btree (thread_id, created_at);
+CREATE INDEX index_ai_conversation_threads_on_created_at ON ai_conversation_threads USING btree (created_at);
+
CREATE INDEX index_ai_conversation_threads_on_last_updated_at ON ai_conversation_threads USING btree (last_updated_at);
CREATE INDEX index_ai_conversation_threads_on_organization_id ON ai_conversation_threads USING btree (organization_id);
@@ -35183,6 +35226,8 @@ CREATE INDEX index_group_import_states_on_user_id ON group_import_states USING b
CREATE UNIQUE INDEX index_group_microsoft_applications_on_temp_source_id ON system_access_group_microsoft_applications USING btree (temp_source_id);
+CREATE INDEX index_group_push_rules_on_group_id ON group_push_rules USING btree (group_id);
+
CREATE INDEX index_group_repository_storage_moves_on_group_id ON group_repository_storage_moves USING btree (group_id);
CREATE INDEX index_group_saved_replies_on_group_id ON group_saved_replies USING btree (group_id);
@@ -44048,6 +44093,9 @@ ALTER TABLE ONLY dast_profiles
ALTER TABLE ONLY group_custom_attributes
ADD CONSTRAINT fk_rails_246e0db83a FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY group_push_rules
+ ADD CONSTRAINT fk_rails_2515e57aa7 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY incident_management_oncall_rotations
ADD CONSTRAINT fk_rails_256e0bc604 FOREIGN KEY (oncall_schedule_id) REFERENCES incident_management_oncall_schedules(id) ON DELETE CASCADE;
diff --git a/doc/api/openapi/openapi_v2.yaml b/doc/api/openapi/openapi_v2.yaml
index 9d2cc987ba1..82f5308464a 100644
--- a/doc/api/openapi/openapi_v2.yaml
+++ b/doc/api/openapi/openapi_v2.yaml
@@ -46745,9 +46745,6 @@ definitions:
type: boolean
ci_pipeline_variables_minimum_override_role:
type: string
- runners_token:
- type: string
- example: b8547b1dc37721d05889db52fa2f02
runner_token_expiration_interval:
type: integer
format: int32
@@ -46768,6 +46765,9 @@ definitions:
example: continuous
ci_push_repository_for_job_token_allowed:
type: boolean
+ runners_token:
+ type: string
+ example: b8547b1dc37721d05889db52fa2f02
ci_config_path:
type: string
example: ''
@@ -62326,9 +62326,6 @@ definitions:
type: boolean
ci_pipeline_variables_minimum_override_role:
type: string
- runners_token:
- type: string
- example: b8547b1dc37721d05889db52fa2f02
runner_token_expiration_interval:
type: integer
format: int32
@@ -62349,6 +62346,9 @@ definitions:
example: continuous
ci_push_repository_for_job_token_allowed:
type: boolean
+ runners_token:
+ type: string
+ example: b8547b1dc37721d05889db52fa2f02
ci_config_path:
type: string
example: ''
diff --git a/doc/development/cells/application_settings_analysis.md b/doc/development/cells/application_settings_analysis.md
index be15d5a8cd1..ac11a0d8b8e 100644
--- a/doc/development/cells/application_settings_analysis.md
+++ b/doc/development/cells/application_settings_analysis.md
@@ -14,12 +14,12 @@ title: Application Settings analysis
## Statistics
-- Number of attributes: 493
+- Number of attributes: 495
- Number of encrypted attributes: 41 (8.0%)
- Number of attributes documented: 298 (60.0%)
- Number of attributes on GitLab.com different from the defaults: 222 (45.0%)
-- Number of attributes with `clusterwide` set: 493 (100.0%)
-- Number of attributes with `clusterwide: true` set: 125 (25.0%)
+- Number of attributes with `clusterwide` set: 495 (100.0%)
+- Number of attributes with `clusterwide: true` set: 126 (25.0%)
## Individual columns
@@ -147,6 +147,7 @@ title: Application Settings analysis
| `domain_denylist` | `false` | `text` | `array of strings` | `false` | `null` | `true` | `true`| `true` |
| `domain_denylist_enabled` | `false` | `boolean` | `boolean` | `false` | `false` | `true` | `true`| `true` |
| `dsa_key_restriction` | `false` | `integer` | `integer` | `true` | `'-1'::integer` | `false` | `false`| `true` |
+| `duo_chat` | `false` | `jsonb` | `` | `true` | `'{}'::jsonb` | `true` | `true`| `false` |
| `duo_features_enabled` | `false` | `boolean` | `boolean` | `true` | `true` | `false` | `false`| `true` |
| `duo_workflow` | `false` | `jsonb` | `` | `false` | `'{}'::jsonb` | `true` | `true`| `false` |
| `ecdsa_key_restriction` | `false` | `integer` | `integer` | `true` | `0` | `false` | `false`| `true` |
diff --git a/doc/integration/advanced_search/elasticsearch.md b/doc/integration/advanced_search/elasticsearch.md
index a6da97b8bd8..ea2dcd06512 100644
--- a/doc/integration/advanced_search/elasticsearch.md
+++ b/doc/integration/advanced_search/elasticsearch.md
@@ -596,6 +596,7 @@ only project records are indexed and no associated data can be searched.
- Global search for limited indexing [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41041) in GitLab 13.4 [with a flag](../../administration/feature_flags.md) named `advanced_global_search_for_limited_indexing`. Disabled by default.
- [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/244276) in GitLab 14.2.
+- Global search for limited indexing [generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/186727) in GitLab 17.11 as a UI option, instead of the `advanced_global_search_for_limited_indexing` flag.
{{< /history >}}
@@ -609,12 +610,15 @@ When you index only some namespaces:
For example, if you index two separate groups, you must run separate code searches on each group individually.
-{{< alert type="warning" >}}
+To enable global search for limited indexing:
-If you've already indexed your instance, you must [reindex the instance](#index-the-instance)
-to delete all existing data for filtering to work correctly.
-
-{{< /alert >}}
+1. On the left sidebar, at the bottom, select **Admin**.
+1. Select **Settings > Search**.
+1. Expand **Advanced search**
+1. Select **Enable global search for limited indexing**.
+1. Select **Save changes**.
+1. If you've already indexed your instance, you must [reindex the instance](#index-the-instance).
+ This deletes existing search data, to enable filtering to work correctly.
## Enable custom language analyzers
diff --git a/doc/subscriptions/gitlab_com/compute_minutes.md b/doc/subscriptions/gitlab_com/compute_minutes.md
index 2bf3f1910f1..85f8bcbc485 100644
--- a/doc/subscriptions/gitlab_com/compute_minutes.md
+++ b/doc/subscriptions/gitlab_com/compute_minutes.md
@@ -18,11 +18,12 @@ pricing for additional compute minutes on the [GitLab Pricing page](https://abou
Additional compute minutes:
-- Are valid for 12 months from date of purchase or until all compute minutes are consumed,
- whichever comes first. Expiry of compute minutes is not enforced.
- Are used only after the monthly quota included in your subscription runs out.
- Are [carried over to the next month](#monthly-rollover-of-purchased-compute-minutes),
if any remain at the end of the month.
+- Are valid for 12 months from date of purchase if not consumed earlier.
+- Expiry of compute minutes is not yet enforced, which allows their use even after the expiry date.
+ However, GitLab does not guarantee that compute minutes will remain valid after the expiry date.
- Bought on a trial subscription are available after the trial ends or upgrading to a paid plan.
- Remain available when you change subscription tiers, including changes between paid tiers or to the Free tier.
diff --git a/doc/user/compliance/compliance_frameworks.md b/doc/user/compliance/compliance_frameworks.md
index 7725bb6843a..b4954d4d9fe 100644
--- a/doc/user/compliance/compliance_frameworks.md
+++ b/doc/user/compliance/compliance_frameworks.md
@@ -180,31 +180,69 @@ Each control includes logic that GitLab uses during scheduled or triggered scans
The following controls are available to use in framework requirements:
-- **SAST running**
-- **At least two approvals**
-- **Author approved merge request**
-- **Committers approved merge request**
-- **Internal visibility is forbidden**
-- **Default branch protected**
-- **Auth SSO enabled**
-- **Secret detection running**
-- **Dependency scanning running**
-- **Container scanning running**
-- **License compliance running**
-- **DAST running**
-- **API security running**
-- **Fuzz testing running**
-- **Code quality running**
-- **IaC scanning running**
-- **Code changes requires code owners**
-- **Reset approvals on push**
-- **Status checks required**
-- **Require branch up to date**
-- **Resolve discussions required**
-- **Require linear history**
-- **Restrict push/merge access**
-- **Force push disabled**
-- **Terraform enabled**
+### GitLab Compliance Controls
+
+This table documents all available controls that can be used in GitLab compliance frameworks. Controls are checks against the configuration or behavior of projects that are assigned to a compliance framework.
+
+| Name | ID | Description | Documentation Link |
+|------|----|-----------|--------------------|
+| SAST running | `scanner_sast_running` | Ensures Static Application Security Testing (SAST) is configured and running in the project pipelines. | [SAST Configuration](../../user/application_security/sast/_index.md) |
+| At least two approvals | `minimum_approvals_required_2` | Ensures that merge requests require at least two approvals before merging. | [Merge request approvals](../../user/project/merge_requests/approvals/_index.md) |
+| Author approved merge request | `merge_request_prevent_author_approval` | Ensures that the author of a merge request cannot approve their own changes. | [Merge request approvals](../../user/project/merge_requests/approvals/_index.md) |
+| Committers approved merge request | `merge_request_prevent_committers_approval` | Ensures that users who have committed to a merge request cannot approve it. | [Merge Request Approvals](../../user/project/merge_requests/approvals/_index.md) |
+| Internal visibility is forbidden | `project_visibility_not_internal` | Ensures projects are not set to internal visibility. | [Project Visibility](../../user/public_access.md) |
+| Default branch protected | `default_branch_protected` | Ensures the default branch has protection rules enabled. | [Protected Branches](../../user/project/repository/branches/protected.md) |
+| Auth SSO enabled | `auth_sso_enabled` | Ensures Single Sign-On (SSO) authentication is enabled for the project. | [SSO for GitLab.com Groups](../../user/group/saml_sso/_index.md) |
+| Secret detection running | `scanner_secret_detection_running` | Ensures secret detection scanning is configured and running in the project pipelines. | [Secret Detection](../../user/application_security/secret_detection/_index.md) |
+| Dependency scanning running | `scanner_dep_scanning_running` | Ensures dependency scanning is configured and running in the project pipelines. | [Dependency Scanning](../../user/application_security/dependency_scanning/_index.md) |
+| Container scanning running | `scanner_container_scanning_running` | Ensures container scanning is configured and running in the project pipelines. | [Container Scanning](../../user/application_security/container_scanning/_index.md) |
+| License compliance running | `scanner_license_compliance_running` | Ensures license compliance scanning is configured and running in the project pipelines. | [License Compliance](../../user/compliance/license_approval_policies.md) |
+| DAST running | `scanner_dast_running` | Ensures Dynamic Application Security Testing (DAST) is configured and running in the project pipelines. | [DAST Configuration](../../user/application_security/dast/_index.md) |
+| API security running | `scanner_api_security_running` | Ensures API security scanning is configured and running in the project pipelines. | [API Security](../../user/application_security/api_security/_index.md) |
+| Fuzz testing running | `scanner_fuzz_testing_running` | Ensures fuzz testing is configured and running in the project pipelines. | [Fuzz Testing](../../user/application_security/coverage_fuzzing/_index.md) |
+| Code quality running | `scanner_code_quality_running` | Ensures code quality scanning is configured and running in the project pipelines. | [Code Quality](../../ci/testing/code_quality.md) |
+| IaC scanning running | `scanner_iac_running` | Ensures Infrastructure as Code (IaC) scanning is configured and running in the project pipelines. | [IaC Security](../../user/application_security/iac_scanning/_index.md) |
+| Code changes requires code owners | `code_changes_requires_code_owners` | Ensures code changes require approval from code owners. | [Code Owners](../../user/project/codeowners/_index.md) |
+| Reset approvals on push | `reset_approvals_on_push` | Ensures approvals are reset when new commits are pushed to the merge request. | [Reset Approvals on Push](../../user/project/merge_requests/approvals/settings.md) |
+| Status checks required | `status_checks_required` | Ensures status checks must pass before merging is allowed. | [Status Checks](../../user/project/merge_requests/status_checks.md) |
+| Require branch up to date | `require_branch_up_to_date` | Ensures the source branch is up to date with the target branch before merging. | [Merge Requests](../../user/project/merge_requests/methods/_index.md) |
+| Resolve discussions required | `resolve_discussions_required` | Ensures all discussions must be resolved before merging is allowed. | [Resolve Discussions](../../user/discussions/_index.md) |
+| Require linear history | `require_linear_history` | Ensures a linear commit history by forbidding merge commits. | [Merge Request Fast-forward Merges](../../user/project/merge_requests/methods/_index.md#fast-forward-merge) |
+| Restrict push/merge access | `restrict_push_merge_access` | Restricts who can push to or merge into protected branches. | [Protected Branches](../../user/project/repository/branches/protected.md) |
+| Force push disabled | `force_push_disabled` | Prevents force pushing to repositories. | [Protected Branches](../../user/project/repository/branches/protected.md) |
+| Terraform enabled | `terraform_enabled` | Ensures Terraform integration is enabled for the project. | [Terraform in GitLab](../../administration/terraform_state.md) |
+| Version control enabled | `version_control_enabled` | Ensures version control functionality is enabled for the project. | [Git in GitLab](../../topics/git/_index.md) |
+| Issue tracking enabled | `issue_tracking_enabled` | Ensures issue tracking functionality is enabled for the project. | [GitLab Issues](../../user/project/issues/_index.md) |
+| Stale branch cleanup enabled | `stale_branch_cleanup_enabled` | Ensures automatic cleanup of stale branches is enabled. | [Deleting Branches](../../user/project/repository/branches/_index.md) |
+| Branch deletion disabled | `branch_deletion_disabled` | Prevents deletion of branches. | [Protected Branches](../../user/project/repository/branches/protected.md) |
+| Review and archive stale repositories | `review_and_archive_stale_repos` | Ensures stale repositories are reviewed and archived. | [Archiving Projects](../../user/project/settings/_index.md) |
+| Review and remove inactive users | `review_and_remove_inactive_users` | Ensures inactive users are reviewed and removed. | [Managing Users](../../administration/admin_area.md) |
+| Minimum number of admins | `minimum_number_of_admins` | Ensures a minimum number of administrators are assigned to the project. | [Project Members](../../user/project/members/_index.md) |
+| Require MFA for contributors | `require_mfa_for_contributors` | Ensures contributors have Multi-Factor Authentication enabled. | [MFA for Contributors](../../user/profile/account/two_factor_authentication.md) |
+| Require MFA at org level | `require_mfa_at_org_level` | Ensures Multi-Factor Authentication is required at the organization level. | [Group-level MFA Enforcement](../../user/profile/account/two_factor_authentication.md) |
+| Ensure 2 admins per repository | `ensure_2_admins_per_repo` | Ensures at least two administrators are assigned to each repository. | [Project Members](../../user/project/members/_index.md) |
+| Strict permission for repository | `strict_permissions_for_repo` | Ensures strict permissions are set for repository access. | [Project Members Permissions](../../user/permissions.md) |
+| Secure webhooks | `secure_webhooks` | Ensures webhooks are securely configured. | [Webhooks](../../user/project/integrations/webhooks.md) |
+| Restricted build access | `restricted_build_access` | Restricts access to build artifacts and pipeline outputs. | [Pipeline Security](../../ci/pipelines/settings.md) |
+| GitLab license level ultimate | `gitlab_license_level_ultimate` | Ensures the GitLab instance is using an Ultimate license level. | [GitLab Licensing](https://about.gitlab.com/pricing/feature-comparison/) |
+| Status page configured | `status_page_configured` | Ensures a status page is configured for the project. | [Status Page](../../operations/incident_management/status_page.md) |
+| Has valid CI config | `has_valid_ci_config` | Ensures the project has a valid CI/CD configuration. | [CI/CD Pipeline Configuration](../../ci/yaml/_index.md) |
+| Error tracking enabled | `error_tracking_enabled` | Ensures error tracking is enabled for the project. | [Error Tracking](../../operations/error_tracking.md) |
+| Default branch users can push | `default_branch_users_can_push` | Controls whether users can push directly to the default branch. | [Protected Branches](../../user/project/repository/branches/protected.md) |
+| Default branch protected from direct push | `default_branch_protected_from_direct_push` | Prevents direct pushes to the default branch. | [Protected Branches](../../user/project/repository/branches/protected.md) |
+| Push protection enabled | `push_protection_enabled` | Ensures push protection is enabled for sensitive files. | [Push Rules](../../user/project/repository/push_rules.md) |
+| Project marked for deletion | `project_marked_for_deletion` | Checks if project is marked for deletion (false is compliant). | [Project Settings](../../user/project/settings/_index.md) |
+| Project archived | `project_archived` | Checks if project is archived (typically false is compliant). | [Archiving Projects](../../user/project/settings/_index.md) |
+| Default branch users can merge | `default_branch_users_can_merge` | Controls whether users can merge changes to the default branch. | [Protected Branches](../../user/project/repository/branches/protected.md) |
+| Merge request commit reset approvals | `merge_request_commit_reset_approvals` | Ensures new commits to merge requests reset approvals. | [Reset Approvals on Push](../../user/project/merge_requests/approvals/settings.md) |
+| Project visibility not public | `project_visibility_not_public` | Ensures projects are not set to public visibility. | [Project Visibility](../../user/public_access.md) |
+| Package hunter no findings untriaged | `package_hunter_no_findings_untriaged` | Ensures all package hunter findings are triaged. | [Package Hunter](../../user/application_security/triage/_index.md) |
+| Project pipelines not public | `project_pipelines_not_public` | Ensures project pipelines are not publicly visible. | [Pipeline Settings](../../ci/pipelines/settings.md) |
+| Vulnerabilities SLO days over threshold | `vulnerabilities_slo_days_over_threshold` | Ensures vulnerabilities are addressed within SLO thresholds. | [Vulnerability Management](../../user/application_security/vulnerabilities/_index.md) |
+| Merge requests approval rules prevent editing | `merge_requests_approval_rules_prevent_editing` | Prevents editing of merge request approval rules. | [Merge Request Approvals Settings](../../user/project/merge_requests/approvals/settings.md) |
+| Project user defined variables restricted to maintainers | `project_user_defined_variables_restricted_to_maintainers` | Restricts creation of project variables to maintainers only. | [Project CI/CD Variables](../../ci/variables/_index.md) |
+| Merge requests require code owner approval | `merge_requests_require_code_owner_approval` | Ensures merge requests require approval from code owners. | [Code Owners](../../user/project/codeowners/_index.md) |
+| CI/CD job token scope enabled | `cicd_job_token_scope_enabled` | Ensures CI/CD job token scope restrictions are enabled. | [CI/CD Job Token](../../ci/jobs/ci_job_token.md) |
#### External controls
@@ -230,8 +268,8 @@ To add an external control when creating or editing a framework:
1. Select **New framework** or edit an existing one.
1. In the **Requirements** section, select **New requirement**.
1. Select **Add an external control**.
-1. In the feilds edit **External URL** and **HMAC shared secret**.
-1. Select **Save changes to the framework** to save the requirment.
+1. In the fields edit **External URL** and **`HMAC` shared secret**.
+1. Select **Save changes to the framework** to save the requirement.
#### External control lifecycle
@@ -265,10 +303,10 @@ To add a requirement when creating or editing a framework:
1. On the page, select the **Frameworks** tab.
1. Select **New framework** or edit an existing one.
1. In the **Requirements** section, select **New requirement**.
-1. In the popup add **Name** and **Description**.
+1. In the dialog add **Name** and **Description**.
1. Select **Add a GitLab control** to add more controls.
-1. In the control dropdown search and select a control.
-1. Select **Save changes to the framework** to save the requirment.
+1. In the control dropdown list search and select a control.
+1. Select **Save changes to the framework** to save the requirement.
### Edit requirements
@@ -279,8 +317,8 @@ To edit a requirement when creating or editing a framework:
1. On the page, select the **Frameworks** tab.
1. Select **New framework** or edit an existing one.
1. In the **Requirements** section, select **Action** > **Edit**.
-1. In the popup edit **Name** and **Description**.
+1. In the dialog edit **Name** and **Description**.
1. Select **Add a GitLab control** to add more controls.
-1. In the control dropdown search and select a control.
+1. In the control dropdown list search and select a control.
1. Select {{< icon name="remove" >}} to remove a control.
-1. Select **Save changes to the framework** to save the requirment.
+1. Select **Save changes to the framework** to save the requirement.
diff --git a/doc/user/project/repository/code_suggestions/_index.md b/doc/user/project/repository/code_suggestions/_index.md
index dd7a7cad6be..73015ea4fd9 100644
--- a/doc/user/project/repository/code_suggestions/_index.md
+++ b/doc/user/project/repository/code_suggestions/_index.md
@@ -11,8 +11,7 @@ title: Code Suggestions
- Tier: Premium, Ultimate
- Add-on: GitLab Duo Pro or Enterprise, GitLab Duo with Amazon Q
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
-- LLMs: For code completion, Vertex AI-hosted [`Codestral`](https://console.cloud.google.com/vertex-ai/publishers/mistralai/model-garden/codestral-2501) and Fireworks AI-hosted [`Qwen2.5 7B`](https://fireworks.ai/models/fireworks/qwen2p5-coder-7b). For code generation, Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet).
-- To opt out of Qwen2.5 7B for a group, the feature flag `code_completion_model_opt_out_from_fireworks_qwen` is available.
+- LLMs: For code completion, Vertex AI-hosted [`Codestral`](https://console.cloud.google.com/vertex-ai/publishers/mistralai/model-garden/codestral-2501) and Fireworks AI-hosted [`Codestral`](https://mistral.ai/news/codestral-2501). For code generation, Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet).
- LLM for Amazon Q: Amazon Q Developer
{{< /details >}}
@@ -26,6 +25,7 @@ title: Code Suggestions
- [Changed](https://gitlab.com/gitlab-org/fulfillment/meta/-/issues/2031) to require the GitLab Duo Pro add-on on February 15, 2024. Previously, this feature was included with Premium and Ultimate subscriptions.
- [Changed](https://gitlab.com/gitlab-org/fulfillment/meta/-/issues/2031) to require the GitLab Duo Pro or GitLab Duo Enterprise add-on for all supported GitLab versions starting October 17, 2024.
- [Introduced support for Fireworks AI-hosted Qwen2.5 code completion model](https://gitlab.com/groups/gitlab-org/-/epics/15850) in GitLab 17.6, with a flag named `fireworks_qwen_code_completion`.
+- Removed support for Qwen2.5 code completion model
{{< /history >}}
diff --git a/doc/user/search/_index.md b/doc/user/search/_index.md
index fc164d7138b..faff483619b 100644
--- a/doc/user/search/_index.md
+++ b/doc/user/search/_index.md
@@ -50,6 +50,14 @@ For more information, see [issue 477333](https://gitlab.com/gitlab-org/gitlab/-/
{{< /details >}}
+{{< history >}}
+
+- Restricting global search to authenticated users [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41041) in GitLab 13.4 [with a flag](../../administration/feature_flags.md) named `block_anonymous_global_searches`. Disabled by default.
+- Enabling or disabling anonymous searches [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/138975) in GitLab 16.7 [with a flag](../../administration/feature_flags.md) named `allow_anonymous_searches`. Enabled by default.
+- Enabling or disabling anonymous searches [generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/186727) in GitLab 17.11 as a UI option, instead of the `block_anonymous_global_searches` flag.
+
+{{< /history >}}
+
Prerequisites:
- You must have administrator access to the instance.
@@ -59,12 +67,16 @@ By default, requests to `/search` and global search are available for unauthenti
To restrict `/search` to authenticated users only, do one of the following:
- [Restrict public visibility](../../administration/settings/visibility_and_access_controls.md#restrict-visibility-levels)
- ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171368) in GitLab 17.6).
-- Disable the `ops` feature flag `allow_anonymous_searches`
- ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/138975) in GitLab 16.7).
+ of the project or group.
+- Disable the [feature flag](../../administration/feature_flags.md) `allow_anonymous_searches`.
-To restrict global search to authenticated users only,
-enable the `ops` feature flag `block_anonymous_global_searches`.
+To restrict global search to authenticated users only:
+
+1. On the left sidebar, at the bottom, select **Admin**.
+1. Select **Settings > Search**.
+1. Expand **Global search**
+1. Select **Enable blocking of anonymous global search requests**.
+1. Select **Save changes**.
## Disable global search scopes
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 68905a40984..562f02fbeed 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -126,7 +126,6 @@ module API
expose :keep_latest_artifacts_available?, as: :keep_latest_artifact, documentation: { type: 'boolean' }
expose :restrict_user_defined_variables, documentation: { type: 'boolean' }
expose :ci_pipeline_variables_minimum_override_role, documentation: { type: 'string' }
- expose :runners_token, documentation: { type: 'string', example: 'b8547b1dc37721d05889db52fa2f02' }
expose :runner_token_expiration_interval, documentation: { type: 'integer', example: 3600 }
expose :group_runners_enabled, documentation: { type: 'boolean' }
expose :auto_cancel_pending_pipelines, documentation: { type: 'string', example: 'enabled' }
@@ -138,6 +137,11 @@ module API
expose :ci_push_repository_for_job_token_allowed, documentation: { type: 'boolean' }
end
+ with_options if: ->(_, _) { Ability.allowed?(options[:current_user], :read_runners_registration_token, project) } do
+ # Runner token settings
+ expose :runners_token, documentation: { type: 'string', example: 'b8547b1dc37721d05889db52fa2f02' }
+ end
+
expose :ci_config_path, documentation: { type: 'string', example: '' }, if: ->(project, options) { Ability.allowed?(options[:current_user], :read_code, project) }
expose :public_builds, as: :public_jobs, documentation: { type: 'boolean' }
diff --git a/lib/bulk_imports/ndjson_pipeline.rb b/lib/bulk_imports/ndjson_pipeline.rb
index 122ad48db1f..bb075834486 100644
--- a/lib/bulk_imports/ndjson_pipeline.rb
+++ b/lib/bulk_imports/ndjson_pipeline.rb
@@ -199,6 +199,10 @@ module BulkImports
# as they lack a foreign key constraint.
next if IGNORE_PLACEHOLDER_USER_CREATION[relation_key]&.include?(reference)
+ # Skip creating placeholder users for imported ghost users.
+ # Ghost user contributions are assigned directly to the destination ghost user
+ next if context.source_ghost_user_id.to_s == relation_hash[reference].to_s
+
source_user_mapper.find_or_create_source_user(
source_name: nil,
source_username: nil,
diff --git a/lib/bulk_imports/pipeline/context.rb b/lib/bulk_imports/pipeline/context.rb
index a4b2aaf9128..c309bfe8203 100644
--- a/lib/bulk_imports/pipeline/context.rb
+++ b/lib/bulk_imports/pipeline/context.rb
@@ -46,6 +46,10 @@ module BulkImports
@configuration ||= bulk_import.configuration
end
+ def source_ghost_user_id
+ @source_ghost_user_id ||= BulkImports::SourceInternalUserFinder.new(configuration).cached_ghost_user_id
+ end
+
def source_user_mapper
@source_user_mapper ||= Gitlab::Import::SourceUserMapper.new(
namespace: portable.root_ancestor,
diff --git a/lib/bulk_imports/source_internal_user_finder.rb b/lib/bulk_imports/source_internal_user_finder.rb
new file mode 100644
index 00000000000..2ca67b4633e
--- /dev/null
+++ b/lib/bulk_imports/source_internal_user_finder.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module BulkImports # rubocop:disable Gitlab/BoundedContexts -- legacy use
+ class SourceInternalUserFinder
+ include Gitlab::Utils::StrongMemoize
+
+ GHOST_USER_CACHE_KEY = 'bulk_imports/ghost_user_id/%{bulk_import_id}'
+ MAX_RETRIES = 3
+
+ # @param [BulkImports::Configuration] configuration
+ def initialize(configuration)
+ @configuration = configuration
+ end
+
+ # @return [Hash, nil]
+ def fetch_ghost_user
+ attempt = 0
+
+ begin
+ attempt += 1
+ query = <<~GRAPHQL
+ {
+ users(usernames: ["ghost", "ghost1", "ghost2", "ghost3", "ghost4", "ghost5", "ghost6"], humans: false) {
+ nodes {
+ id
+ username
+ type
+ }
+ }
+ }
+ GRAPHQL
+
+ response = client.execute(query: query)
+ response = response.dig('data', 'users', 'nodes') || []
+ response.find { |user| user["type"] == 'GHOST' }
+ rescue StandardError => e
+ if attempt < MAX_RETRIES
+ delay = 2**attempt # Exponential backoff (2, 4, 8...)
+ sleep(delay)
+ retry
+ end
+
+ Gitlab::ErrorTracking.track_exception(e,
+ { message: "Failed to fetch ghost user after #{MAX_RETRIES} attempts",
+ bulk_import_id: configuration.bulk_import_id }
+ )
+ nil
+ end
+ end
+
+ # @return [String, nil]
+ def set_ghost_user_id
+ return if Gitlab::Cache::Import::Caching.read(cache_key).present?
+
+ ghost_user = fetch_ghost_user
+ return unless ghost_user # since fetch_ghost_user returns nil if it's not in the API response
+
+ model_id = GlobalID.parse(ghost_user['id']).model_id
+
+ Gitlab::Cache::Import::Caching.write(cache_key, model_id)
+ rescue StandardError => e
+ Gitlab::ErrorTracking.track_exception(e,
+ { message: "Failed to set source ghost user ID", bulk_import_id: configuration.bulk_import_id }
+ )
+ nil
+ end
+
+ # Returns the cached ID of the ghost user from the source instance, if it exists.
+ #
+ # @return [String, nil]
+ def cached_ghost_user_id
+ Gitlab::Cache::Import::Caching.read(cache_key)
+ end
+
+ private
+
+ attr_reader :configuration
+
+ def client
+ @client ||= BulkImports::Clients::Graphql.new(
+ url: configuration.url,
+ token: configuration.access_token
+ )
+ end
+
+ def cache_key
+ format(GHOST_USER_CACHE_KEY, bulk_import_id: configuration.bulk_import_id)
+ end
+ strong_memoize_attr :cache_key
+ end
+end
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index 89cc52ef447..b878e886308 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -64,6 +64,11 @@ module Gitlab
.for_issue
.for_action(%i[created closed])
+ design_events =
+ project_events_created_between(start_time, end_time, features: :issues)
+ .for_design
+ .for_action(%i[created updated destroyed])
+
mr_events =
project_events_created_between(start_time, end_time, features: :merge_requests)
.for_merge_request
@@ -73,7 +78,7 @@ module Gitlab
project_events_created_between(start_time, end_time, features: %i[issues merge_requests])
.for_action(:commented)
- [repo_events, issue_events, mr_events, project_note_events]
+ [repo_events, issue_events, design_events, mr_events, project_note_events]
end
def can_read_cross_project?
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index 4f6018b63ee..b80d12fa178 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -455,7 +455,11 @@ module Gitlab
end
def diffable_text?
- !too_large? && diffable? && text?
+ !too_large? && diffable? && text? && !whitespace_only?
+ end
+
+ def whitespace_only?
+ !collapsed? && diff_lines_for_serializer.nil? && (added_lines != 0 || removed_lines != 0)
end
private
diff --git a/lib/gitlab/tracking/event_eligibility_checker.rb b/lib/gitlab/tracking/event_eligibility_checker.rb
index f0aeca3716c..16019bfbeee 100644
--- a/lib/gitlab/tracking/event_eligibility_checker.rb
+++ b/lib/gitlab/tracking/event_eligibility_checker.rb
@@ -22,7 +22,7 @@ module Gitlab
end
def send_usage_data?
- Gitlab::CurrentSettings.product_usage_data_enabled?
+ Gitlab::CurrentSettings.gitlab_product_usage_data_enabled?
end
def duo_event?(event_name)
diff --git a/lib/import/bulk_imports/source_users_mapper.rb b/lib/import/bulk_imports/source_users_mapper.rb
index 43fb0cc40e3..6de57cd4475 100644
--- a/lib/import/bulk_imports/source_users_mapper.rb
+++ b/lib/import/bulk_imports/source_users_mapper.rb
@@ -17,13 +17,23 @@ module Import
# returned. In this case SourceUsersMapper#map returns a class that responds
# to [].
class MockedHash
- def initialize(source_user_mapper)
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(source_user_mapper, source_ghost_user_id)
@source_user_mapper = source_user_mapper
+ @source_ghost_user_id = source_ghost_user_id
end
def [](user_identifier)
+ return ghost_user_id if @source_ghost_user_id.to_s == user_identifier.to_s
+
@source_user_mapper.find_source_user(user_identifier)&.mapped_user_id
end
+
+ def ghost_user_id
+ Users::Internal.ghost.id
+ end
+ strong_memoize_attr :ghost_user_id
end
def initialize(context:)
@@ -31,7 +41,7 @@ module Import
end
def map
- @map ||= MockedHash.new(source_user_mapper)
+ @map ||= MockedHash.new(source_user_mapper, source_ghost_user_id)
end
def include?(user_identifier)
@@ -42,7 +52,7 @@ module Import
attr_reader :context
- delegate :source_user_mapper, to: :context
+ delegate :source_user_mapper, :source_ghost_user_id, to: :context
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 68471874193..ab07b53aa12 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2486,6 +2486,9 @@ msgstr ""
msgid "API key"
msgstr ""
+msgid "API security"
+msgstr ""
+
msgid "API token of the Packagist server."
msgstr ""
@@ -4515,6 +4518,9 @@ msgstr ""
msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
+msgid "AdminSettings|Enable event tracking"
+msgstr ""
+
msgid "AdminSettings|Enable instance runners for new projects"
msgstr ""
@@ -4530,9 +4536,6 @@ msgstr ""
msgid "AdminSettings|Enable product analytics"
msgstr ""
-msgid "AdminSettings|Enable product usage tracking"
-msgstr ""
-
msgid "AdminSettings|Enable smartcn custom analyzer: Indexing"
msgstr ""
@@ -4737,6 +4740,12 @@ msgstr ""
msgid "AdminSettings|Send email to maintainers after project is inactive for"
msgstr ""
+msgid "AdminSettings|Send event data to GitLab."
+msgstr ""
+
+msgid "AdminSettings|Send event data to your own Snowplow collector."
+msgstr ""
+
msgid "AdminSettings|Send warning email"
msgstr ""
@@ -8211,6 +8220,9 @@ msgstr ""
msgid "Archived in this version"
msgstr ""
+msgid "Archiving projects"
+msgstr ""
+
msgid "Archiving the project makes it entirely read-only. It is hidden from the dashboard and doesn't display in searches. %{strong_start}The repository cannot be committed to, and no issues, comments, or other entities can be created.%{strong_end} %{link_start}Learn more.%{link_end}"
msgstr ""
@@ -9462,9 +9474,6 @@ msgstr ""
msgid "Best Regards,"
msgstr ""
-msgid "Best practices"
-msgstr ""
-
msgid "Beta"
msgstr ""
@@ -10458,9 +10467,6 @@ msgstr ""
msgid "Branch rules"
msgstr ""
-msgid "Branch settings"
-msgstr ""
-
msgid "Branch target"
msgstr ""
@@ -11439,12 +11445,18 @@ msgstr ""
msgid "CI/CD configuration file"
msgstr ""
+msgid "CI/CD job token"
+msgstr ""
+
msgid "CI/CD job token permissions for '%{projectName}' were successfully updated."
msgstr ""
msgid "CI/CD limits"
msgstr ""
+msgid "CI/CD pipeline configuration"
+msgstr ""
+
msgid "CICDAnalytics|%{percent}%{percentSymbol}"
msgstr ""
@@ -14383,6 +14395,12 @@ msgstr ""
msgid "Code owner approval is required"
msgstr ""
+msgid "Code owners"
+msgstr ""
+
+msgid "Code quality"
+msgstr ""
+
msgid "Code review"
msgstr ""
@@ -15512,9 +15530,6 @@ msgstr ""
msgid "ComplianceFrameworks|To unlink this policy and framework, edit the policy's scope."
msgstr ""
-msgid "ComplianceFrameworks|URL to external system."
-msgstr ""
-
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
msgstr ""
@@ -15739,7 +15754,16 @@ msgstr ""
msgid "ComplianceStandardsAdherence|A rule is configured to require two approvals."
msgstr ""
-msgid "ComplianceStandardsAdherence|API Security testing identifies vulnerabilities specific to your application APIs before they can be exploited"
+msgid "ComplianceStandardsAdherence|Add adequate administrators"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Add multiple administrators"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Adjust permissions to control who can merge changes to the default branch."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Adjust permissions to control who can push directly to the default branch."
msgstr ""
msgid "ComplianceStandardsAdherence|All attached frameworks"
@@ -15751,7 +15775,10 @@ msgstr ""
msgid "ComplianceStandardsAdherence|At least two approvals"
msgstr ""
-msgid "ComplianceStandardsAdherence|Change your project visibility settings to comply with organizational security requirements"
+msgid "ComplianceStandardsAdherence|Change your project visibility settings to comply with organizational security requirements."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Change your project visibility to private or internal as per security requirements."
msgstr ""
msgid "ComplianceStandardsAdherence|Check"
@@ -15760,25 +15787,115 @@ msgstr ""
msgid "ComplianceStandardsAdherence|Checks"
msgstr ""
-msgid "ComplianceStandardsAdherence|Code quality scanning identifies maintainability issues that could lead to increased security risks over time"
+msgid "ComplianceStandardsAdherence|Checks if project is archived (typically false is compliant)."
msgstr ""
-msgid "ComplianceStandardsAdherence|Configure DAST in your CI/CD pipeline to automatically test your application for security issues"
+msgid "ComplianceStandardsAdherence|Checks if project is marked for deletion (false is compliant)."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure DAST in your CI/CD pipeline to automatically test your application for security issues."
msgstr ""
msgid "ComplianceStandardsAdherence|Configure DAST scanning"
msgstr ""
+msgid "ComplianceStandardsAdherence|Configure Infrastructure as Code scanning to detect misconfigurations before deployment."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure SAST scanning"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure access controls to limit who can access build artifacts and outputs."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure access restrictions"
+msgstr ""
+
msgid "ComplianceStandardsAdherence|Configure approval requirements"
msgstr ""
-msgid "ComplianceStandardsAdherence|Configure your project settings to prevent merge request authors from approving their own changes"
+msgid "ComplianceStandardsAdherence|Configure branch protection to prevent accidental or unauthorized branch deletion."
msgstr ""
-msgid "ComplianceStandardsAdherence|Configure your project to require at least two approvals on merge requests to improve code quality and security"
+msgid "ComplianceStandardsAdherence|Configure branch protection to prevent direct pushes to the default branch."
msgstr ""
-msgid "ComplianceStandardsAdherence|Container scanning checks your container images for known vulnerabilities to prevent exploitation in production"
+msgid "ComplianceStandardsAdherence|Configure branch protection to prevent force pushes to repository branches."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure branch update requirements"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure code owners to ensure appropriate review of specific code sections."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure issue tracking to organize and prioritize project work."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure job token scope"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure linear history"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure merge permissions"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure merge requests to require approval from designated code owners."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure merge requests to reset approvals when new commits are added."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure organization settings to require Multi-Factor Authentication for all users."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure pipeline settings to restrict public access to CI/CD pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure project settings to allow only maintainers to create CI/CD variables."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure push permissions"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure push rules to prevent sensitive information from being committed."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure settings to prevent editing of merge request approval rules."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure status checks"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure status page"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure strict permissions"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure your project settings to prevent merge request authors from approving their own changes."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure your project to require all contributors to use Multi-Factor Authentication."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure your project to require all discussions be resolved before merging."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure your project to require at least two approvals on merge requests to improve code quality and security."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Configure your project to reset approvals when new changes are pushed."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Controls whether users can merge changes to the default branch."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Controls whether users can push directly to the default branch."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Create valid CI configuration"
msgstr ""
msgid "ComplianceStandardsAdherence|DAST scan"
@@ -15793,13 +15910,10 @@ msgstr ""
msgid "ComplianceStandardsAdherence|Date since last status change"
msgstr ""
-msgid "ComplianceStandardsAdherence|Default branch protection prevents direct commits and ensures changes are reviewed through merge requests"
+msgid "ComplianceStandardsAdherence|Disable branch deletion"
msgstr ""
-msgid "ComplianceStandardsAdherence|Dependency scanning identifies vulnerable dependencies in your project that could be exploited by attackers"
-msgstr ""
-
-msgid "ComplianceStandardsAdherence|Dynamic Application Security Testing (DAST) identifies runtime vulnerabilities by analyzing your application while it runs"
+msgid "ComplianceStandardsAdherence|Disable force pushing"
msgstr ""
msgid "ComplianceStandardsAdherence|Enable DAST scanner"
@@ -15814,22 +15928,205 @@ msgstr ""
msgid "ComplianceStandardsAdherence|Enable SAST scanner in the project's security configuration to satisfy this requirement."
msgstr ""
+msgid "ComplianceStandardsAdherence|Enable SSO authentication"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enable Terraform integration"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enable approval reset on commit"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enable branch protection"
+msgstr ""
+
msgid "ComplianceStandardsAdherence|Enable code quality scanning"
msgstr ""
-msgid "ComplianceStandardsAdherence|Enable code quality scanning to improve code maintainability and reduce technical debt"
+msgid "ComplianceStandardsAdherence|Enable code quality scanning to improve code maintainability and reduce technical debt."
msgstr ""
msgid "ComplianceStandardsAdherence|Enable dependency scanning"
msgstr ""
-msgid "ComplianceStandardsAdherence|Enable dependency scanning to automatically detect vulnerable libraries in your application"
+msgid "ComplianceStandardsAdherence|Enable dependency scanning to automatically detect vulnerable libraries in your application."
msgstr ""
-msgid "ComplianceStandardsAdherence|Enforcing minimum approval requirements ensures code changes are properly reviewed before merging"
+msgid "ComplianceStandardsAdherence|Enable error tracking"
msgstr ""
-msgid "ComplianceStandardsAdherence|Ensuring that code committers cannot approve their contributed merge requests maintains separation of duties"
+msgid "ComplianceStandardsAdherence|Enable issue tracking"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enable license compliance scanning"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enable organization-wide MFA"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enable push protection"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enable reset approvals on push"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enable stale branch cleanup"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enable version control"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Enforce MFA for contributors"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensure each repository has at least two administrators assigned for redundancy."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensure version control is properly configured for your project."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensure your project has an appropriate number of administrators assigned."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures API security scanning is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures CI/CD job token scope restrictions are enabled."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures Dynamic Application Security Testing (DAST) is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures Infrastructure as Code (IaC) scanning is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures Multi-Factor Authentication is required at the organization level."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures Single Sign-On (SSO) authentication is enabled for the project."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures Static Application Security Testing (SAST) is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures Terraform integration is enabled for the project."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures a linear commit history by forbidding merge commits."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures a minimum number of administrators are assigned to the project."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures a status page is configured for the project."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures all discussions must be resolved before merging is allowed."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures all package hunter findings are triaged."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures approvals are reset when new commits are pushed to the merge request."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures at least two administrators are assigned to each repository."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures automatic cleanup of stale branches is enabled."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures code changes require approval from code owners."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures code quality scanning is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures container scanning is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures contributors have Multi-Factor Authentication enabled."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures dependency scanning is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures error tracking is enabled for the project."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures fuzz testing is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures inactive users are reviewed and removed."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures issue tracking functionality is enabled for the project."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures license compliance scanning is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures merge requests require approval from code owners."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures new commits to merge requests reset approvals."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures project pipelines are not publicly visible."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures projects are not set to internal visibility."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures projects are not set to public visibility."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures push protection is enabled for sensitive files."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures secret detection scanning is configured and running in the project pipelines."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures stale repositories are reviewed and archived."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures status checks must pass before merging is allowed."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures strict permissions are set for repository access."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures that merge requests require at least two approvals before merging."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures that the author of a merge request cannot approve their own changes."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures the GitLab instance is using an Ultimate license level."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures the default branch has protection rules enabled."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures the project has a valid CI/CD configuration."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures the source branch is up to date with the target branch before merging."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures version control functionality is enabled for the project."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures vulnerabilities are addressed within SLO thresholds."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Ensures webhooks are securely configured."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Establish a process to periodically review and archive inactive repositories."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Establish a process to periodically review and remove inactive users."
msgstr ""
msgid "ComplianceStandardsAdherence|External control"
@@ -15859,9 +16156,6 @@ msgstr ""
msgid "ComplianceStandardsAdherence|Frameworks"
msgstr ""
-msgid "ComplianceStandardsAdherence|Fuzz testing automatically generates random inputs to find unexpected behavior and potential security issues"
-msgstr ""
-
msgid "ComplianceStandardsAdherence|Group by"
msgstr ""
@@ -15889,28 +16183,28 @@ msgstr ""
msgid "ComplianceStandardsAdherence|Implement API security testing"
msgstr ""
-msgid "ComplianceStandardsAdherence|Implement API security testing to protect your application interfaces from attacks"
+msgid "ComplianceStandardsAdherence|Implement API security testing to protect your application interfaces from attacks."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Implement repository review process"
msgstr ""
msgid "ComplianceStandardsAdherence|Implement secret detection"
msgstr ""
-msgid "ComplianceStandardsAdherence|Implement secret detection scanning in your CI/CD pipeline to identify and remove exposed credentials"
+msgid "ComplianceStandardsAdherence|Implement secret detection scanning in your CI/CD pipeline to identify and remove exposed credentials."
msgstr ""
-msgid "ComplianceStandardsAdherence|Infrastructure as Code (IaC) scanning detects misconfigurations in your infrastructure definitions before deployment"
+msgid "ComplianceStandardsAdherence|Implement user review process"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Implement vulnerability SLOs"
msgstr ""
msgid "ComplianceStandardsAdherence|Last scanned"
msgstr ""
-msgid "ComplianceStandardsAdherence|Learn about code review best practices"
-msgstr ""
-
-msgid "ComplianceStandardsAdherence|Learn more about implementing effective code review practices to enhance security"
-msgstr ""
-
-msgid "ComplianceStandardsAdherence|License compliance scanning identifies potentially problematic open source licenses that could create legal issues"
+msgid "ComplianceStandardsAdherence|Lock approval rules"
msgstr ""
msgid "ComplianceStandardsAdherence|Merge request approval rules"
@@ -15937,9 +16231,6 @@ msgstr ""
msgid "ComplianceStandardsAdherence|None"
msgstr ""
-msgid "ComplianceStandardsAdherence|Organization policy requires that projects are not set to internal visibility to protect sensitive data"
-msgstr ""
-
msgid "ComplianceStandardsAdherence|Other compliance frameworks applied to %{linkStart}%{projectName}%{linkEnd}"
msgstr ""
@@ -15967,7 +16258,19 @@ msgstr ""
msgid "ComplianceStandardsAdherence|Prevent committers as approvers"
msgstr ""
-msgid "ComplianceStandardsAdherence|Preventing authors from approving their own merge requests ensures independent code review"
+msgid "ComplianceStandardsAdherence|Prevents deletion of branches."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Prevents direct pushes to the default branch."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Prevents editing of merge request approval rules."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Prevents force pushing to repositories."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Prevents users who have committed to a merge request from approving it."
msgstr ""
msgid "ComplianceStandardsAdherence|Project"
@@ -15982,16 +16285,52 @@ msgstr ""
msgid "ComplianceStandardsAdherence|Raw text search is not currently supported. Please use the available filters."
msgstr ""
+msgid "ComplianceStandardsAdherence|Require code owner approval"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Require resolved discussions"
+msgstr ""
+
msgid "ComplianceStandardsAdherence|Requirement"
msgstr ""
msgid "ComplianceStandardsAdherence|Requirements"
msgstr ""
-msgid "ComplianceStandardsAdherence|Review best practices for secure container deployments"
+msgid "ComplianceStandardsAdherence|Restrict pipeline access"
msgstr ""
-msgid "ComplianceStandardsAdherence|Review container security practices"
+msgid "ComplianceStandardsAdherence|Restrict pipeline visibility"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Restrict variable creation"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Restricts access to build artifacts and pipeline outputs."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Restricts creation of project variables to maintainers only."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Restricts who can push to or merge into protected branches."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Review and secure all webhook configurations to prevent unauthorized access."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Review and triage all package hunter findings to assess and address potential risks."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Review and update the project status to ensure it is not marked for deletion."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Review archive status"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Review project status"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Review the project archive status and unarchive if necessary for active projects."
msgstr ""
msgid "ComplianceStandardsAdherence|SAST scan"
@@ -16003,28 +16342,76 @@ msgstr ""
msgid "ComplianceStandardsAdherence|SAST scanner is not configured in the pipeline configuration for the default branch."
msgstr ""
-msgid "ComplianceStandardsAdherence|Secret detection prevents sensitive information like API keys from being accidentally committed to your repository"
+msgid "ComplianceStandardsAdherence|Secure webhook configurations"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up CI/CD job token scope restrictions to enhance pipeline security."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up IaC scanning"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up SAST in your CI/CD pipeline to automatically detect code vulnerabilities."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up Service Level Objectives for addressing identified vulnerabilities within defined timeframes."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up Single Sign-On authentication to improve security and user management."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up Terraform integration for your project to manage infrastructure as code."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up a properly configured CI/CD pipeline for your project."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up a status page to communicate project availability and incidents."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up automatic cleanup of stale branches to maintain repository hygiene."
msgstr ""
msgid "ComplianceStandardsAdherence|Set up branch protection"
msgstr ""
-msgid "ComplianceStandardsAdherence|Set up branch protection rules for your default branch to enforce quality standards"
+msgid "ComplianceStandardsAdherence|Set up branch protection rules for your default branch to enforce quality standards."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up branch protection rules to restrict push and merge access to protected branches."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up code owners"
msgstr ""
msgid "ComplianceStandardsAdherence|Set up container scanning"
msgstr ""
-msgid "ComplianceStandardsAdherence|Set up container scanning in your pipeline to identify vulnerabilities in your container images"
+msgid "ComplianceStandardsAdherence|Set up container scanning in your pipeline to identify vulnerabilities in your container images."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up error tracking to monitor and address application errors."
msgstr ""
msgid "ComplianceStandardsAdherence|Set up fuzz testing"
msgstr ""
-msgid "ComplianceStandardsAdherence|Set up fuzz testing in your pipeline to identify edge cases and potential crashes"
+msgid "ComplianceStandardsAdherence|Set up fuzz testing in your pipeline to identify edge cases and potential crashes."
msgstr ""
-msgid "ComplianceStandardsAdherence|Single Sign-On authentication improves security by centralizing user access management"
+msgid "ComplianceStandardsAdherence|Set up license compliance scanning to identify potentially problematic open source licenses."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up required status checks to ensure code quality before merging."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up requirements for branches to be up to date before merging."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up strict permission controls for repository access."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Set up your project to maintain a clean, linear commit history."
msgstr ""
msgid "ComplianceStandardsAdherence|Standard"
@@ -16033,9 +16420,6 @@ msgstr ""
msgid "ComplianceStandardsAdherence|Standards"
msgstr ""
-msgid "ComplianceStandardsAdherence|Static Application Security Testing (SAST) scans your code for vulnerabilities that may lead to exploits"
-msgstr ""
-
msgid "ComplianceStandardsAdherence|Status"
msgstr ""
@@ -16048,6 +16432,9 @@ msgstr ""
msgid "ComplianceStandardsAdherence|This is an external control for %{link}"
msgstr ""
+msgid "ComplianceStandardsAdherence|Triage package hunter findings"
+msgstr ""
+
msgid "ComplianceStandardsAdherence|Unable to load the standards adherence report. Refresh the page and try again."
msgstr ""
@@ -16057,7 +16444,13 @@ msgstr ""
msgid "ComplianceStandardsAdherence|Update project visibility"
msgstr ""
-msgid "ComplianceStandardsAdherence|Update your approval settings to prevent committers from approving merge requests containing their commits"
+msgid "ComplianceStandardsAdherence|Update your approval settings to prevent committers from approving merge requests containing their commits."
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Upgrade to Ultimate license"
+msgstr ""
+
+msgid "ComplianceStandardsAdherence|Upgrade your GitLab license to Ultimate to access all security and compliance features."
msgstr ""
msgid "ComplianceStandardsAdherence|View details"
@@ -16126,9 +16519,6 @@ msgstr ""
msgid "Configuration help"
msgstr ""
-msgid "Configuration options"
-msgstr ""
-
msgid "Configure"
msgstr ""
@@ -16204,9 +16594,6 @@ msgstr ""
msgid "Configure advanced permissions, Large File Storage, two-factor authentication, and customer relations settings."
msgstr ""
-msgid "Configure approval rules"
-msgstr ""
-
msgid "Configure checkin reminder frequency"
msgstr ""
@@ -16222,9 +16609,6 @@ msgstr ""
msgid "Configure it later"
msgstr ""
-msgid "Configure now"
-msgstr ""
-
msgid "Configure pipeline"
msgstr ""
@@ -17374,7 +17758,7 @@ msgstr ""
msgid "Control + Shift + M"
msgstr ""
-msgid "Control whether events are sent to GitLab."
+msgid "Control whether events are sent to GitLab or your own %{snowplow_link_start}Snowplow%{snowplow_link_end} collector. Only one can be selected at a time and enabling one will disable the other. %{help_link_start}Learn more.%{help_link_end}"
msgstr ""
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
@@ -18756,6 +19140,9 @@ msgstr ""
msgid "CycleAnalytics|Failed to parse stage events."
msgstr ""
+msgid "CycleAnalytics|Failed to parse value stream."
+msgstr ""
+
msgid "CycleAnalytics|If you have recently upgraded your GitLab license from a tier without this feature, it can take up to 30 minutes for data to collect and display."
msgstr ""
@@ -18824,6 +19211,9 @@ msgstr ""
msgid "CycleAnalytics|seconds"
msgstr ""
+msgid "DAST configuration"
+msgstr ""
+
msgid "DAST configuration not found"
msgstr ""
@@ -20478,6 +20868,9 @@ msgstr ""
msgid "Deleting a project places it into a read-only state until %{date}, at which point the project will be permanently deleted. Are you ABSOLUTELY sure?"
msgstr ""
+msgid "Deleting branches"
+msgstr ""
+
msgid "Deleting project '%{project_name}'. All data will be removed on %{date}."
msgstr ""
@@ -23389,6 +23782,9 @@ msgstr ""
msgid "Enable automatic repository housekeeping"
msgstr ""
+msgid "Enable blocking of anonymous global search requests"
+msgstr ""
+
msgid "Enable cleanup policies for projects created earlier than GitLab 12.7."
msgstr ""
@@ -23416,6 +23812,9 @@ msgstr ""
msgid "Enable for this project"
msgstr ""
+msgid "Enable global search for limited indexing"
+msgstr ""
+
msgid "Enable group runners"
msgstr ""
@@ -24661,6 +25060,9 @@ msgstr ""
msgid "Event tag (optional)"
msgstr ""
+msgid "Event tracking"
+msgstr ""
+
msgid "Event type '%{type}' is not yet supported"
msgstr ""
@@ -26543,6 +26945,9 @@ msgstr ""
msgid "Function `labels` can only take a maximum of 10 parameters."
msgstr ""
+msgid "Fuzz testing"
+msgstr ""
+
msgid "GCP region configured"
msgstr ""
@@ -27261,6 +27666,9 @@ msgstr ""
msgid "Git global setup"
msgstr ""
+msgid "Git in GitLab"
+msgstr ""
+
msgid "Git local setup"
msgstr ""
@@ -27495,6 +27903,12 @@ msgstr ""
msgid "GitLab is undergoing maintenance"
msgstr ""
+msgid "GitLab issues"
+msgstr ""
+
+msgid "GitLab licensing"
+msgstr ""
+
msgid "GitLab logo"
msgstr ""
@@ -29041,6 +29455,9 @@ msgstr ""
msgid "Group work item bulk edit is a licensed feature not available for this group."
msgstr ""
+msgid "Group-level MFA enforcement"
+msgstr ""
+
msgid "Group-level wiki is disabled."
msgstr ""
@@ -30548,6 +30965,9 @@ msgstr ""
msgid "IP subnet restriction only allowed for top-level groups"
msgstr ""
+msgid "IaC security"
+msgstr ""
+
msgid "Icon"
msgstr ""
@@ -30976,12 +31396,6 @@ msgstr ""
msgid "Impersonation tokens"
msgstr ""
-msgid "Implementation details"
-msgstr ""
-
-msgid "Implementation guide"
-msgstr ""
-
msgid "Import"
msgstr ""
@@ -35309,6 +35723,9 @@ msgstr ""
msgid "License Compliance| Used by %{dependencies}"
msgstr ""
+msgid "License compliance"
+msgstr ""
+
msgid "License does not provide access to Code Suggestions."
msgstr ""
@@ -35782,6 +36199,9 @@ msgstr ""
msgid "MD5"
msgstr ""
+msgid "MFA for contributors"
+msgstr ""
+
msgid "MLExperimentTracking|Delete experiment?"
msgstr ""
@@ -35929,15 +36349,15 @@ msgstr ""
msgid "Manage usage"
msgstr ""
-msgid "Manage visibility"
-msgstr ""
-
msgid "Manage your subscription"
msgstr ""
msgid "Managed Account"
msgstr ""
+msgid "Managing users"
+msgstr ""
+
msgid "Manifest"
msgstr ""
@@ -37159,6 +37579,9 @@ msgstr ""
msgid "Merge request approvals"
msgstr ""
+msgid "Merge request approvals settings"
+msgstr ""
+
msgid "Merge request author cannot push to target project"
msgstr ""
@@ -37177,6 +37600,9 @@ msgstr ""
msgid "Merge request events"
msgstr ""
+msgid "Merge request fast-forward merges"
+msgstr ""
+
msgid "Merge request must be open."
msgstr ""
@@ -42239,6 +42665,9 @@ msgstr ""
msgid "Package forwarding"
msgstr ""
+msgid "Package hunter"
+msgstr ""
+
msgid "Package limits"
msgstr ""
@@ -43549,6 +43978,12 @@ msgstr ""
msgid "Pipeline schedules"
msgstr ""
+msgid "Pipeline security"
+msgstr ""
+
+msgid "Pipeline settings"
+msgstr ""
+
msgid "Pipeline status emails"
msgstr ""
@@ -45512,9 +45947,6 @@ msgstr ""
msgid "Product analytics"
msgstr ""
-msgid "Product usage data tracking"
-msgstr ""
-
msgid "ProductAnalytics|1. Add the NPM package to your package.json using your preferred package manager"
msgstr ""
@@ -46415,6 +46847,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project CI/CD variables"
+msgstr ""
+
msgid "Project Files"
msgstr ""
@@ -46502,6 +46937,9 @@ msgstr ""
msgid "Project members"
msgstr ""
+msgid "Project members permissions"
+msgstr ""
+
msgid "Project milestone"
msgstr ""
@@ -46553,6 +46991,9 @@ msgstr ""
msgid "Project variables"
msgstr ""
+msgid "Project visibility"
+msgstr ""
+
msgid "Project visibility level is less restrictive than the group settings."
msgstr ""
@@ -50452,6 +50893,9 @@ msgstr ""
msgid "Reset"
msgstr ""
+msgid "Reset approvals on push"
+msgstr ""
+
msgid "Reset conversation and ignore previous messages."
msgstr ""
@@ -50491,6 +50935,9 @@ msgstr ""
msgid "Resolve conflicts"
msgstr ""
+msgid "Resolve discussions"
+msgstr ""
+
msgid "Resolve locally"
msgstr ""
@@ -52016,6 +52463,9 @@ msgstr ""
msgid "SAST"
msgstr ""
+msgid "SAST configuration"
+msgstr ""
+
msgid "SBOMs last updated"
msgstr ""
@@ -52094,6 +52544,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "SSO for GitLab.com groups"
+msgstr ""
+
msgid "Sat"
msgstr ""
@@ -53299,9 +53752,6 @@ msgstr ""
msgid "Security reports last updated"
msgstr ""
-msgid "Security settings"
-msgstr ""
-
msgid "SecurityApprovals|A merge request approval is required when test coverage declines."
msgstr ""
@@ -55163,6 +55613,9 @@ msgstr ""
msgid "SecurityReports|Projects added"
msgstr ""
+msgid "SecurityReports|Reachable:"
+msgstr ""
+
msgid "SecurityReports|Remove project from dashboard"
msgstr ""
@@ -56401,9 +56854,6 @@ msgstr ""
msgid "Settings|What is experiment?"
msgstr ""
-msgid "Setup guide"
-msgstr ""
-
msgid "Severity"
msgstr ""
@@ -57098,6 +57548,9 @@ msgstr ""
msgid "Snowplow"
msgstr ""
+msgid "Snowplow tracking and Product event tracking cannot be enabled at the same time. Please disable one of them."
+msgstr ""
+
msgid "Soft wrap"
msgstr ""
@@ -57919,12 +58372,18 @@ msgstr ""
msgid "Status (optional)"
msgstr ""
+msgid "Status checks"
+msgstr ""
+
msgid "Status checks must pass."
msgstr ""
msgid "Status not supported"
msgstr ""
+msgid "Status page"
+msgstr ""
+
msgid "Status was retried."
msgstr ""
@@ -59296,6 +59755,9 @@ msgstr ""
msgid "Terraform Module Registry"
msgstr ""
+msgid "Terraform in GitLab"
+msgstr ""
+
msgid "Terraform modules"
msgstr ""
@@ -65766,9 +66228,6 @@ msgstr ""
msgid "View trigger token usage examples"
msgstr ""
-msgid "View tutorial"
-msgstr ""
-
msgid "View usage details"
msgstr ""
@@ -65913,9 +66372,6 @@ msgstr ""
msgid "Vulnerabilities|Vulnerability report export"
msgstr ""
-msgid "Vulnerabilities|changed vulnerability status to Needs Triage because it was redetected in pipeline %{pipeline_link}"
-msgstr ""
-
msgid "Vulnerability"
msgstr ""
@@ -65925,6 +66381,9 @@ msgstr ""
msgid "Vulnerability Report"
msgstr ""
+msgid "Vulnerability management"
+msgstr ""
+
msgid "Vulnerability remediated. Review before resolving."
msgstr ""
@@ -67905,6 +68364,9 @@ msgstr ""
msgid "WorkItem|Link items together to show that they're related or that one is blocking others."
msgstr ""
+msgid "WorkItem|Link items together to show that they're related."
+msgstr ""
+
msgid "WorkItem|Linked item removed"
msgstr ""
diff --git a/package.json b/package.json
index c5778fc3048..07331b0ec7e 100644
--- a/package.json
+++ b/package.json
@@ -66,7 +66,7 @@
"@gitlab/fonts": "^1.3.0",
"@gitlab/query-language-rust": "0.5.2",
"@gitlab/svgs": "3.126.0",
- "@gitlab/ui": "112.2.1",
+ "@gitlab/ui": "112.2.2",
"@gitlab/vue-router-vue3": "npm:vue-router@4.5.0",
"@gitlab/vuex-vue3": "npm:vuex@4.1.0",
"@gitlab/web-ide": "^0.0.1-dev-20250401183248",
diff --git a/rubocop/rubocop-migrations.yml b/rubocop/rubocop-migrations.yml
index a1dd431d321..056a8758644 100644
--- a/rubocop/rubocop-migrations.yml
+++ b/rubocop/rubocop-migrations.yml
@@ -18,6 +18,7 @@ Migration/UpdateLargeTable:
- :ci_job_artifacts
- :ci_job_artifacts_102
- :ci_job_variables
+ - :ci_pipeline_messages
- :ci_pipeline_variables
- :ci_pipeline_variables_102
- :ci_pipelines
@@ -57,6 +58,10 @@ Migration/UpdateLargeTable:
- :ci_job_artifact_states
- :ci_pipeline_messages
- :namespaces
+ - :approval_merge_request_rules_users
+ - :audit_events
+ - :ci_job_artifact_states
+ - :ci_pipeline_messages
- :packages_package_files
- :personal_access_tokens
- :projects
@@ -149,4 +154,4 @@ Migration/UpdateLargeTable:
DeniedMethods:
- :change_column_type_concurrently
- :rename_column_concurrently
- - :update_column_in_batches
+ - :update_column_in_batches
\ No newline at end of file
diff --git a/spec/components/rapid_diffs/viewers/no_preview_component_spec.rb b/spec/components/rapid_diffs/viewers/no_preview_component_spec.rb
index 91b03270dcb..43cb22f05fd 100644
--- a/spec/components/rapid_diffs/viewers/no_preview_component_spec.rb
+++ b/spec/components/rapid_diffs/viewers/no_preview_component_spec.rb
@@ -136,6 +136,17 @@ RSpec.describe RapidDiffs::Viewers::NoPreviewComponent, type: :component, featur
expect(page).to have_text("No diff preview for this file type.")
end
end
+
+ context 'with a whitespace only diff' do
+ before do
+ allow(diff_file).to receive(:whitespace_only?).and_return(true)
+ end
+
+ it 'shows no preview message' do
+ render_component
+ expect(page).to have_text("Contains only whitespace changes.")
+ end
+ end
end
describe 'actions' do
@@ -151,7 +162,7 @@ RSpec.describe RapidDiffs::Viewers::NoPreviewComponent, type: :component, featur
allow(diff_file).to receive(:diffable_text?).and_return(true)
end
- it 'shows no preview message' do
+ it 'shows preview button' do
render_component
expect(page).to have_button("Show file contents")
end
@@ -161,7 +172,18 @@ RSpec.describe RapidDiffs::Viewers::NoPreviewComponent, type: :component, featur
allow(diff_file).to receive(:collapsed?).and_return(true)
end
- it 'shows no preview message' do
+ it 'shows preview button' do
+ render_component
+ expect(page).to have_button("Show changes")
+ end
+ end
+
+ context 'when diff is whitespace only' do
+ before do
+ allow(diff_file).to receive(:whitespace_only?).and_return(true)
+ end
+
+ it 'shows preview button' do
render_component
expect(page).to have_button("Show changes")
end
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 80f63b4049c..713c18f2639 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -155,9 +155,9 @@ RSpec.describe SearchController, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
render_views
- context 'when block_anonymous_global_searches is disabled' do
+ context 'when global_search_block_anonymous_searches_enabled is disabled' do
before do
- stub_feature_flags(block_anonymous_global_searches: false)
+ stub_application_setting(global_search_block_anonymous_searches_enabled: false)
end
it 'omits pipeline status from load' do
@@ -212,7 +212,11 @@ RSpec.describe SearchController, feature_category: :global_search do
end
end
- context 'when block_anonymous_global_searches is enabled' do
+ context 'when global_search_block_anonymous_searches_enabled is enabled' do
+ before do
+ stub_application_setting(global_search_block_anonymous_searches_enabled: true)
+ end
+
context 'for unauthenticated user' do
before do
sign_out(user)
@@ -763,7 +767,7 @@ RSpec.describe SearchController, feature_category: :global_search do
before do
stub_application_setting(restricted_visibility_levels: restricted_visibility_levels)
stub_feature_flags(allow_anonymous_searches: allow_anonymous_searches)
- stub_feature_flags(block_anonymous_global_searches: block_anonymous_global_searches)
+ stub_application_setting(global_search_block_anonymous_searches_enabled: block_anonymous_global_searches)
end
it 'redirects to the sign in/sign up page when it should' do
diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb
index ae861817234..531e6f97561 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -136,7 +136,11 @@ RSpec.describe 'User searches for code', :js, :disable_rate_limiter, feature_cat
end
context 'when signed out' do
- context 'when block_anonymous_global_searches is enabled' do
+ context 'when global_search_block_anonymous_searches_enabled is enabled' do
+ before do
+ stub_application_setting(global_search_block_anonymous_searches_enabled: true)
+ end
+
it 'is redirected to login page' do
visit(search_path)
diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb
index ca0483ca838..3bcf1e2d17a 100644
--- a/spec/features/search/user_searches_for_issues_spec.rb
+++ b/spec/features/search/user_searches_for_issues_spec.rb
@@ -131,11 +131,11 @@ RSpec.describe 'User searches for issues', :js, :clean_gitlab_redis_rate_limitin
end
context 'when signed out' do
- context 'when block_anonymous_global_searches is disabled' do
+ context 'when global_search_block_anonymous_searches_enabled is disabled' do
let_it_be(:project) { create(:project, :public) }
before do
- stub_feature_flags(block_anonymous_global_searches: false)
+ stub_application_setting(global_search_block_anonymous_searches_enabled: false)
visit(search_path)
end
@@ -152,7 +152,11 @@ RSpec.describe 'User searches for issues', :js, :clean_gitlab_redis_rate_limitin
end
end
- context 'when block_anonymous_global_searches is enabled' do
+ context 'when global_search_block_anonymous_searches_enabled is enabled' do
+ before do
+ stub_application_setting(global_search_block_anonymous_searches_enabled: true)
+ end
+
it 'is redirected to login page' do
visit(search_path)
diff --git a/spec/features/search/user_searches_for_projects_spec.rb b/spec/features/search/user_searches_for_projects_spec.rb
index b1d8ce69823..165872cef5a 100644
--- a/spec/features/search/user_searches_for_projects_spec.rb
+++ b/spec/features/search/user_searches_for_projects_spec.rb
@@ -6,9 +6,9 @@ RSpec.describe 'User searches for projects', :js, :disable_rate_limiter, feature
let!(:project) { create(:project, :public, name: 'Shop') }
context 'when signed out' do
- context 'when block_anonymous_global_searches is disabled' do
+ context 'when global_search_block_anonymous_searches_enabled is disabled' do
before do
- stub_feature_flags(block_anonymous_global_searches: false)
+ stub_application_setting(global_search_block_anonymous_searches_enabled: false)
end
include_examples 'top right search form'
@@ -46,7 +46,11 @@ RSpec.describe 'User searches for projects', :js, :disable_rate_limiter, feature
end
end
- context 'when block_anonymous_global_searches is enabled' do
+ context 'when global_search_block_anonymous_searches_enabled is enabled' do
+ before do
+ stub_application_setting(global_search_block_anonymous_searches_enabled: true)
+ end
+
it 'is redirected to login page' do
visit(search_path)
expect(page).to have_content('You need to sign in or sign up before continuing.')
diff --git a/spec/frontend/ci/pipeline_details/graph/components/job_group_dropdown_spec.js b/spec/frontend/ci/pipeline_details/graph/components/job_group_dropdown_spec.js
index 3934f7a4b91..3082a383fae 100644
--- a/spec/frontend/ci/pipeline_details/graph/components/job_group_dropdown_spec.js
+++ b/spec/frontend/ci/pipeline_details/graph/components/job_group_dropdown_spec.js
@@ -71,6 +71,7 @@ describe('job group dropdown component', () => {
const findTriggerButton = () => wrapper.find('button');
const findDisclosureDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
const findJobDropdownItems = () => wrapper.findAllComponents(JobDropdownItem);
+ const findFailedJobs = () => wrapper.find('[data-testid="failed-jobs"]');
const createComponent = ({ props, mountFn = shallowMount } = {}) => {
wrapper = mountFn(JobGroupDropdown, {
@@ -207,4 +208,45 @@ describe('job group dropdown component', () => {
},
);
});
+
+ describe('failed jobs', () => {
+ it('shows failed jobs grouped if there are any', () => {
+ const failedJob = {
+ id: 5000,
+ name: 'rspec:linux 1/3',
+ status: {
+ icon: 'status_failed',
+ text: 'failed',
+ label: 'failed',
+ tooltip: 'failed',
+ group: 'failed',
+ },
+ };
+
+ createComponent({
+ props: {
+ group: {
+ ...group,
+ status: {
+ status: 'failed',
+ tooltip: 'Failed - (stuck or timeout failure) (allowed to fail)',
+ text: 'Failed text',
+ },
+ jobs: [...group.jobs, failedJob],
+ },
+ },
+ mountFn: mount,
+ });
+
+ expect(findFailedJobs().exists()).toBe(true);
+ expect(findFailedJobs().text()).toContain('Failed jobs');
+ expect(findFailedJobs().text()).toContain('rspec:linux 1/3');
+ });
+
+ it('does not show failed jobs if there aren`t any', () => {
+ createComponent();
+
+ expect(findFailedJobs().exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/pages/admin/application_settings/metrics_and_profiling/product_usage_data_spec.js b/spec/frontend/pages/admin/application_settings/metrics_and_profiling/product_usage_data_spec.js
new file mode 100644
index 00000000000..55b98969c10
--- /dev/null
+++ b/spec/frontend/pages/admin/application_settings/metrics_and_profiling/product_usage_data_spec.js
@@ -0,0 +1,72 @@
+import initProductUsageData from '~/pages/admin/application_settings/metrics_and_profiling/product_usage_data';
+import { ELEMENT_IDS } from '~/pages/admin/application_settings/metrics_and_profiling/constants';
+
+describe('Product Usage Data', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `
+
+
+
+ `;
+ });
+
+ describe('initProductUsageData Functionality', () => {
+ it('should hide snowplow settings when snowplow is not checked', () => {
+ initProductUsageData();
+
+ const snowplowSettings = document.getElementById('js-snowplow-settings');
+ expect(snowplowSettings.style.display).toBe('none');
+ });
+
+ it('should show snowplow settings when snowplow is checked', () => {
+ const snowplowCheckbox = document.getElementById(ELEMENT_IDS.SNOWPLOW_ENABLED);
+ snowplowCheckbox.checked = true;
+
+ initProductUsageData();
+
+ const snowplowSettings = document.getElementById('js-snowplow-settings');
+ expect(snowplowSettings.style.display).toBe('block');
+ });
+
+ it('should uncheck snowplow when product usage is checked', () => {
+ initProductUsageData();
+
+ const productUsageCheckbox = document.getElementById(ELEMENT_IDS.PRODUCT_USAGE_DATA);
+ const snowplowCheckbox = document.getElementById(ELEMENT_IDS.SNOWPLOW_ENABLED);
+
+ snowplowCheckbox.checked = true;
+ productUsageCheckbox.checked = true;
+ productUsageCheckbox.dispatchEvent(new Event('change'));
+
+ expect(snowplowCheckbox.checked).toBe(false);
+ });
+
+ it('should uncheck product usage when snowplow is checked', () => {
+ initProductUsageData();
+
+ const productUsageCheckbox = document.getElementById(ELEMENT_IDS.PRODUCT_USAGE_DATA);
+ const snowplowCheckbox = document.getElementById(ELEMENT_IDS.SNOWPLOW_ENABLED);
+
+ productUsageCheckbox.checked = true;
+ snowplowCheckbox.checked = true;
+ snowplowCheckbox.dispatchEvent(new Event('change'));
+
+ expect(productUsageCheckbox.checked).toBe(false);
+ });
+
+ it('should toggle snowplow settings visibility when snowplow checkbox changes', () => {
+ initProductUsageData();
+
+ const snowplowCheckbox = document.getElementById(ELEMENT_IDS.SNOWPLOW_ENABLED);
+ const snowplowSettings = document.getElementById('js-snowplow-settings');
+
+ snowplowCheckbox.checked = true;
+ snowplowCheckbox.dispatchEvent(new Event('change'));
+ expect(snowplowSettings.style.display).toBe('block');
+
+ snowplowCheckbox.checked = false;
+ snowplowCheckbox.dispatchEvent(new Event('change'));
+ expect(snowplowSettings.style.display).toBe('none');
+ });
+ });
+});
diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js
index 35b793bef54..f0c51b4d2fb 100644
--- a/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js
+++ b/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js
@@ -30,6 +30,7 @@ describe('WorkItemAddRelationshipForm', () => {
workItemType = 'Objective',
childrenIds = [],
linkedWorkItemsMutationHandler = linkedWorkItemsSuccessMutationHandler,
+ hasBlockedWorkItemsFeature = true,
} = {}) => {
const mockApolloProvider = createMockApollo([
[addLinkedItemsMutation, linkedWorkItemsMutationHandler],
@@ -43,6 +44,7 @@ describe('WorkItemAddRelationshipForm', () => {
workItemFullPath: 'test-project-path',
workItemType,
childrenIds,
+ hasBlockedWorkItemsFeature,
},
});
@@ -71,6 +73,13 @@ describe('WorkItemAddRelationshipForm', () => {
expect(findMaxWorkItemNote().text()).toBe('Add up to 10 items at a time.');
});
+ it('does not render relationship type radio options when hasBlockedWorkItemsFeature is false', async () => {
+ await createComponent({ hasBlockedWorkItemsFeature: false });
+
+ expect(findLinkWorkItemForm().exists()).toBe(true);
+ expect(findRadioGroup().exists()).toBe(false);
+ });
+
it('renders work item token input with default props', () => {
expect(findWorkItemTokenInput().props()).toMatchObject({
value: [],
diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js
index 0476e391f63..01356ba51e8 100644
--- a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js
+++ b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js
@@ -52,6 +52,7 @@ describe('WorkItemRelationships', () => {
workItemLinkedItemsHandler = workItemLinkedItemsSuccessHandler,
removeLinkedWorkItemMutationHandler = removeLinkedWorkItemSuccessMutationHandler,
canAdminWorkItemLink = true,
+ hasBlockedWorkItemsFeature = true,
} = {}) => {
const mockApollo = createMockApollo([
[workItemLinkedItemsQuery, workItemLinkedItemsHandler],
@@ -66,6 +67,7 @@ describe('WorkItemRelationships', () => {
workItemFullPath: 'gitlab-org/gitlab-test',
canAdminWorkItemLink,
workItemType,
+ hasBlockedWorkItemsFeature,
},
mocks: {
$toast,
@@ -118,6 +120,23 @@ describe('WorkItemRelationships', () => {
expect(findWorkItemRelationshipForm().exists()).toBe(false);
});
+ it.each`
+ hasBlockedWorkItemsFeature | emptyStateMessage
+ ${true} | ${"Link items together to show that they're related or that one is blocking others."}
+ ${false} | ${"Link items together to show that they're related."}
+ `(
+ 'renders the component with correct empty state message when hasBlockedWorkItemsFeature is $hasBlockedWorkItemsFeature',
+ async ({ hasBlockedWorkItemsFeature, emptyStateMessage }) => {
+ await createComponent({
+ workItemLinkedItemsHandler: jest.fn().mockResolvedValue(workItemEmptyLinkedItemsResponse),
+ hasBlockedWorkItemsFeature,
+ });
+
+ expect(findEmptyRelatedMessageContainer().exists()).toBe(true);
+ expect(findEmptyRelatedMessageContainer().text()).toBe(emptyStateMessage);
+ },
+ );
+
it('renders blocking, blocked by and related to linked item lists with proper count', async () => {
await createComponent();
@@ -148,6 +167,7 @@ describe('WorkItemRelationships', () => {
await findAddButton().vm.$emit('click');
expect(findWorkItemRelationshipForm().exists()).toBe(true);
+ expect(findWorkItemRelationshipForm().props('hasBlockedWorkItemsFeature')).toBe(true);
await findWorkItemRelationshipForm().vm.$emit('cancel');
expect(findWorkItemRelationshipForm().exists()).toBe(false);
diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb
index 1e9fd9f8fc1..186926cb24b 100644
--- a/spec/helpers/application_settings_helper_spec.rb
+++ b/spec/helpers/application_settings_helper_spec.rb
@@ -51,6 +51,11 @@ RSpec.describe ApplicationSettingsHelper, feature_category: :shared do
.to include(*%i[snowplow_collector_hostname snowplow_cookie_domain snowplow_enabled snowplow_app_id])
end
+ it 'contains product usage data setting' do
+ expect(helper.visible_attributes)
+ .to include(:gitlab_product_usage_data_enabled)
+ end
+
it 'contains :resource_usage_limits' do
expect(helper.visible_attributes).to include(:resource_usage_limits)
end
@@ -387,16 +392,18 @@ RSpec.describe ApplicationSettingsHelper, feature_category: :shared do
application_setting.global_search_merge_requests_enabled = false
application_setting.global_search_users_enabled = false
application_setting.global_search_snippet_titles_enabled = true
+ application_setting.global_search_block_anonymous_searches_enabled = true
helper.instance_variable_set(:@application_setting, application_setting)
end
it 'returns correctly checked checkboxes' do
helper.gitlab_ui_form_for(application_setting, url: search_admin_application_settings_path) do |form|
result = helper.global_search_settings_checkboxes(form)
- expect(result[0]).to have_checked_field('Enable issues tab in global search results', with: 1)
- expect(result[1]).not_to have_checked_field('Enable merge requests tab in global search results', with: 1)
- expect(result[2]).to have_checked_field('Enable snippet tab in global search results', with: 1)
- expect(result[3]).not_to have_checked_field('Enable users tab in global search results', with: 1)
+ expect(result[0]).to have_checked_field('Enable blocking of anonymous global search requests', with: 1)
+ expect(result[1]).to have_checked_field('Enable issues tab in global search results', with: 1)
+ expect(result[2]).not_to have_checked_field('Enable merge requests tab in global search results', with: 1)
+ expect(result[3]).to have_checked_field('Enable snippet tab in global search results', with: 1)
+ expect(result[4]).not_to have_checked_field('Enable users tab in global search results', with: 1)
end
end
end
diff --git a/spec/initializers/1_settings_spec.rb b/spec/initializers/1_settings_spec.rb
index 67a3db46b42..503667067dd 100644
--- a/spec/initializers/1_settings_spec.rb
+++ b/spec/initializers/1_settings_spec.rb
@@ -55,6 +55,26 @@ RSpec.describe '1_settings', feature_category: :shared do
end
end
+ describe 'initial_gitlab_product_usage_data' do
+ it 'is enabled by default' do
+ Settings.gitlab['initial_gitlab_product_usage_data'] = nil
+ load_settings
+
+ expect(Settings.gitlab.initial_gitlab_product_usage_data).to be(true)
+ end
+
+ context 'when explicitly set' do
+ before do
+ Settings.gitlab['initial_gitlab_product_usage_data'] = false
+ load_settings
+ end
+
+ it 'uses the configured value' do
+ expect(Settings.gitlab.initial_gitlab_product_usage_data).to be(false)
+ end
+ end
+ end
+
describe 'cell configuration' do
let(:config) do
{
diff --git a/spec/lib/api/entities/project_spec.rb b/spec/lib/api/entities/project_spec.rb
index 4b02e1c6f1e..856a9dbac78 100644
--- a/spec/lib/api/entities/project_spec.rb
+++ b/spec/lib/api/entities/project_spec.rb
@@ -3,10 +3,11 @@
require 'spec_helper'
RSpec.describe ::API::Entities::Project do
- let(:project) { create(:project, :public) }
- let(:current_user) { create(:user) }
- let(:options) { { current_user: current_user } }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:parent_group) { create(:group, :allow_runner_registration_token) }
+ let_it_be(:project) { create(:project, :public, group: parent_group) }
+ let(:options) { { current_user: current_user } }
let(:entity) do
described_class.new(project, options)
end
@@ -14,7 +15,7 @@ RSpec.describe ::API::Entities::Project do
subject(:json) { entity.as_json }
context 'without project feature' do
- before do
+ before_all do
project.project_feature.destroy!
project.reload
end
@@ -27,12 +28,14 @@ RSpec.describe ::API::Entities::Project do
end
describe '.service_desk_address', feature_category: :service_desk do
+ let_it_be(:project) { create(:project, :public) }
+
before do
- allow(::ServiceDesk).to receive(:enabled?).and_return(true)
+ allow(::ServiceDesk).to receive(:enabled?).with(project).and_return(true)
end
context 'when a user can admin issues' do
- before do
+ before_all do
project.add_reporter(current_user)
end
@@ -49,9 +52,9 @@ RSpec.describe ::API::Entities::Project do
end
describe '.shared_with_groups' do
- let(:group) { create(:group, :private) }
+ let_it_be(:group) { create(:group, :private) }
- before do
+ before_all do
project.project_group_links.create!(group: group)
end
@@ -62,7 +65,7 @@ RSpec.describe ::API::Entities::Project do
end
context 'when the current user has access to the group' do
- before do
+ before_all do
group.add_guest(current_user)
end
@@ -74,7 +77,7 @@ RSpec.describe ::API::Entities::Project do
describe '.ci/cd settings' do
context 'when the user is not an admin' do
- before do
+ before_all do
project.add_reporter(current_user)
end
@@ -84,7 +87,7 @@ RSpec.describe ::API::Entities::Project do
end
context 'when the user has admin privileges' do
- before do
+ before_all do
project.add_maintainer(current_user)
end
@@ -93,4 +96,26 @@ RSpec.describe ::API::Entities::Project do
end
end
end
+
+ describe 'runner token settings', feature_category: :runner do
+ context 'when the user is not an admin' do
+ before_all do
+ project.add_reporter(current_user)
+ end
+
+ it 'does not return runner token settings' do
+ expect(json[:runners_token]).to be_nil
+ end
+ end
+
+ context 'when the user has admin privileges' do
+ before_all do
+ project.add_maintainer(current_user)
+ end
+
+ it 'returns runner token settings' do
+ expect(json[:runners_token]).to be_present
+ end
+ end
+ end
end
diff --git a/spec/lib/bulk_imports/ndjson_pipeline_spec.rb b/spec/lib/bulk_imports/ndjson_pipeline_spec.rb
index 721c15a4056..9f1e4bfa190 100644
--- a/spec/lib/bulk_imports/ndjson_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/ndjson_pipeline_spec.rb
@@ -179,6 +179,19 @@ RSpec.describe BulkImports::NdjsonPipeline, feature_category: :importers do
expect(Import::SourceUser.pluck(:source_user_identifier)).to match_array(%w[101 102 103 104 105 106])
end
end
+
+ context 'when relation hash includes attributes from source ghost user' do
+ before do
+ allow(context).to receive(:source_ghost_user_id).and_return('107')
+ end
+
+ it 'skips creating source user and placeholder user for ghost user references' do
+ expect { subject.deep_transform_relation!(relation_hash, 'test', relation_definition) { |a, _b| a } }
+ .to change { Import::SourceUser.count }.by(4).and change { User.count }.by(4)
+
+ expect(Import::SourceUser.pluck(:source_user_identifier)).not_to include('107')
+ end
+ end
end
context 'when subrelations is an array' do
diff --git a/spec/lib/bulk_imports/pipeline/context_spec.rb b/spec/lib/bulk_imports/pipeline/context_spec.rb
index ef7275c7526..06aab7ab7b2 100644
--- a/spec/lib/bulk_imports/pipeline/context_spec.rb
+++ b/spec/lib/bulk_imports/pipeline/context_spec.rb
@@ -110,4 +110,26 @@ RSpec.describe BulkImports::Pipeline::Context, feature_category: :importers do
it { is_expected.to eq(true) }
end
end
+
+ describe '#source_ghost_user_id' do
+ let(:source_internal_user_finder) { instance_double(BulkImports::SourceInternalUserFinder) }
+
+ before do
+ allow(BulkImports::SourceInternalUserFinder).to receive(:new)
+ .with(bulk_import.configuration)
+ .and_return(source_internal_user_finder)
+ end
+
+ it 'returns the ghost user ID' do
+ expect(source_internal_user_finder).to receive(:cached_ghost_user_id).and_return('10')
+
+ expect(subject.source_ghost_user_id).to eq('10')
+ end
+
+ it 'memoizes the result' do
+ expect(source_internal_user_finder).to receive(:cached_ghost_user_id).once.and_return('10')
+
+ 2.times { subject.source_ghost_user_id }
+ end
+ end
end
diff --git a/spec/lib/bulk_imports/source_internal_user_finder_spec.rb b/spec/lib/bulk_imports/source_internal_user_finder_spec.rb
new file mode 100644
index 00000000000..37bb415a3c7
--- /dev/null
+++ b/spec/lib/bulk_imports/source_internal_user_finder_spec.rb
@@ -0,0 +1,175 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::SourceInternalUserFinder, feature_category: :importers do
+ let(:url) { 'https://gitlab.example.com' }
+ let(:token) { 'token' }
+ let(:bulk_import) { create(:bulk_import, :started, configuration: configuration) }
+ let(:configuration) { build(:bulk_import_configuration, url: url, access_token: token) }
+ let(:client) { instance_double(BulkImports::Clients::Graphql) }
+ let(:service) { described_class.new(configuration) }
+ let(:cache_key) { format(described_class::GHOST_USER_CACHE_KEY, bulk_import_id: bulk_import.id) }
+
+ before do
+ allow(BulkImports::Clients::Graphql).to receive(:new).with(url: url, token: token).and_return(client)
+ end
+
+ describe '#fetch_ghost_user' do
+ let(:query) do
+ <<~GRAPHQL
+ {
+ users(usernames: ["ghost", "ghost1", "ghost2", "ghost3", "ghost4", "ghost5", "ghost6"], humans: false) {
+ nodes {
+ id
+ username
+ type
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ let(:users) do
+ [
+ { 'id' => 'gid://gitlab/User/210', 'username' => 'ghost1', 'type' => 'GHOST' }
+ ]
+ end
+
+ context 'when ghost user is found' do
+ let(:response) { { 'data' => { 'users' => { 'nodes' => users } } } }
+
+ it 'returns the ghost user from the GraphQL response' do
+ expect(client).to receive(:execute).with(query: query).and_return(response)
+
+ result = service.fetch_ghost_user
+
+ expect(result).to eq(users[0])
+ end
+ end
+
+ context 'when no ghost user is found' do
+ let(:response) { { 'data' => { 'users' => { 'nodes' => [] } } } }
+
+ it 'returns nil' do
+ expect(client).to receive(:execute).with(query: query).and_return(response)
+
+ result = service.fetch_ghost_user
+
+ expect(result).to be_nil
+ end
+ end
+
+ context 'when nodes is nil' do
+ let(:response) { { 'data' => { 'users' => { 'nodes' => nil } } } }
+
+ it 'returns nil' do
+ expect(client).to receive(:execute).with(query: query).and_return(response)
+
+ result = service.fetch_ghost_user
+
+ expect(result).to be_nil
+ end
+ end
+
+ context 'when the response structure is unexpected' do
+ let(:response) { { 'data' => {} } }
+
+ it 'returns nil' do
+ expect(client).to receive(:execute).with(query: query).and_return(response)
+
+ result = service.fetch_ghost_user
+
+ expect(result).to be_nil
+ end
+ end
+
+ context 'when API call fails' do
+ let(:error) { StandardError.new('API error') }
+
+ let(:response) { { 'data' => { 'users' => { 'nodes' => users } } } }
+
+ it 'retries the API call with exponential backoff' do
+ # First attempt fails
+ expect(client).to receive(:execute).with(query: query).and_raise(error).ordered
+ # Second attempt fails
+ expect(service).to receive(:sleep).with(2) # Exponential backoff 2^1
+ expect(client).to receive(:execute).with(query: query).and_raise(error).ordered
+ # Third attempt succeeds
+ expect(service).to receive(:sleep).with(4).ordered # Exponential backoff 2^2
+ expect(client).to receive(:execute).with(query: query).and_return(response).ordered
+
+ result = service.fetch_ghost_user
+
+ expect(result).to eq(users[0])
+ end
+
+ it 'gives up after MAX_RETRIES attempts' do
+ expect(service).to receive(:sleep).exactly(described_class::MAX_RETRIES - 1).times
+
+ expect(client).to receive(:execute).exactly(described_class::MAX_RETRIES).times.with(query: query)
+ .and_raise(error)
+
+ allow(Gitlab::ErrorTracking).to receive(:track_exception).once
+
+ result = service.fetch_ghost_user
+
+ expect(result).to be_nil
+ end
+ end
+ end
+
+ describe '#set_ghost_user_id' do
+ let(:ghost_user) { { 'id' => 'gid://gitlab/User/210', 'username' => 'ghost', 'type' => 'GHOST' } }
+
+ context 'when ghost user is found' do
+ it 'extracts the ID and caches it' do
+ expect(service).to receive(:fetch_ghost_user).and_return(ghost_user)
+ expect(Gitlab::Cache::Import::Caching).to receive(:write).with(cache_key, '210')
+
+ service.set_ghost_user_id
+ end
+ end
+
+ context 'when ghost user is not found' do
+ it 'returns nil without caching' do
+ expect(service).to receive(:fetch_ghost_user).and_return(nil)
+ expect(Gitlab::Cache::Import::Caching).not_to receive(:write)
+
+ result = service.set_ghost_user_id
+
+ expect(result).to be_nil
+ end
+ end
+
+ context 'when an error occurs' do
+ let(:error) { StandardError.new('Error setting ghost user ID') }
+
+ it 'tracks the exception and returns nil' do
+ expect(service).to receive(:fetch_ghost_user).and_raise(error)
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ error,
+ { message: "Failed to set source ghost user ID", bulk_import_id: bulk_import.id }
+ )
+
+ result = service.set_ghost_user_id
+
+ expect(result).to be_nil
+ end
+ end
+ end
+
+ describe '#cached_ghost_user_id' do
+ it 'returns the cached ghost user ID' do
+ expect(Gitlab::Cache::Import::Caching).to receive(:read).with(cache_key).and_return('210')
+
+ expect(service.cached_ghost_user_id).to eq('210')
+ end
+
+ it 'returns nil when no cached value exists' do
+ expect(Gitlab::Cache::Import::Caching).to receive(:read).with(cache_key).and_return(nil)
+
+ expect(service.cached_ghost_user_id).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/contributions_calendar_spec.rb b/spec/lib/gitlab/contributions_calendar_spec.rb
index d1dbd167d48..4fed718ba5a 100644
--- a/spec/lib/gitlab/contributions_calendar_spec.rb
+++ b/spec/lib/gitlab/contributions_calendar_spec.rb
@@ -128,6 +128,14 @@ RSpec.describe Gitlab::ContributionsCalendar, feature_category: :user_profile do
expect(calendar(contributor).activity_dates[today]).to eq(4)
end
+ it "counts design events" do
+ create_event(public_project, today, 0, :created, :design)
+ create_event(public_project, today, 1, :updated, :design)
+ create_event(public_project, today, 2, :destroyed, :design)
+
+ expect(calendar(contributor).activity_dates[today]).to eq(3)
+ end
+
context "when events fall under different dates depending on the system time zone" do
before do
create_event(public_project, today, 1)
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 4fce3ebee04..5d276127005 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -1304,6 +1304,18 @@ RSpec.describe Gitlab::Diff::File, feature_category: :shared do
end
end
+ describe '#whitespace_only?' do
+ subject(:whitespace_only?) { diff_file.whitespace_only? }
+
+ it 'returns true for non-collapsed empty diffs' do
+ allow(diff_file).to receive(:collapsed?).and_return(false)
+ allow(diff_file).to receive(:diff_lines_for_serializer).and_return(nil)
+ allow(diff_file).to receive(:added_lines).and_return(2)
+ allow(diff_file).to receive(:removed_lines).and_return(2)
+ expect(whitespace_only?).to eq(true)
+ end
+ end
+
describe '#modified_file?' do
subject(:modified_file?) { diff_file.modified_file? }
diff --git a/spec/lib/gitlab/tracking/event_eligibility_checker_spec.rb b/spec/lib/gitlab/tracking/event_eligibility_checker_spec.rb
index e57d9192d3f..3e5227c07f6 100644
--- a/spec/lib/gitlab/tracking/event_eligibility_checker_spec.rb
+++ b/spec/lib/gitlab/tracking/event_eligibility_checker_spec.rb
@@ -10,23 +10,34 @@ RSpec.describe Gitlab::Tracking::EventEligibilityChecker, feature_category: :ser
subject { checker.eligible?(event_name) }
- where(:event_name, :product_usage_data_enabled, :snowplow_enabled, :result) do
- 'perform_completion_worker' | true | false | true
- 'perform_completion_worker' | false | false | true
- 'some_other_event' | true | false | true
- 'some_other_event' | false | true | true
- 'some_other_event' | false | false | false
+ context 'when fully eligible due to produce usage data' do
+ let(:event_name) { 'perform_completion_worker' }
+
+ before do
+ create(:application_setting, snowplow_enabled: false, gitlab_product_usage_data_enabled: true)
+ end
+
+ it { is_expected.to be(true) }
end
- before do
- allow(Gitlab::CurrentSettings).to receive_messages(
- snowplow_enabled?: snowplow_enabled,
- product_usage_data_enabled?: product_usage_data_enabled
- )
- end
+ context 'for all permutations' do
+ where(:event_name, :product_usage_data_enabled, :snowplow_enabled, :result) do
+ 'perform_completion_worker' | true | false | true
+ 'perform_completion_worker' | false | false | true
+ 'some_other_event' | true | false | true
+ 'some_other_event' | false | true | true
+ 'some_other_event' | false | false | false
+ end
- with_them do
- it { is_expected.to eq(result) }
+ before do
+ stub_application_setting(
+ snowplow_enabled?: snowplow_enabled, gitlab_product_usage_data_enabled?: product_usage_data_enabled
+ )
+ end
+
+ with_them do
+ it { is_expected.to eq(result) }
+ end
end
context 'when collect_product_usage_events feature flag is disabled' do
@@ -40,9 +51,8 @@ RSpec.describe Gitlab::Tracking::EventEligibilityChecker, feature_category: :ser
before do
stub_feature_flags(collect_product_usage_events: false)
- allow(Gitlab::CurrentSettings).to receive_messages(
- snowplow_enabled?: snowplow_enabled,
- product_usage_data_enabled?: product_usage_data_enabled
+ stub_application_setting(
+ snowplow_enabled?: snowplow_enabled, gitlab_product_usage_data_enabled?: product_usage_data_enabled
)
end
diff --git a/spec/lib/import/bulk_imports/source_users_mapper_spec.rb b/spec/lib/import/bulk_imports/source_users_mapper_spec.rb
index 9014fe15654..2cd1c048116 100644
--- a/spec/lib/import/bulk_imports/source_users_mapper_spec.rb
+++ b/spec/lib/import/bulk_imports/source_users_mapper_spec.rb
@@ -40,6 +40,10 @@ RSpec.describe Import::BulkImports::SourceUsersMapper, feature_category: :import
subject(:mapper) { described_class.new(context: context) }
+ before do
+ allow(context).to receive(:source_ghost_user_id).and_return('')
+ end
+
describe '#map' do
it 'returns placeholder user id' do
expect(mapper.map['101']).to eq(import_source_user_1.placeholder_user_id)
@@ -56,6 +60,17 @@ RSpec.describe Import::BulkImports::SourceUsersMapper, feature_category: :import
expect(mapper.map['-1']).to eq(nil)
end
end
+
+ context 'when source user is a ghost user' do
+ before do
+ allow(context).to receive(:source_ghost_user_id).and_return('10')
+ end
+
+ it 'returns the destination ghost user ID' do
+ expect(mapper.map['10']).to eq(Users::Internal.ghost.id)
+ expect(mapper.map[10]).to eq(Users::Internal.ghost.id)
+ end
+ end
end
describe '#include?' do
@@ -70,5 +85,34 @@ RSpec.describe Import::BulkImports::SourceUsersMapper, feature_category: :import
expect(mapper.include?(-1)).to eq(false)
end
end
+
+ context 'when a source_user_identifier matches the ghost user ID from source instance' do
+ before do
+ allow(context).to receive(:source_ghost_user_id).and_return('10')
+ end
+
+ it 'returns true for the source ghost user ID without creating a new source user' do
+ expect(mapper.include?(10)).to eq(true)
+ expect(Import::SourceUser.find_by(source_user_identifier: 10)).to be_nil
+ end
+ end
+ end
+
+ describe Import::BulkImports::SourceUsersMapper::MockedHash do
+ let(:source_user_mapper) { instance_double(Import::BulkImports::SourceUsersMapper) }
+ let(:source_ghost_user_id) { '123' }
+ let(:mocked_hash) { described_class.new(source_user_mapper, source_ghost_user_id) }
+
+ describe '#ghost_user_id' do
+ it 'returns the ghost user ID' do
+ expect(mocked_hash.ghost_user_id).to eq(Users::Internal.ghost.id)
+ end
+
+ it 'memoizes the ghost user ID' do
+ expect(Users::Internal).to receive(:ghost).once.and_call_original
+
+ 2.times { mocked_hash.ghost_user_id }
+ end
+ end
end
end
diff --git a/spec/migrations/20250326034951_ensure_gitlab_product_usage_data_enabled_in_service_ping_settings_spec.rb b/spec/migrations/20250326034951_ensure_gitlab_product_usage_data_enabled_in_service_ping_settings_spec.rb
new file mode 100644
index 00000000000..8dd07e0366a
--- /dev/null
+++ b/spec/migrations/20250326034951_ensure_gitlab_product_usage_data_enabled_in_service_ping_settings_spec.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!
+
+RSpec.describe EnsureGitlabProductUsageDataEnabledInServicePingSettings, feature_category: :database do
+ let(:application_settings_table) { table(:application_settings) }
+
+ describe '#up' do
+ context 'when there are no application settings' do
+ it 'does not raise an error' do
+ expect { migrate! }.not_to raise_error
+ end
+ end
+
+ context 'when snowplow_enabled is TRUE' do
+ let!(:application_settings) do
+ application_settings_table.create!(snowplow_enabled: true)
+ end
+
+ it 'sets gitlab_product_usage_data_enabled to FALSE' do
+ expect { migrate! }
+ .to change { application_settings.reload.service_ping_settings }
+ .from({})
+ .to({ 'gitlab_product_usage_data_enabled' => false })
+ end
+
+ context 'and service_ping_settings has existing values' do
+ before do
+ application_settings_table.update!(
+ service_ping_settings: { 'existing_setting' => 'value' }
+ )
+ end
+
+ it 'preserves existing settings and sets gitlab_product_usage_data_enabled to FALSE' do
+ expect { migrate! }
+ .to change { application_settings.reload.service_ping_settings }
+ .from({ 'existing_setting' => 'value' })
+ .to({ 'existing_setting' => 'value', 'gitlab_product_usage_data_enabled' => false })
+ end
+ end
+ end
+
+ context 'when snowplow_enabled is FALSE' do
+ let!(:application_settings) do
+ application_settings_table.create!(snowplow_enabled: false)
+ end
+
+ it 'sets gitlab_product_usage_data_enabled to TRUE' do
+ expect { migrate! }
+ .to change { application_settings.reload.service_ping_settings }
+ .from({})
+ .to({ 'gitlab_product_usage_data_enabled' => true })
+ end
+
+ context 'and service_ping_settings has existing values' do
+ before do
+ application_settings_table.update!(
+ service_ping_settings: { 'existing_setting' => 'value' }
+ )
+ end
+
+ it 'preserves existing settings and sets gitlab_product_usage_data_enabled to TRUE' do
+ expect { migrate! }
+ .to change { application_settings.reload.service_ping_settings }
+ .from({ 'existing_setting' => 'value' })
+ .to({ 'existing_setting' => 'value', 'gitlab_product_usage_data_enabled' => true })
+ end
+ end
+ end
+
+ context 'when working with default snowplow_enabled value' do
+ let!(:application_settings) do
+ application_settings_table.create!(snowplow_enabled: false)
+ end
+
+ it 'sets gitlab_product_usage_data_enabled to TRUE' do
+ expect { migrate! }
+ .to change { application_settings.reload.service_ping_settings }
+ .from({})
+ .to({ 'gitlab_product_usage_data_enabled' => true })
+ end
+ end
+
+ context 'with multiple application settings records' do
+ let!(:application_settings1) do
+ application_settings_table.create!(snowplow_enabled: true)
+ end
+
+ let!(:application_settings2) do
+ application_settings_table.create!(snowplow_enabled: false)
+ end
+
+ it 'updates all records according to their snowplow_enabled value' do
+ migrate!
+
+ expect(application_settings1.reload.service_ping_settings)
+ .to eq({ 'gitlab_product_usage_data_enabled' => false })
+
+ expect(application_settings2.reload.service_ping_settings)
+ .to eq({ 'gitlab_product_usage_data_enabled' => true })
+ end
+ end
+ end
+
+ describe '#down' do
+ it 'is a no-op' do
+ application_settings_table.create!(
+ snowplow_enabled: true,
+ service_ping_settings: { 'gitlab_product_usage_data_enabled' => false }
+ )
+
+ schema_migrate_down!
+
+ expect(application_settings_table.first.service_ping_settings)
+ .to eq({ 'gitlab_product_usage_data_enabled' => false })
+ end
+ end
+end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index fe659e813b7..01de3e5561b 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -1499,6 +1499,100 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do
end
end
+ describe 'snowplow_and_product_usage_data_are_mutually_exclusive validation' do
+ context 'when both snowplow and product usage data tracking are enabled' do
+ before do
+ setting.snowplow_enabled = true
+ setting.gitlab_product_usage_data_enabled = true
+ end
+
+ it 'is invalid' do
+ expect(setting).to be_invalid
+ expect(setting.errors[:base]).to include(
+ /Snowplow tracking and Product event tracking cannot be enabled at the same time/
+ )
+ end
+ end
+
+ context 'when only snowplow tracking is enabled' do
+ before do
+ setting.snowplow_enabled = true
+ setting.gitlab_product_usage_data_enabled = false
+ end
+
+ it 'is valid' do
+ expect(setting).to be_valid
+ end
+ end
+
+ context 'when only product usage data tracking is enabled' do
+ before do
+ setting.snowplow_enabled = false
+ setting.gitlab_product_usage_data_enabled = true
+ end
+
+ it 'is valid' do
+ expect(setting).to be_valid
+ end
+ end
+
+ context 'when neither snowplow nor product usage data tracking is enabled' do
+ before do
+ setting.snowplow_enabled = false
+ setting.gitlab_product_usage_data_enabled = false
+ end
+
+ it 'is valid' do
+ expect(setting).to be_valid
+ end
+ end
+
+ context 'when changing snowplow_enabled' do
+ before do
+ setting.gitlab_product_usage_data_enabled = true
+ end
+
+ it 'is invalid when enabling snowplow while product usage data is enabled' do
+ setting.snowplow_enabled = true
+
+ expect(setting).to be_invalid
+ expect(setting.errors[:base]).to include(
+ /Snowplow tracking and Product event tracking cannot be enabled at the same time/
+ )
+ end
+ end
+
+ context 'when changing gitlab_product_usage_data_enabled' do
+ before do
+ setting.snowplow_enabled = true
+ end
+
+ it 'is invalid when enabling product usage data while snowplow is enabled' do
+ setting.gitlab_product_usage_data_enabled = true
+
+ expect(setting).to be_invalid
+ expect(setting.errors[:base]).to include(
+ /Snowplow tracking and Product event tracking cannot be enabled at the same time/
+ )
+ end
+ end
+
+ context 'when changing an unrelated attribute' do
+ before do
+ setting.snowplow_enabled = true
+ setting.gitlab_product_usage_data_enabled = true
+ setting.save!(validate: false)
+ end
+
+ it 'skips the validation and allows saving' do
+ setting.home_page_url = 'https://example.com'
+
+ expect(setting.save).to be true
+ expect(setting.reload.home_page_url).to eq('https://example.com')
+ end
+ end
+ end
+
describe '#runners_registration_token' do
context 'when allowed by application setting' do
before do
diff --git a/spec/models/ci/instance_variable_spec.rb b/spec/models/ci/instance_variable_spec.rb
index 9a6a78ee5f4..89d8c37d0d7 100644
--- a/spec/models/ci/instance_variable_spec.rb
+++ b/spec/models/ci/instance_variable_spec.rb
@@ -11,6 +11,10 @@ RSpec.describe Ci::InstanceVariable do
it { is_expected.to validate_uniqueness_of(:key).with_message(/\(\w+\) has already been taken/) }
it { is_expected.to validate_length_of(:value).is_at_most(10_000).with_message(/The value of the provided variable exceeds the 10000 character limit/) }
+ it_behaves_like 'encrypted attribute', :value, :db_key_base_32 do
+ let(:record) { subject }
+ end
+
it_behaves_like 'includes Limitable concern' do
subject { build(:ci_instance_variable) }
end
diff --git a/spec/models/ci/job_variable_spec.rb b/spec/models/ci/job_variable_spec.rb
index bb10d8c1c99..2fb4e489428 100644
--- a/spec/models/ci/job_variable_spec.rb
+++ b/spec/models/ci/job_variable_spec.rb
@@ -13,6 +13,10 @@ RSpec.describe Ci::JobVariable, feature_category: :continuous_integration do
it { is_expected.to validate_presence_of(:project_id) }
end
+ it_behaves_like 'encrypted attribute', :value, :db_key_base_32 do
+ let(:record) { create(:ci_job_variable) }
+ end
+
describe 'partitioning' do
let(:job_variable) { build(:ci_job_variable, job: ci_build) }
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index 2e1304e1287..ca82ab1a594 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -31,6 +31,10 @@ RSpec.describe Ci::Trigger, feature_category: :continuous_integration do
end
end
+ it_behaves_like 'encrypted attribute', :encrypted_token_tmp, :db_key_base_32 do
+ let(:record) { create(:ci_trigger_without_token) }
+ end
+
describe '#last_used' do
let_it_be(:project) { create :project }
let_it_be_with_refind(:trigger) { create(:ci_trigger, project: project) }
diff --git a/spec/models/concerns/ci/has_variable_spec.rb b/spec/models/concerns/ci/has_variable_spec.rb
index a91ac622368..16958aa3237 100644
--- a/spec/models/concerns/ci/has_variable_spec.rb
+++ b/spec/models/concerns/ci/has_variable_spec.rb
@@ -11,6 +11,10 @@ RSpec.describe Ci::HasVariable, feature_category: :continuous_integration do
it { is_expected.not_to allow_value('foo bar').for(:key) }
it { is_expected.not_to allow_value('foo/bar').for(:key) }
+ it_behaves_like 'encrypted attribute', :value, :db_key_base do
+ let(:record) { subject }
+ end
+
describe 'scopes' do
describe '.by_key' do
let!(:matching_variable) { create(:ci_variable, key: 'example') }
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 14b45eaf19e..10ef33e8182 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -191,14 +191,12 @@ RSpec.describe Event, feature_category: :user_profile do
let!(:push_event) { create_push_event(project, project.owner) }
let!(:comment_event) { create(:event, :commented, project: project) }
- before do
- create(:design_event, project: project) # should not be in scope
- end
+ let!(:design_event) { create(:design_event, project: project) }
it 'returns events for MergeRequest, Issue, WorkItem and push, comment events' do
expect(described_class.contributions).to contain_exactly(
*merge_request_events, *issue_events, work_item_event,
- push_event, comment_event
+ push_event, comment_event, design_event
)
end
end
diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb
index 85ffc8d8909..030a256df60 100644
--- a/spec/models/personal_access_token_spec.rb
+++ b/spec/models/personal_access_token_spec.rb
@@ -399,6 +399,12 @@ RSpec.describe PersonalAccessToken, feature_category: :system_access do
expect(personal_access_token).to be_valid
end
+
+ it 'is valid with expiration date set beyond 400 days' do
+ personal_access_token.expires_at = 2.years.from_now
+
+ expect(personal_access_token).to be_valid
+ end
end
end
diff --git a/spec/policies/application_setting_policy_spec.rb b/spec/policies/application_setting_policy_spec.rb
index 1c06a3d88fe..08dc3a972b2 100644
--- a/spec/policies/application_setting_policy_spec.rb
+++ b/spec/policies/application_setting_policy_spec.rb
@@ -3,12 +3,12 @@
require 'spec_helper'
RSpec.describe ApplicationSettingPolicy do
- let(:current_user) { create(:user) }
- let(:user) { create(:user) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:user) { create(:user) }
subject { described_class.new(current_user, [user]) }
- describe 'update_runners_registration_token' do
+ describe 'runner registration token settings' do
let(:allow_runner_registration_token) { true }
before do
@@ -18,33 +18,39 @@ RSpec.describe ApplicationSettingPolicy do
context 'when anonymous' do
let(:current_user) { nil }
+ it { is_expected.not_to be_allowed(:read_runners_registration_token) }
it { is_expected.not_to be_allowed(:update_runners_registration_token) }
end
context 'regular user' do
+ it { is_expected.not_to be_allowed(:read_runners_registration_token) }
it { is_expected.not_to be_allowed(:update_runners_registration_token) }
end
context 'when external' do
- let(:current_user) { build(:user, :external) }
+ let_it_be(:current_user) { build(:user, :external) }
+ it { is_expected.not_to be_allowed(:read_runners_registration_token) }
it { is_expected.not_to be_allowed(:update_runners_registration_token) }
end
context 'admin' do
- let(:current_user) { create(:admin) }
+ let_it_be(:current_user) { create(:admin) }
context 'when admin mode is enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:read_runners_registration_token) }
it { is_expected.to be_allowed(:update_runners_registration_token) }
context 'with registration tokens disabled' do
let(:allow_runner_registration_token) { false }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
context 'when admin mode is disabled' do
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 6471e3d8cfb..aadb3612ae9 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -1412,7 +1412,7 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
end
end
- describe 'update_runners_registration_token' do
+ describe 'runner registration token settings' do
let(:allow_runner_registration_token) { true }
before do
@@ -1423,16 +1423,19 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:read_runners_registration_token) }
it { is_expected.to be_allowed(:update_runners_registration_token) }
context 'with registration tokens disabled' do
let(:allow_runner_registration_token) { false }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
context 'when admin mode is disabled' do
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
@@ -1440,11 +1443,13 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
context 'with owner' do
let(:current_user) { owner }
+ it { is_expected.to be_allowed(:read_runners_registration_token) }
it { is_expected.to be_allowed(:update_runners_registration_token) }
context 'with registration tokens disabled' do
let(:allow_runner_registration_token) { false }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
@@ -1452,36 +1457,42 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
context 'with maintainer' do
let(:current_user) { maintainer }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
context 'with reporter' do
let(:current_user) { reporter }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
context 'with planner' do
let(:current_user) { planner }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
context 'with guest' do
let(:current_user) { guest }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
context 'with non member' do
let(:current_user) { create(:user) }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
context 'with anonymous' do
let(:current_user) { nil }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index e526f1e79a2..6885ed44339 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -3162,7 +3162,7 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
end
end
- describe 'update_runners_registration_token' do
+ describe 'runner registration token settings' do
# Override project with a version with namespace_settings
let(:project) { project_with_runner_registration_token }
let(:allow_runner_registration_token) { true }
@@ -3174,6 +3174,7 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
context 'when anonymous' do
let(:current_user) { anonymous }
+ it { is_expected.not_to be_allowed(:read_runners_registration_token) }
it { is_expected.not_to be_allowed(:update_runners_registration_token) }
end
@@ -3181,16 +3182,19 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
let(:current_user) { create(:admin) }
context 'when admin mode is enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:read_runners_registration_token) }
it { is_expected.to be_allowed(:update_runners_registration_token) }
context 'with registration tokens disabled' do
let(:allow_runner_registration_token) { false }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
context 'when admin mode is disabled' do
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
@@ -3199,6 +3203,7 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
context role do
let(:current_user) { send(role) }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
@@ -3207,11 +3212,13 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
context role do
let(:current_user) { send(role) }
+ it { is_expected.to be_allowed(:read_runners_registration_token) }
it { is_expected.to be_allowed(:update_runners_registration_token) }
context 'with registration tokens disabled' do
let(:allow_runner_registration_token) { false }
+ it { is_expected.to be_disallowed(:read_runners_registration_token) }
it { is_expected.to be_disallowed(:update_runners_registration_token) }
end
end
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 3d515c5df46..b5120e687a5 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -543,7 +543,9 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
end
it "allows updating the settings" do
- put api("/application/settings", admin), params: settings
+ put api("/application/settings", admin), params: settings.merge({
+ gitlab_product_usage_data_enabled: false
+ })
expect(response).to have_gitlab_http_status(:ok)
settings.each do |attribute, value|
diff --git a/spec/requests/search_controller_spec.rb b/spec/requests/search_controller_spec.rb
index 18bbe60b716..70f8dbb3900 100644
--- a/spec/requests/search_controller_spec.rb
+++ b/spec/requests/search_controller_spec.rb
@@ -205,6 +205,10 @@ RSpec.describe SearchController, type: :request, feature_category: :global_searc
let(:params) { nil }
+ before do
+ stub_application_setting(global_search_block_anonymous_searches_enabled: true)
+ end
+
context 'when user is not signed-in' do
it { is_expected.to redirect_to(new_user_session_path) }
end
diff --git a/spec/services/bulk_imports/process_service_spec.rb b/spec/services/bulk_imports/process_service_spec.rb
index 6e62874f115..135b89eaebc 100644
--- a/spec/services/bulk_imports/process_service_spec.rb
+++ b/spec/services/bulk_imports/process_service_spec.rb
@@ -3,6 +3,14 @@
require 'spec_helper'
RSpec.describe BulkImports::ProcessService, feature_category: :importers do
+ let(:source_internal_user_finder) { instance_double(BulkImports::SourceInternalUserFinder) }
+
+ before do
+ allow(BulkImports::SourceInternalUserFinder).to receive(:new)
+ .and_return(source_internal_user_finder)
+ allow(source_internal_user_finder).to receive(:set_ghost_user_id)
+ end
+
describe '#execute' do
let_it_be_with_reload(:bulk_import) { create(:bulk_import) }
@@ -183,6 +191,13 @@ RSpec.describe BulkImports::ProcessService, feature_category: :importers do
expect(entity_2.trackers).not_to be_empty
end
+ it 'cached source ghost user id' do
+ create(:bulk_import_entity, :created, bulk_import: bulk_import)
+ expect(source_internal_user_finder).to receive(:set_ghost_user_id).once
+
+ subject.execute
+ end
+
context 'when there are created entities to process' do
before do
stub_const("#{described_class}::DEFAULT_BATCH_SIZE", 1)
diff --git a/yarn.lock b/yarn.lock
index 883f55606c9..61dbdb0be80 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1441,10 +1441,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.126.0.tgz#1c0bb95c11de808b78afd05dc95aca258c3b39f0"
integrity sha512-7X8uzitNn7NDcVy+FVCw8npMNEUpLGHTO5Z+BJZqVILj/FD+0WveYdPxAEVa9hXYQn5qXWM0ZAknzB9LM6Id8w==
-"@gitlab/ui@112.2.1":
- version "112.2.1"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-112.2.1.tgz#be5fa326f300dc61a48d1369c816f6797096b5ee"
- integrity sha512-n4NGntUoiTMK0e51bLq5HzRT5Ncad8PtDujT9nb5n2ptNPVnzl94yJrH8e8X6SXbNDfJd827NtmdPNfZbJPgsQ==
+"@gitlab/ui@112.2.2":
+ version "112.2.2"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-112.2.2.tgz#234c3d2906b27062ba06ae097feba5dcc3ba7e65"
+ integrity sha512-Uh5RmpZKJ4pv6d/QJwJNyGeR6CTaqlQHmtZy8FQfqLH5DcIjvNwlASYhArb6bH8dQbSfLlTCctzotQ51RBRTjg==
dependencies:
"@floating-ui/dom" "1.4.3"
echarts "^5.3.2"