Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									013d339e9a
								
							
						
					
					
						commit
						8212b8fd70
					
				|  | @ -587,11 +587,13 @@ lib/gitlab/checks/** | |||
| /doc/administration/package_information/ @axil | ||||
| /doc/administration/packages/ @marcel.amirault | ||||
| /doc/administration/packages/index.md @phillipwells | ||||
| /doc/administration/pages/ @msedlakjakubowski | ||||
| /doc/administration/polling.md @axil | ||||
| /doc/administration/postgresql/ @lciutacu | ||||
| /doc/administration/raketasks/ @axil | ||||
| /doc/administration/raketasks/ldap.md @jglassman1 | ||||
| /doc/administration/raketasks/praefect.md @eread | ||||
| /doc/administration/raketasks/tokens/ @jglassman1 | ||||
| /doc/administration/read_only_gitlab.md @axil | ||||
| /doc/administration/redis/ @axil | ||||
| /doc/administration/reference_architectures/ @axil | ||||
|  | @ -646,6 +648,7 @@ lib/gitlab/checks/** | |||
| /doc/administration/troubleshooting/ @axil | ||||
| /doc/administration/uploads.md @axil | ||||
| /doc/administration/user_settings.md @jglassman1 | ||||
| /doc/administration/wikis/ @msedlakjakubowski | ||||
| /doc/api/access_requests.md @jglassman1 | ||||
| /doc/api/admin_sidekiq_queues.md @axil | ||||
| /doc/api/api_resources.md @eread | ||||
|  | @ -706,6 +709,7 @@ lib/gitlab/checks/** | |||
| /doc/api/group_releases.md @phillipwells | ||||
| /doc/api/group_repository_storage_moves.md @ashrafkhamis | ||||
| /doc/api/group_ssh_certificates.md @msedlakjakubowski | ||||
| /doc/api/group_wikis.md @msedlakjakubowski | ||||
| /doc/api/groups.md @lciutacu | ||||
| /doc/api/import.md @eread | ||||
| /doc/api/index.md @eread | ||||
|  | @ -738,6 +742,8 @@ lib/gitlab/checks/** | |||
| /doc/api/openapi/ @eread | ||||
| /doc/api/packages.md @phillipwells | ||||
| /doc/api/packages/ @phillipwells | ||||
| /doc/api/pages.md @msedlakjakubowski | ||||
| /doc/api/pages_domains.md @msedlakjakubowski | ||||
| /doc/api/personal_access_tokens.md @jglassman1 | ||||
| /doc/api/pipeline_schedules.md @marcel.amirault @lyspin | ||||
| /doc/api/pipeline_triggers.md @marcel.amirault @lyspin | ||||
|  | @ -800,6 +806,7 @@ lib/gitlab/checks/** | |||
| /doc/api/vulnerabilities.md @rdickenson | ||||
| /doc/api/vulnerability_exports.md @rdickenson | ||||
| /doc/api/vulnerability_findings.md @rdickenson | ||||
| /doc/api/wikis.md @msedlakjakubowski | ||||
| /doc/ci/ @marcel.amirault @lyspin | ||||
| /doc/ci/chatops/ @phillipwells | ||||
| /doc/ci/cloud_deployment/ @phillipwells | ||||
|  | @ -899,6 +906,7 @@ lib/gitlab/checks/** | |||
| /doc/tutorials/dependency_scanning.md @rdickenson @phillipwells | ||||
| /doc/tutorials/export_sbom.md @rdickenson @phillipwells | ||||
| /doc/tutorials/fuzz_testing/ @rdickenson @phillipwells | ||||
| /doc/tutorials/hugo/ @msedlakjakubowski | ||||
| /doc/tutorials/idea_management/ @msedlakjakubowski | ||||
| /doc/tutorials/install_gitlab_single_node/ @axil | ||||
| /doc/tutorials/issue_triage/ @msedlakjakubowski | ||||
|  | @ -914,6 +922,7 @@ lib/gitlab/checks/** | |||
| /doc/tutorials/update_commit_messages/ @msedlakjakubowski | ||||
| /doc/tutorials/website_project_with_analytics/ @lciutacu | ||||
| /doc/update/ @axil | ||||
| /doc/user/ @msedlakjakubowski | ||||
| /doc/user/analytics/ @lciutacu | ||||
| /doc/user/analytics/ci_cd_analytics.md @phillipwells | ||||
| /doc/user/application_security/ @rdickenson @phillipwells | ||||
|  | @ -923,13 +932,11 @@ lib/gitlab/checks/** | |||
| /doc/user/application_security/security_dashboard/ @rdickenson | ||||
| /doc/user/application_security/vulnerabilities/ @rdickenson | ||||
| /doc/user/application_security/vulnerability_report/ @rdickenson | ||||
| /doc/user/asciidoc.md @msedlakjakubowski | ||||
| /doc/user/clusters/ @phillipwells | ||||
| /doc/user/compliance/ @eread | ||||
| /doc/user/compliance/license_approval_policies.md @rdickenson | ||||
| /doc/user/compliance/license_scanning_of_cyclonedx_files/ @rdickenson @phillipwells | ||||
| /doc/user/discussions/ @aqualls | ||||
| /doc/user/emoji_reactions.md @msedlakjakubowski | ||||
| /doc/user/enterprise_user/ @jglassman1 | ||||
| /doc/user/get_started/get_started_managing_code.md @msedlakjakubowski | ||||
| /doc/user/get_started/get_started_managing_infrastructure.md @phillipwells | ||||
|  | @ -962,9 +969,7 @@ lib/gitlab/checks/** | |||
| /doc/user/infrastructure/ @phillipwells | ||||
| /doc/user/infrastructure/clusters/manage/management_project_applications/ @phillipwells | ||||
| /doc/user/infrastructure/clusters/manage/management_project_applications/runner.md @ashrafkhamis | ||||
| /doc/user/markdown.md @msedlakjakubowski | ||||
| /doc/user/namespace/ @lciutacu | ||||
| /doc/user/okrs.md @msedlakjakubowski | ||||
| /doc/user/operations_dashboard/ @phillipwells | ||||
| /doc/user/organization/ @lciutacu | ||||
| /doc/user/packages/ @phillipwells | ||||
|  | @ -976,7 +981,6 @@ lib/gitlab/checks/** | |||
| /doc/user/profile/achievements.md @lciutacu | ||||
| /doc/user/profile/comment_templates.md @aqualls | ||||
| /doc/user/profile/contributions_calendar.md @lciutacu | ||||
| /doc/user/project/ @msedlakjakubowski | ||||
| /doc/user/project/badges.md @lciutacu | ||||
| /doc/user/project/clusters/ @phillipwells | ||||
| /doc/user/project/code_intelligence.md @aqualls | ||||
|  | @ -1028,11 +1032,9 @@ lib/gitlab/checks/** | |||
| /doc/user/public_access.md @lciutacu | ||||
| /doc/user/reserved_names.md @lciutacu | ||||
| /doc/user/search/ @ashrafkhamis | ||||
| /doc/user/snippets.md @msedlakjakubowski | ||||
| /doc/user/ssh.md @jglassman1 | ||||
| /doc/user/ssh_troubleshooting.md @jglassman1 | ||||
| /doc/user/storage_management_automation.md @fneill | ||||
| /doc/user/tasks.md @msedlakjakubowski | ||||
| /doc/user/usage_quotas.md @fneill | ||||
| /doc/user/workspace/ @ashrafkhamis | ||||
| # End rake-managed-docs-block | ||||
|  |  | |||
							
								
								
									
										3
									
								
								Gemfile
								
								
								
								
							
							
						
						
									
										3
									
								
								Gemfile
								
								
								
								
							|  | @ -407,7 +407,8 @@ gem 'webrick', '~> 1.8.1', require: false # rubocop:todo Gemfile/MissingFeatureC | |||
| gem 'prometheus-client-mmap', '~> 1.1', '>= 1.1.1', require: 'prometheus/client' # rubocop:todo Gemfile/MissingFeatureCategory | ||||
| 
 | ||||
| # Event-driven reactor for Ruby | ||||
| gem 'async', '~> 2.12.1' # rubocop:disable Gemfile/MissingFeatureCategory -- This is general utility gem | ||||
| # Required manually in config/initializers/require_async_gem | ||||
| gem 'async', '~> 2.12.1', require: false # rubocop:disable Gemfile/MissingFeatureCategory -- This is general utility gem | ||||
| 
 | ||||
| # OpenTelemetry | ||||
| group :opentelemetry do | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils'; | |||
| import { waitForElement } from '~/lib/utils/dom_utils'; | ||||
| import findAndFollowLink from '~/lib/utils/navigation_utility'; | ||||
| import { refreshCurrentPage } from '~/lib/utils/url_utility'; | ||||
| import { helpCenterState } from '~/super_sidebar/constants'; | ||||
| import { duoChatGlobalState } from '~/super_sidebar/constants'; | ||||
| import { | ||||
|   keysFor, | ||||
|   TOGGLE_KEYBOARD_SHORTCUTS_DIALOG, | ||||
|  | @ -225,7 +225,7 @@ export default class Shortcuts { | |||
| 
 | ||||
|   static onToggleDuoChat(e) { | ||||
|     e.preventDefault(); | ||||
|     helpCenterState.showTanukiBotChatDrawer = !helpCenterState.showTanukiBotChatDrawer; | ||||
|     duoChatGlobalState.isShown = !duoChatGlobalState.isShown; | ||||
|   } | ||||
| 
 | ||||
|   static onTogglePerfBar(e) { | ||||
|  |  | |||
|  | @ -60,6 +60,7 @@ export default { | |||
|       modelData: null, | ||||
|       versionData: null, | ||||
|       markdownDocPath: helpPagePath('user/markdown'), | ||||
|       markdownEditorRestrictedToolBarItems: ['full-screen'], | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|  | @ -242,7 +243,7 @@ export default { | |||
|       :title="$options.modal.title" | ||||
|       :action-primary="actionPrimary" | ||||
|       :action-secondary="$options.modal.actionSecondary" | ||||
|       size="sm" | ||||
|       size="lg" | ||||
|       @primary="create" | ||||
|       @secondary="resetModal" | ||||
|     > | ||||
|  | @ -284,6 +285,7 @@ export default { | |||
|             :markdown-docs-path="markdownDocPath" | ||||
|             :disable-attachments="disableAttachments" | ||||
|             :placeholder="$options.modal.nameDescriptionPlaceholder" | ||||
|             :restricted-tool-bar-items="markdownEditorRestrictedToolBarItems" | ||||
|             @input="setDescription" | ||||
|           /> | ||||
|         </gl-form-group> | ||||
|  |  | |||
|  | @ -173,7 +173,7 @@ export default { | |||
|       :title="$options.modal.title" | ||||
|       :action-primary="actionPrimary" | ||||
|       :action-secondary="$options.modal.actionSecondary" | ||||
|       size="sm" | ||||
|       size="lg" | ||||
|       @primary="create" | ||||
|       @secondary="resetModal" | ||||
|     > | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ import { FORUM_URL, PROMO_URL } from 'jh_else_ce/lib/utils/url_utility'; | |||
| import { __ } from '~/locale'; | ||||
| import { STORAGE_KEY } from '~/whats_new/utils/notification'; | ||||
| import Tracking from '~/tracking'; | ||||
| import { DROPDOWN_Y_OFFSET, HELP_MENU_TRACKING_DEFAULTS, helpCenterState } from '../constants'; | ||||
| import { DROPDOWN_Y_OFFSET, HELP_MENU_TRACKING_DEFAULTS, duoChatGlobalState } from '../constants'; | ||||
| 
 | ||||
| export default { | ||||
|   components: { | ||||
|  | @ -39,7 +39,7 @@ export default { | |||
|   data() { | ||||
|     return { | ||||
|       showWhatsNewNotification: this.shouldShowWhatsNewNotification(), | ||||
|       helpCenterState, | ||||
|       duoChatGlobalState, | ||||
|       toggleWhatsNewDrawer: null, | ||||
|     }; | ||||
|   }, | ||||
|  |  | |||
|  | @ -21,8 +21,9 @@ export const sidebarState = Vue.observable({ | |||
|   wasHoverPeek: false, | ||||
| }); | ||||
| 
 | ||||
| export const helpCenterState = Vue.observable({ | ||||
|   showTanukiBotChatDrawer: false, | ||||
| export const duoChatGlobalState = Vue.observable({ | ||||
|   commands: [], | ||||
|   isShown: false, | ||||
| }); | ||||
| 
 | ||||
| export const SUPER_SIDEBAR_PEEK_OPEN_DELAY = 200; | ||||
|  |  | |||
|  | @ -119,6 +119,11 @@ export default { | |||
|       required: false, | ||||
|       default: () => ({}), | ||||
|     }, | ||||
|     restrictedToolBarItems: { | ||||
|       type: Array, | ||||
|       required: false, | ||||
|       default: () => [], | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     const editingMode = | ||||
|  | @ -139,7 +144,9 @@ export default { | |||
|       return this.autofocus && !this.autofocused ? 'end' : false; | ||||
|     }, | ||||
|     markdownFieldRestrictedToolBarItems() { | ||||
|       return this.disableAttachments ? ['attach-file'] : []; | ||||
|       const restrictAttachments = this.disableAttachments ? ['attach-file'] : []; | ||||
| 
 | ||||
|       return [...this.restrictedToolBarItems, ...restrictAttachments]; | ||||
|     }, | ||||
|   }, | ||||
|   watch: { | ||||
|  |  | |||
|  | @ -187,10 +187,15 @@ class GraphqlController < ApplicationController | |||
|   def disable_query_limiting | ||||
|     return unless Gitlab::QueryLimiting.enabled_for_env? | ||||
| 
 | ||||
|     disable_issue = request.headers[DISABLE_SQL_QUERY_LIMIT_HEADER] | ||||
|     return unless disable_issue | ||||
|     disable_reference = request.headers[DISABLE_SQL_QUERY_LIMIT_HEADER] | ||||
|     return unless disable_reference | ||||
| 
 | ||||
|     Gitlab::QueryLimiting.disable!(disable_issue) | ||||
|     disable_issue, new_threshold = disable_reference.split(';') | ||||
|     if new_threshold | ||||
|       Gitlab::QueryLimiting.disable!(disable_issue, new_threshold: new_threshold&.to_i) | ||||
|     else | ||||
|       Gitlab::QueryLimiting.disable!(disable_issue) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def set_user_last_activity | ||||
|  |  | |||
|  | @ -21,7 +21,10 @@ class Import::GitlabGroupsController < ApplicationController | |||
|       ) | ||||
|       .with_defaults(organization_id: Current.organization_id) | ||||
| 
 | ||||
|     response = ::Groups::CreateService.new(current_user, group_data).execute | ||||
|     response = Namespace.with_disabled_organization_validation do | ||||
|       ::Groups::CreateService.new(current_user, group_data).execute | ||||
|     end | ||||
| 
 | ||||
|     group = response[:group] | ||||
| 
 | ||||
|     if response.success? | ||||
|  |  | |||
|  | @ -22,7 +22,9 @@ class Ldap::OmniauthCallbacksController < OmniauthCallbacksController | |||
|       return admin_mode_flow(Gitlab::Auth::Ldap::User) if current_user_mode.admin_mode_requested? | ||||
|     end | ||||
| 
 | ||||
|     sign_in_user_flow(Gitlab::Auth::Ldap::User) | ||||
|     Namespace.with_disabled_organization_validation do | ||||
|       sign_in_user_flow(Gitlab::Auth::Ldap::User) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   define_providers! | ||||
|  |  | |||
|  | @ -165,7 +165,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController | |||
|         redirect_identity_exists | ||||
|       end | ||||
|     else | ||||
|       sign_in_user_flow(auth_module::User) | ||||
|       Namespace.with_disabled_organization_validation do | ||||
|         sign_in_user_flow(auth_module::User) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,14 +38,15 @@ class RegistrationsController < Devise::RegistrationsController | |||
|   def create | ||||
|     set_resource_fields | ||||
| 
 | ||||
|     super do |new_user| | ||||
|       if new_user.persisted? | ||||
|         after_successful_create_hook(new_user) | ||||
|       else | ||||
|         track_error(new_user) | ||||
|     Namespace.with_disabled_organization_validation do | ||||
|       super do |new_user| | ||||
|         if new_user.persisted? | ||||
|           after_successful_create_hook(new_user) | ||||
|         else | ||||
|           track_error(new_user) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     # Devise sets a flash message on both successful & failed signups, | ||||
|     # but we only want to show a message if the resource is blocked by a pending approval. | ||||
|     flash[:notice] = nil unless allow_flash_content?(resource) | ||||
|  |  | |||
|  | @ -7,79 +7,42 @@ module Types | |||
| 
 | ||||
|       graphql_name 'WorkItemWidget' | ||||
| 
 | ||||
|       field :type, ::Types::WorkItems::WidgetTypeEnum, | ||||
|       field :type, | ||||
|         ::Types::WorkItems::WidgetTypeEnum, | ||||
|         null: true, | ||||
|         description: 'Widget type.' | ||||
| 
 | ||||
|       ORPHAN_TYPES = [ | ||||
|         ::Types::WorkItems::Widgets::DescriptionType, | ||||
|         ::Types::WorkItems::Widgets::HierarchyType, | ||||
|         ::Types::WorkItems::Widgets::LabelsType, | ||||
|         ::Types::WorkItems::Widgets::AssigneesType, | ||||
|         ::Types::WorkItems::Widgets::StartAndDueDateType, | ||||
|         ::Types::WorkItems::Widgets::MilestoneType, | ||||
|         ::Types::WorkItems::Widgets::NotesType, | ||||
|         ::Types::WorkItems::Widgets::NotificationsType, | ||||
|         ::Types::WorkItems::Widgets::CurrentUserTodosType, | ||||
|         ::Types::WorkItems::Widgets::AwardEmojiType, | ||||
|         ::Types::WorkItems::Widgets::LinkedItemsType, | ||||
|         ::Types::WorkItems::Widgets::ParticipantsType, | ||||
|         ::Types::WorkItems::Widgets::TimeTracking::TimeTrackingType, | ||||
|         ::Types::WorkItems::Widgets::DesignsType, | ||||
|         ::Types::WorkItems::Widgets::DevelopmentType, | ||||
|         ::Types::WorkItems::Widgets::CrmContactsType | ||||
|       ].freeze | ||||
| 
 | ||||
|       def self.ce_orphan_types | ||||
|         ORPHAN_TYPES | ||||
|       end | ||||
| 
 | ||||
|       # Whenever a new widget is added make sure to update the spec to avoid N + 1 queries in | ||||
|       # spec/requests/api/graphql/project/work_items_spec.rb and add the necessary preloads | ||||
|       # in app/graphql/resolvers/work_items_resolver.rb | ||||
|       # | ||||
|       # rubocop:disable Metrics/CyclomaticComplexity -- we'll have a lot of widgets to handle in the WidgetInterface | ||||
|       def self.resolve_type(object, context) | ||||
|         case object | ||||
|         when ::WorkItems::Widgets::Description | ||||
|           ::Types::WorkItems::Widgets::DescriptionType | ||||
|         when ::WorkItems::Widgets::Hierarchy | ||||
|           ::Types::WorkItems::Widgets::HierarchyType | ||||
|         when ::WorkItems::Widgets::Assignees | ||||
|           ::Types::WorkItems::Widgets::AssigneesType | ||||
|         when ::WorkItems::Widgets::Labels | ||||
|           ::Types::WorkItems::Widgets::LabelsType | ||||
|         when ::WorkItems::Widgets::StartAndDueDate | ||||
|           ::Types::WorkItems::Widgets::StartAndDueDateType | ||||
|         when ::WorkItems::Widgets::Milestone | ||||
|           ::Types::WorkItems::Widgets::MilestoneType | ||||
|         when ::WorkItems::Widgets::Notes | ||||
|           ::Types::WorkItems::Widgets::NotesType | ||||
|         when ::WorkItems::Widgets::Notifications | ||||
|           ::Types::WorkItems::Widgets::NotificationsType | ||||
|         when ::WorkItems::Widgets::CurrentUserTodos | ||||
|           ::Types::WorkItems::Widgets::CurrentUserTodosType | ||||
|         when ::WorkItems::Widgets::AwardEmoji | ||||
|           ::Types::WorkItems::Widgets::AwardEmojiType | ||||
|         when ::WorkItems::Widgets::LinkedItems | ||||
|           ::Types::WorkItems::Widgets::LinkedItemsType | ||||
|         when ::WorkItems::Widgets::Participants | ||||
|           ::Types::WorkItems::Widgets::ParticipantsType | ||||
|         when ::WorkItems::Widgets::TimeTracking | ||||
|           ::Types::WorkItems::Widgets::TimeTracking::TimeTrackingType | ||||
|         when ::WorkItems::Widgets::Designs | ||||
|           ::Types::WorkItems::Widgets::DesignsType | ||||
|         when ::WorkItems::Widgets::Development | ||||
|           ::Types::WorkItems::Widgets::DevelopmentType | ||||
|         when ::WorkItems::Widgets::CrmContacts | ||||
|           ::Types::WorkItems::Widgets::CrmContactsType | ||||
|         else | ||||
|           raise "Unknown GraphQL type for widget #{object}" | ||||
|         end | ||||
|       end | ||||
|       # rubocop:enable Metrics/CyclomaticComplexity | ||||
|       TYPE_MAPPINGS = { | ||||
|         ::WorkItems::Widgets::Description => ::Types::WorkItems::Widgets::DescriptionType, | ||||
|         ::WorkItems::Widgets::Hierarchy => ::Types::WorkItems::Widgets::HierarchyType, | ||||
|         ::WorkItems::Widgets::Labels => ::Types::WorkItems::Widgets::LabelsType, | ||||
|         ::WorkItems::Widgets::Assignees => ::Types::WorkItems::Widgets::AssigneesType, | ||||
|         ::WorkItems::Widgets::StartAndDueDate => ::Types::WorkItems::Widgets::StartAndDueDateType, | ||||
|         ::WorkItems::Widgets::Milestone => ::Types::WorkItems::Widgets::MilestoneType, | ||||
|         ::WorkItems::Widgets::Notes => ::Types::WorkItems::Widgets::NotesType, | ||||
|         ::WorkItems::Widgets::Notifications => ::Types::WorkItems::Widgets::NotificationsType, | ||||
|         ::WorkItems::Widgets::CurrentUserTodos => ::Types::WorkItems::Widgets::CurrentUserTodosType, | ||||
|         ::WorkItems::Widgets::AwardEmoji => ::Types::WorkItems::Widgets::AwardEmojiType, | ||||
|         ::WorkItems::Widgets::LinkedItems => ::Types::WorkItems::Widgets::LinkedItemsType, | ||||
|         ::WorkItems::Widgets::Participants => ::Types::WorkItems::Widgets::ParticipantsType, | ||||
|         ::WorkItems::Widgets::TimeTracking => ::Types::WorkItems::Widgets::TimeTracking::TimeTrackingType, | ||||
|         ::WorkItems::Widgets::Designs => ::Types::WorkItems::Widgets::DesignsType, | ||||
|         ::WorkItems::Widgets::Development => ::Types::WorkItems::Widgets::DevelopmentType, | ||||
|         ::WorkItems::Widgets::CrmContacts => ::Types::WorkItems::Widgets::CrmContactsType | ||||
|       }.freeze | ||||
| 
 | ||||
|       orphan_types(*ORPHAN_TYPES) | ||||
|       def self.type_mappings | ||||
|         TYPE_MAPPINGS | ||||
|       end | ||||
| 
 | ||||
|       def self.resolve_type(object, context) | ||||
|         type_mappings[object.class] || raise("Unknown GraphQL type for widget #{object}") | ||||
|       end | ||||
| 
 | ||||
|       orphan_types(*type_mappings.values) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -115,6 +115,7 @@ class Namespace < ApplicationRecord | |||
|   has_one :import_user, class_name: 'User', through: :namespace_import_user, foreign_key: :user_id | ||||
| 
 | ||||
|   validates :owner, presence: true, if: ->(n) { n.owner_required? } | ||||
|   validates :organization, presence: true, if: :require_organization? | ||||
|   validates :name, | ||||
|     presence: true, | ||||
|     length: { maximum: 255 } | ||||
|  | @ -351,6 +352,15 @@ class Namespace < ApplicationRecord | |||
|       coalesce = Arel::Nodes::NamedFunction.new('COALESCE', [sum, 0]) | ||||
|       coalesce.as(column.to_s) | ||||
|     end | ||||
| 
 | ||||
|     def with_disabled_organization_validation | ||||
|       current_value = Gitlab::SafeRequestStore[:require_organization] | ||||
|       Gitlab::SafeRequestStore[:require_organization] = false | ||||
| 
 | ||||
|       yield | ||||
|     ensure | ||||
|       Gitlab::SafeRequestStore[:require_organization] = current_value | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def to_reference_base(from = nil, full: false, absolute_path: false) | ||||
|  | @ -712,6 +722,12 @@ class Namespace < ApplicationRecord | |||
|       :active_pages_deployments) | ||||
|   end | ||||
| 
 | ||||
|   def require_organization? | ||||
|     return false unless Feature.enabled?(:require_organization, Feature.current_request) | ||||
| 
 | ||||
|     Gitlab::SafeRequestStore.fetch(:require_organization) { true } # rubocop:disable Style/RedundantFetchBlock -- This fetch has a different interface | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def cross_namespace_reference?(from) | ||||
|  |  | |||
|  | @ -27,7 +27,9 @@ module Ci | |||
| 
 | ||||
|         runner = ::Ci::Runner.new(params) | ||||
| 
 | ||||
|         return ServiceResponse.success(payload: { runner: runner }) if runner.save | ||||
|         if Namespace.with_disabled_organization_validation { runner.save } | ||||
|           return ServiceResponse.success(payload: { runner: runner }) | ||||
|         end | ||||
| 
 | ||||
|         ServiceResponse.error(message: runner.errors.full_messages, reason: :save_error) | ||||
|       end | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ module Groups | |||
|       @group.name ||= @group.path.dup | ||||
| 
 | ||||
|       create_chat_team | ||||
|       create_group | ||||
|       Namespace.with_disabled_organization_validation { create_group } | ||||
| 
 | ||||
|       return error_response unless @group.persisted? | ||||
| 
 | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ module Groups | |||
|       group.assign_attributes(params) | ||||
| 
 | ||||
|       begin | ||||
|         success = group.save | ||||
|         success = Namespace.with_disabled_organization_validation { group.save } | ||||
| 
 | ||||
|         after_update if success | ||||
| 
 | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ module MergeRequests | |||
|       trigger_merge_request_reviewers_updated(merge_request) | ||||
| 
 | ||||
|       capture_suggested_reviewers_accepted(merge_request) | ||||
|       set_first_reviewer_assigned_at_metrics(merge_request, current_user) if new_reviewers.any? | ||||
|       set_first_reviewer_assigned_at_metrics(merge_request) if new_reviewers.any? | ||||
|     end | ||||
| 
 | ||||
|     def cleanup_environments(merge_request) | ||||
|  | @ -279,9 +279,7 @@ module MergeRequests | |||
|       # Implemented in EE | ||||
|     end | ||||
| 
 | ||||
|     def set_first_reviewer_assigned_at_metrics(merge_request, user) | ||||
|       return unless Feature.enabled?(:store_first_reviewer_assignment_timestamp_in_metrics, user, type: :beta) | ||||
| 
 | ||||
|     def set_first_reviewer_assigned_at_metrics(merge_request) | ||||
|       metrics = merge_request.metrics | ||||
|       return unless metrics | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,7 +13,9 @@ module Users | |||
|       user = build_class.new(current_user, params).execute | ||||
|       reset_token = user.generate_reset_token if user.recently_sent_password_reset? | ||||
| 
 | ||||
|       after_create_hook(user, reset_token) if user.save | ||||
|       Namespace.with_disabled_organization_validation do | ||||
|         after_create_hook(user, reset_token) if user.save | ||||
|       end | ||||
| 
 | ||||
|       user | ||||
|     end | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| --- | ||||
| name: store_first_reviewer_assignment_timestamp_in_metrics | ||||
| feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/466383 | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/158422 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/470926 | ||||
| milestone: '17.2' | ||||
| group: group::optimize | ||||
| type: beta | ||||
| default_enabled: true | ||||
| name: require_organization | ||||
| feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/411832 | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155732 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/470839 | ||||
| milestone: '17.3' | ||||
| group: group::tenant scale | ||||
| type: gitlab_com_derisk | ||||
| default_enabled: false | ||||
|  | @ -0,0 +1,13 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| if RUBY_VERSION >= "3.3" | ||||
|   # No more such warnings in Ruby 3.3+ | ||||
|   require 'async' | ||||
| else | ||||
|   # Silences this warning while requiring async gem: | ||||
|   # `warning: IO::Buffer is experimental and both the Ruby and C interface may change in the future!` | ||||
|   # See also https://github.com/socketry/io-event/issues/82 | ||||
|   Kernel.silence_warnings do | ||||
|     require 'async' | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,9 @@ | |||
| --- | ||||
| migration_job_name: BackfillEvidencesProjectId | ||||
| description: Backfills sharding key `evidences.project_id` from `releases`. | ||||
| feature_category: release_evidence | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/159580 | ||||
| milestone: '17.3' | ||||
| queued_migration_version: 20240716133952 | ||||
| finalize_after: '2024-08-22' | ||||
| finalized_by: # version of the migration that finalized this BBM | ||||
|  | @ -17,3 +17,4 @@ desired_sharding_key: | |||
|         table: releases | ||||
|         sharding_key: project_id | ||||
|         belongs_to: release | ||||
| desired_sharding_key_migration_job_name: BackfillEvidencesProjectId | ||||
|  |  | |||
|  | @ -0,0 +1,9 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddProjectIdToEvidences < Gitlab::Database::Migration[2.2] | ||||
|   milestone '17.3' | ||||
| 
 | ||||
|   def change | ||||
|     add_column :evidences, :project_id, :bigint | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,16 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class IndexEvidencesOnProjectId < Gitlab::Database::Migration[2.2] | ||||
|   milestone '17.3' | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   INDEX_NAME = 'index_evidences_on_project_id' | ||||
| 
 | ||||
|   def up | ||||
|     add_concurrent_index :evidences, :project_id, name: INDEX_NAME | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     remove_concurrent_index_by_name :evidences, INDEX_NAME | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,16 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddEvidencesProjectIdFk < Gitlab::Database::Migration[2.2] | ||||
|   milestone '17.3' | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   def up | ||||
|     add_concurrent_foreign_key :evidences, :projects, column: :project_id, on_delete: :cascade | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     with_lock_retries do | ||||
|       remove_foreign_key :evidences, column: :project_id | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,25 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddEvidencesProjectIdTrigger < Gitlab::Database::Migration[2.2] | ||||
|   milestone '17.3' | ||||
| 
 | ||||
|   def up | ||||
|     install_sharding_key_assignment_trigger( | ||||
|       table: :evidences, | ||||
|       sharding_key: :project_id, | ||||
|       parent_table: :releases, | ||||
|       parent_sharding_key: :project_id, | ||||
|       foreign_key: :release_id | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     remove_sharding_key_assignment_trigger( | ||||
|       table: :evidences, | ||||
|       sharding_key: :project_id, | ||||
|       parent_table: :releases, | ||||
|       parent_sharding_key: :project_id, | ||||
|       foreign_key: :release_id | ||||
|     ) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,40 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class QueueBackfillEvidencesProjectId < Gitlab::Database::Migration[2.2] | ||||
|   milestone '17.3' | ||||
|   restrict_gitlab_migration gitlab_schema: :gitlab_main_cell | ||||
| 
 | ||||
|   MIGRATION = "BackfillEvidencesProjectId" | ||||
|   DELAY_INTERVAL = 2.minutes | ||||
|   BATCH_SIZE = 1000 | ||||
|   SUB_BATCH_SIZE = 100 | ||||
| 
 | ||||
|   def up | ||||
|     queue_batched_background_migration( | ||||
|       MIGRATION, | ||||
|       :evidences, | ||||
|       :id, | ||||
|       :project_id, | ||||
|       :releases, | ||||
|       :project_id, | ||||
|       :release_id, | ||||
|       job_interval: DELAY_INTERVAL, | ||||
|       batch_size: BATCH_SIZE, | ||||
|       sub_batch_size: SUB_BATCH_SIZE | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     delete_batched_background_migration( | ||||
|       MIGRATION, | ||||
|       :evidences, | ||||
|       :id, | ||||
|       [ | ||||
|         :project_id, | ||||
|         :releases, | ||||
|         :project_id, | ||||
|         :release_id | ||||
|       ] | ||||
|     ) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1 @@ | |||
| db1a5709484f2352e0858fb1efc4191e408c361aba1d3b94a50f943c508fd941 | ||||
|  | @ -0,0 +1 @@ | |||
| 4b6c8960d7eab5e62087b56afb1855b277eab8bd38662f7fc8c269392638ff06 | ||||
|  | @ -0,0 +1 @@ | |||
| 8680f5ef0acbc1e9b4bebf9b8c81e7d72f72f7f2bdc68d6aab247dd9b8ad4416 | ||||
|  | @ -0,0 +1 @@ | |||
| 1798657b951493363ac8bd42596b3ff2b6007a0e80f026a2f1890543b1d2f7bf | ||||
|  | @ -0,0 +1 @@ | |||
| b88c38cc0991bda066f569bbb3e6a1519bccc19219dde3782eb7afdffbe4063c | ||||
|  | @ -1757,6 +1757,22 @@ RETURN NEW; | |||
| END | ||||
| $$; | ||||
| 
 | ||||
| CREATE FUNCTION trigger_cac7c0698291() RETURNS trigger | ||||
|     LANGUAGE plpgsql | ||||
|     AS $$ | ||||
| BEGIN | ||||
| IF NEW."project_id" IS NULL THEN | ||||
|   SELECT "project_id" | ||||
|   INTO NEW."project_id" | ||||
|   FROM "releases" | ||||
|   WHERE "releases"."id" = NEW."release_id"; | ||||
| END IF; | ||||
| 
 | ||||
| RETURN NEW; | ||||
| 
 | ||||
| END | ||||
| $$; | ||||
| 
 | ||||
| CREATE FUNCTION trigger_d4487a75bd44() RETURNS trigger | ||||
|     LANGUAGE plpgsql | ||||
|     AS $$ | ||||
|  | @ -10457,7 +10473,8 @@ CREATE TABLE evidences ( | |||
|     created_at timestamp with time zone NOT NULL, | ||||
|     updated_at timestamp with time zone NOT NULL, | ||||
|     summary_sha bytea, | ||||
|     summary jsonb DEFAULT '{}'::jsonb NOT NULL | ||||
|     summary jsonb DEFAULT '{}'::jsonb NOT NULL, | ||||
|     project_id bigint | ||||
| ); | ||||
| 
 | ||||
| CREATE SEQUENCE evidences_id_seq | ||||
|  | @ -27366,6 +27383,8 @@ CREATE INDEX index_events_on_project_id_and_id ON events USING btree (project_id | |||
| 
 | ||||
| CREATE UNIQUE INDEX index_events_on_target_type_and_target_id_and_fingerprint ON events USING btree (target_type, target_id, fingerprint); | ||||
| 
 | ||||
| CREATE INDEX index_evidences_on_project_id ON evidences USING btree (project_id); | ||||
| 
 | ||||
| CREATE INDEX index_evidences_on_release_id ON evidences USING btree (release_id); | ||||
| 
 | ||||
| CREATE INDEX index_expired_and_not_notified_personal_access_tokens ON personal_access_tokens USING btree (id, expires_at) WHERE ((impersonation = false) AND (revoked = false) AND (expire_notification_delivered = false)); | ||||
|  | @ -31914,6 +31933,8 @@ CREATE TRIGGER trigger_c8bc8646bce9 BEFORE INSERT OR UPDATE ON vulnerability_sta | |||
| 
 | ||||
| CREATE TRIGGER trigger_c9090feed334 BEFORE INSERT OR UPDATE ON boards_epic_lists FOR EACH ROW EXECUTE FUNCTION trigger_c9090feed334(); | ||||
| 
 | ||||
| CREATE TRIGGER trigger_cac7c0698291 BEFORE INSERT OR UPDATE ON evidences FOR EACH ROW EXECUTE FUNCTION trigger_cac7c0698291(); | ||||
| 
 | ||||
| CREATE TRIGGER trigger_catalog_resource_sync_event_on_project_update AFTER UPDATE ON projects FOR EACH ROW WHEN ((((old.name)::text IS DISTINCT FROM (new.name)::text) OR (old.description IS DISTINCT FROM new.description) OR (old.visibility_level IS DISTINCT FROM new.visibility_level))) EXECUTE FUNCTION insert_catalog_resource_sync_event(); | ||||
| 
 | ||||
| CREATE TRIGGER trigger_d4487a75bd44 BEFORE INSERT OR UPDATE ON terraform_state_versions FOR EACH ROW EXECUTE FUNCTION trigger_d4487a75bd44(); | ||||
|  | @ -33125,6 +33146,9 @@ ALTER TABLE ONLY personal_access_tokens | |||
| ALTER TABLE ONLY jira_tracker_data | ||||
|     ADD CONSTRAINT fk_c98abcd54c FOREIGN KEY (integration_id) REFERENCES integrations(id) ON DELETE CASCADE; | ||||
| 
 | ||||
| ALTER TABLE ONLY evidences | ||||
|     ADD CONSTRAINT fk_ca4bbc114d FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; | ||||
| 
 | ||||
| ALTER TABLE ONLY subscription_add_on_purchases | ||||
|     ADD CONSTRAINT fk_caed789645 FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE; | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ exceptions: | |||
|   - ANSI | ||||
|   - APAC | ||||
|   - API | ||||
|   - ARIA | ||||
|   - APM | ||||
|   - ARM | ||||
|   - ARN | ||||
|  |  | |||
|  | @ -46,7 +46,8 @@ Instead of checking repositories manually, GitLab can be configured to run the c | |||
| 1. Enable **Enable repository checks**. | ||||
| 
 | ||||
| When enabled, GitLab periodically runs a repository check on all project repositories and wiki | ||||
| repositories to detect possible data corruption. A project is checked no more than once per month. | ||||
| repositories to detect possible data corruption. A project is checked no more than once per month, and new projects aren't checked for at least 24 hours. | ||||
| 
 | ||||
| Administrators can configure the frequency of repository checks. To edit the frequency: | ||||
| 
 | ||||
| - For Linux package installations, edit `gitlab_rails['repository_check_worker_cron']` in | ||||
|  |  | |||
|  | @ -118,45 +118,38 @@ Chat interface. The following example shows how to open the GitLab Duo Chat | |||
| drawer by using an event listener and the GitLab Duo Chat global state: | ||||
| 
 | ||||
| ```javascript | ||||
| import { helpCenterState } from '~/super_sidebar/constants'; | ||||
| import { duoChatGlobalState } from '~/super_sidebar/constants'; | ||||
| myFancyToggleToOpenChat.addEventListener('click', () => { | ||||
|   helpCenterState.showTanukiBotChatDrawer = true; | ||||
|   duoChatGlobalState.isShown = true; | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| #### Initiating GitLab Duo Chat with a pre-defined prompt | ||||
| 
 | ||||
| In some scenarios, you may want to direct users towards a specific topic or | ||||
| query when they open GitLab Duo Chat. The following example method: | ||||
| 
 | ||||
| 1. Opens the GitLab Duo Chat drawer. | ||||
| 1. Sends a pre-defined prompt to GitLab Duo Chat. | ||||
| query when they open GitLab Duo Chat. We have a utility function that will  | ||||
| open DuoChat drawer and send a command in a queue for DuoChat to execute on. | ||||
| This should trigger the loading state and the streaming with the given prompt. | ||||
| 
 | ||||
| ```javascript | ||||
| import chatMutation from 'ee/ai/graphql/chat.mutation.graphql'; | ||||
| import { helpCenterState } from '~/super_sidebar/constants'; | ||||
| import { sendDuoChatCommand } from 'ee/ai/utils'; | ||||
| [...] | ||||
| 
 | ||||
| methods: { | ||||
|   openChatWithPrompt() { | ||||
|     const myPrompt = "What is the meaning of life?" | ||||
|     helpCenterState.showTanukiBotChatDrawer = true; | ||||
| 
 | ||||
|     this.$apollo | ||||
|       .mutate({ | ||||
|         mutation: chatMutation, | ||||
|         variables: { | ||||
|           question: myPrompt, | ||||
|           resourceId: this.resourceId, | ||||
|         }, | ||||
|       }) | ||||
|       .catch((error) => { | ||||
|         // handle potential errors here | ||||
|       }); | ||||
|     sendDuoChatCommand( | ||||
|       { | ||||
|         question: '/feedback' // This is your prompt | ||||
|         resourceId: 'gid:://gitlab/WorkItem/1', // A unique ID to identify the action for streaming | ||||
|         variables: {} // Any additional graphql variables you want to pass to ee/app/assets/javascripts/ai/graphql/chat.mutation.graphql when executing the query | ||||
|       } | ||||
|     ) | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Note that `sendDuoChatCommand` cannot be chained, meaning that you can send one command to DuoChat and have to wait until this action is done before sending a different command or the previous command might not work as expected. | ||||
| 
 | ||||
| This enhancement allows for a more tailored user experience by guiding the | ||||
| conversation in GitLab Duo Chat towards predefined areas of interest or concern. | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ All tables with the following `gitlab_schema` are considered "cell-local": | |||
| 
 | ||||
| - `gitlab_main_cell` | ||||
| - `gitlab_ci` | ||||
| - `gitlab_sec` | ||||
| 
 | ||||
| All newly created cell-local tables are required to have a `sharding_key` | ||||
| defined in the corresponding `db/docs/` file for that table. | ||||
|  |  | |||
|  | @ -12,4 +12,15 @@ The following feature flags exist in GitLab. These flags determine the availabil | |||
| 
 | ||||
| For self-managed instances, [GitLab administrators can change the state of the flag](../administration/feature_flags.md). | ||||
| 
 | ||||
| <!-- markdownlint-disable MD044 --> | ||||
| <!-- MD044/proper-names test disabled after this line to make page compatible with markdownlint-cli 0.29.0. --> | ||||
| <!-- See https://docs.gitlab.com/ee/development/documentation/testing/markdownlint.html#disable-markdownlint-tests --> | ||||
| 
 | ||||
| <div class="d-none"> | ||||
|   <strong>If you don't see the feature flag tables below, view them at <a href="https://docs.gitlab.com/ee/user/feature_flags.html">docs.gitlab.com</a>.</strong> | ||||
| </div> | ||||
| <!-- the div above will not display on the docs site but will display on /help --> | ||||
| 
 | ||||
| <!-- markdownlint-enable MD044 --> | ||||
| 
 | ||||
| <!-- When published, everything below this line is generated by 'layouts/feature_flags_table.md.erb' in the 'gitlab-docs' repository --> | ||||
|  |  | |||
|  | @ -57,7 +57,9 @@ module Gitlab | |||
| 
 | ||||
|           block_after_save = needs_blocking? | ||||
| 
 | ||||
|           Users::UpdateService.new(gl_user, user: gl_user).execute! | ||||
|           Namespace.with_disabled_organization_validation do | ||||
|             Users::UpdateService.new(gl_user, user: gl_user).execute! | ||||
|           end | ||||
| 
 | ||||
|           gl_user.block_pending_approval if block_after_save | ||||
|           activate_user_if_user_cap_not_reached | ||||
|  |  | |||
|  | @ -0,0 +1,10 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Gitlab | ||||
|   module BackgroundMigration | ||||
|     class BackfillEvidencesProjectId < BackfillDesiredShardingKeyJob | ||||
|       operation_name :backfill_evidences_project_id | ||||
|       feature_category :release_evidence | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -19,7 +19,9 @@ module Gitlab | |||
|         ) | ||||
| 
 | ||||
|         user.assign_personal_namespace(Organizations::Organization.default_organization) | ||||
|         user.save! | ||||
|         Namespace.with_disabled_organization_validation do | ||||
|           user.save! | ||||
|         end | ||||
|         user | ||||
|       end | ||||
| 
 | ||||
|  |  | |||
|  | @ -178,7 +178,7 @@ module Gitlab | |||
| 
 | ||||
|           modify_attributes | ||||
| 
 | ||||
|           @importable.save!(touch: false) | ||||
|           Namespace.with_disabled_organization_validation { @importable.save!(touch: false) } | ||||
|         end | ||||
| 
 | ||||
|         def filter_attributes(params) | ||||
|  |  | |||
|  | @ -178,7 +178,9 @@ module Gitlab | |||
|           def create_group(**args) | ||||
|             logger.info(message: 'Creating group', **args) | ||||
| 
 | ||||
|             ensure_success(::Groups::CreateService.new(@user, **args).execute[:group]) | ||||
|             Namespace.with_disabled_organization_validation do | ||||
|               ensure_success(::Groups::CreateService.new(@user, **args).execute[:group]) | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def ensure_project(name:, namespace_id:, **args) | ||||
|  | @ -194,7 +196,9 @@ module Gitlab | |||
|           def create_project(**args) | ||||
|             logger.info(message: 'Creating project', **args) | ||||
| 
 | ||||
|             ensure_success(::Projects::CreateService.new(@user, **args).execute) | ||||
|             Namespace.with_disabled_organization_validation do | ||||
|               ensure_success(::Projects::CreateService.new(@user, **args).execute) | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def register_record(record, records) | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ namespace :tw do | |||
|       end | ||||
|     end | ||||
| 
 | ||||
|     # For groups without an assigned TW, comment out the line. | ||||
|     CODE_OWNER_RULES = [ | ||||
|       # CodeOwnerRule.new('Activation', ''), | ||||
|       # CodeOwnerRule.new('Acquisition', ''), | ||||
|  | @ -56,7 +57,7 @@ namespace :tw do | |||
|       CodeOwnerRule.new('Remote Development', '@ashrafkhamis'), | ||||
|       CodeOwnerRule.new('Import and Integrate', '@eread'), | ||||
|       CodeOwnerRule.new('Infrastructure', '@sselhorn'), | ||||
|       # CodeOwnerRule.new('Knowledge', ''), | ||||
|       CodeOwnerRule.new('Knowledge', '@msedlakjakubowski'), | ||||
|       CodeOwnerRule.new('MLOps', '@sselhorn @jglassman1 @fneill'), | ||||
|       # CodeOwnerRule.new('Observability', ''), | ||||
|       CodeOwnerRule.new('Optimize', '@lciutacu'), | ||||
|  |  | |||
|  | @ -37,7 +37,9 @@ namespace :gitlab do | |||
| 
 | ||||
|     tmp_namespace_path = "tmp-project-import-#{Time.now.to_i}" | ||||
|     puts "Creating temporary namespace #{tmp_namespace_path}" | ||||
|     tmp_namespace = Namespace.create!(owner: admin, name: tmp_namespace_path, path: tmp_namespace_path, type: Namespaces::UserNamespace.sti_name) | ||||
|     tmp_namespace = Namespace.with_disabled_organization_validation do | ||||
|       Namespace.create!(owner: admin, name: tmp_namespace_path, path: tmp_namespace_path, type: Namespaces::UserNamespace.sti_name) | ||||
|     end | ||||
| 
 | ||||
|     templates = if template_names.empty? | ||||
|                   Gitlab::ProjectTemplate.all | ||||
|  | @ -54,7 +56,7 @@ namespace :gitlab do | |||
|       } | ||||
| 
 | ||||
|       puts "Creating project for #{template.title}" | ||||
|       project = Projects::CreateService.new(admin, params).execute | ||||
|       project = Namespace.with_disabled_organization_validation { Projects::CreateService.new(admin, params).execute } | ||||
| 
 | ||||
|       unless project.persisted? | ||||
|         raise "Failed to create project: #{project.errors.messages}" | ||||
|  |  | |||
|  | @ -2156,12 +2156,6 @@ msgstr "" | |||
| msgid "AISummary|View summary" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "AI|An error occurred while explaining the code." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "AI|An error occurred while troubleshooting the failed job." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "AI|Apply AI-generated description" | ||||
| msgstr "" | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ gem 'capybara', '~> 3.40.0' | |||
| gem 'capybara-screenshot', '~> 1.0.26' | ||||
| gem 'rake', '~> 13', '>= 13.2.1' | ||||
| gem 'rspec', '~> 3.13' | ||||
| gem 'selenium-webdriver', '= 4.22.0' | ||||
| gem 'selenium-webdriver', '= 4.23.0' | ||||
| gem 'airborne', '~> 0.3.7', require: false # airborne is messing with rspec sandboxed mode so not requiring by default | ||||
| gem 'rest-client', '~> 2.1.0' | ||||
| gem 'rspec_junit_formatter', '~> 0.6.0' | ||||
|  |  | |||
|  | @ -328,7 +328,7 @@ GEM | |||
|     sawyer (0.9.2) | ||||
|       addressable (>= 2.3.5) | ||||
|       faraday (>= 0.17.3, < 3) | ||||
|     selenium-webdriver (4.22.0) | ||||
|     selenium-webdriver (4.23.0) | ||||
|       base64 (~> 0.2) | ||||
|       logger (~> 1.4) | ||||
|       rexml (~> 3.2, >= 3.2.5) | ||||
|  | @ -423,7 +423,7 @@ DEPENDENCIES | |||
|   rspec-parameterized (~> 1.0.2) | ||||
|   rspec_junit_formatter (~> 0.6.0) | ||||
|   ruby-debug-ide (~> 0.7.3) | ||||
|   selenium-webdriver (= 4.22.0) | ||||
|   selenium-webdriver (= 4.23.0) | ||||
|   slack-notifier (~> 2.4) | ||||
|   terminal-table (~> 3.0.2) | ||||
|   warning (~> 1.4) | ||||
|  |  | |||
|  | @ -11,6 +11,8 @@ FactoryBot.define do | |||
| 
 | ||||
|     owner { association(:user, strategy: :build, namespace: instance, username: path) } | ||||
| 
 | ||||
|     association :organization | ||||
| 
 | ||||
|     after(:create) do |namespace, evaluator| | ||||
|       # simulating ::Namespaces::ProcessSyncEventsWorker because most tests don't run Sidekiq inline | ||||
|       # Note: we need to get refreshed `traversal_ids` it is updated via SQL query | ||||
|  |  | |||
|  | @ -115,8 +115,6 @@ describe('ModelCreate', () => { | |||
|       it('should show markdown editor', () => { | ||||
|         createWrapper(); | ||||
| 
 | ||||
|         expect(findMarkdownEditor().exists()).toBe(true); | ||||
| 
 | ||||
|         expect(findMarkdownEditor().props()).toMatchObject({ | ||||
|           enableContentEditor: true, | ||||
|           formFieldProps: { | ||||
|  | @ -127,6 +125,7 @@ describe('ModelCreate', () => { | |||
|           markdownDocsPath: '/help/user/markdown', | ||||
|           renderMarkdownPath: '/markdown-preview', | ||||
|           uploadsPath: '', | ||||
|           restrictedToolBarItems: ['full-screen'], | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|  | @ -238,7 +237,7 @@ describe('ModelCreate', () => { | |||
|         expect(findGlModal().props()).toMatchObject({ | ||||
|           modalId: 'create-model-modal', | ||||
|           title: 'Create model, version & import artifacts', | ||||
|           size: 'sm', | ||||
|           size: 'lg', | ||||
|         }); | ||||
|       }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -124,7 +124,7 @@ describe('ModelVersionCreate', () => { | |||
|         expect(findGlModal().props()).toMatchObject({ | ||||
|           modalId: 'create-model-version-modal', | ||||
|           title: 'Create model version & import artifacts', | ||||
|           size: 'sm', | ||||
|           size: 'lg', | ||||
|         }); | ||||
|       }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -203,6 +203,31 @@ describe('vue_shared/component/markdown/markdown_editor', () => { | |||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('when additional restricted tool bar items are given', () => { | ||||
|     beforeEach(() => { | ||||
|       buildWrapper({ propsData: { restrictedToolBarItems: ['full-screen'] } }); | ||||
|     }); | ||||
| 
 | ||||
|     it('passes them to restrictedToolBarItems', () => { | ||||
|       expect(findMarkdownField().props().restrictedToolBarItems).toContain('full-screen'); | ||||
|     }); | ||||
| 
 | ||||
|     describe('when attachments are disabled', () => { | ||||
|       beforeEach(() => { | ||||
|         buildWrapper({ | ||||
|           propsData: { disableAttachments: true, restrictedToolBarItems: ['full-screen'] }, | ||||
|         }); | ||||
|       }); | ||||
| 
 | ||||
|       it('passes `attach-file` and `full-screen` restrictedToolBarItems', () => { | ||||
|         expect(findMarkdownField().props().restrictedToolBarItems).toEqual([ | ||||
|           'full-screen', | ||||
|           'attach-file', | ||||
|         ]); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('disabled', () => { | ||||
|     it('disables markdown field when disabled prop is true', () => { | ||||
|       buildWrapper({ propsData: { disabled: true } }); | ||||
|  |  | |||
|  | @ -266,13 +266,5 @@ RSpec.describe GitlabSchema.types['Group'], feature_category: :groups_and_projec | |||
|         ) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when group does not have an organization associated with it' do | ||||
|       let_it_be(:group) { create(:group, :public, organization: nil) } | ||||
| 
 | ||||
|       it 'returns nil' do | ||||
|         expect(organization_edit_path).to be_nil | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -1305,13 +1305,5 @@ RSpec.describe GitlabSchema.types['Project'], feature_category: :groups_and_proj | |||
|         ) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when project does not have an organization associated with it' do | ||||
|       let_it_be(:project) { create(:project, :public, organization: nil) } | ||||
| 
 | ||||
|       it 'returns nil' do | ||||
|         expect(organization_edit_path).to be_nil | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ require 'spec_helper' | |||
| 
 | ||||
| RSpec.describe Types::WorkItems::WidgetInterface, feature_category: :team_planning do | ||||
|   include GraphqlHelpers | ||||
|   using RSpec::Parameterized::TableSyntax | ||||
| 
 | ||||
|   it 'exposes the expected fields' do | ||||
|     expected_fields = %i[type] | ||||
|  | @ -11,42 +12,45 @@ RSpec.describe Types::WorkItems::WidgetInterface, feature_category: :team_planni | |||
|     expect(described_class).to have_graphql_fields(*expected_fields) | ||||
|   end | ||||
| 
 | ||||
|   describe ".resolve_type" do | ||||
|     using RSpec::Parameterized::TableSyntax | ||||
|   where(:widget_class, :widget_type_name) do | ||||
|     WorkItems::Widgets::Description      | Types::WorkItems::Widgets::DescriptionType | ||||
|     WorkItems::Widgets::Hierarchy        | Types::WorkItems::Widgets::HierarchyType | ||||
|     WorkItems::Widgets::Assignees        | Types::WorkItems::Widgets::AssigneesType | ||||
|     WorkItems::Widgets::Labels           | Types::WorkItems::Widgets::LabelsType | ||||
|     WorkItems::Widgets::Notes            | Types::WorkItems::Widgets::NotesType | ||||
|     WorkItems::Widgets::Notifications    | Types::WorkItems::Widgets::NotificationsType | ||||
|     WorkItems::Widgets::CurrentUserTodos | Types::WorkItems::Widgets::CurrentUserTodosType | ||||
|     WorkItems::Widgets::AwardEmoji       | Types::WorkItems::Widgets::AwardEmojiType | ||||
|     WorkItems::Widgets::LinkedItems      | Types::WorkItems::Widgets::LinkedItemsType | ||||
|     WorkItems::Widgets::LinkedItems      | Types::WorkItems::Widgets::LinkedItemsType | ||||
|     WorkItems::Widgets::StartAndDueDate  | Types::WorkItems::Widgets::StartAndDueDateType | ||||
|     WorkItems::Widgets::Milestone        | Types::WorkItems::Widgets::MilestoneType | ||||
|     WorkItems::Widgets::Participants     | Types::WorkItems::Widgets::ParticipantsType | ||||
|     WorkItems::Widgets::TimeTracking     | Types::WorkItems::Widgets::TimeTracking::TimeTrackingType | ||||
|     WorkItems::Widgets::Designs          | Types::WorkItems::Widgets::DesignsType | ||||
|     WorkItems::Widgets::CrmContacts      | Types::WorkItems::Widgets::CrmContactsType | ||||
|   end | ||||
| 
 | ||||
|     where(:widget_class, :widget_type_name) do | ||||
|       WorkItems::Widgets::Description      | Types::WorkItems::Widgets::DescriptionType | ||||
|       WorkItems::Widgets::Hierarchy        | Types::WorkItems::Widgets::HierarchyType | ||||
|       WorkItems::Widgets::Assignees        | Types::WorkItems::Widgets::AssigneesType | ||||
|       WorkItems::Widgets::Labels           | Types::WorkItems::Widgets::LabelsType | ||||
|       WorkItems::Widgets::Notes            | Types::WorkItems::Widgets::NotesType | ||||
|       WorkItems::Widgets::Notifications    | Types::WorkItems::Widgets::NotificationsType | ||||
|       WorkItems::Widgets::CurrentUserTodos | Types::WorkItems::Widgets::CurrentUserTodosType | ||||
|       WorkItems::Widgets::AwardEmoji       | Types::WorkItems::Widgets::AwardEmojiType | ||||
|       WorkItems::Widgets::LinkedItems      | Types::WorkItems::Widgets::LinkedItemsType | ||||
|       WorkItems::Widgets::LinkedItems      | Types::WorkItems::Widgets::LinkedItemsType | ||||
|       WorkItems::Widgets::StartAndDueDate  | Types::WorkItems::Widgets::StartAndDueDateType | ||||
|       WorkItems::Widgets::Milestone        | Types::WorkItems::Widgets::MilestoneType | ||||
|       WorkItems::Widgets::Participants     | Types::WorkItems::Widgets::ParticipantsType | ||||
|       WorkItems::Widgets::TimeTracking     | Types::WorkItems::Widgets::TimeTracking::TimeTrackingType | ||||
|       WorkItems::Widgets::Designs          | Types::WorkItems::Widgets::DesignsType | ||||
|       WorkItems::Widgets::Development      | Types::WorkItems::Widgets::DevelopmentType | ||||
|       WorkItems::Widgets::CrmContacts      | Types::WorkItems::Widgets::CrmContactsType | ||||
|     end | ||||
| 
 | ||||
|     with_them do | ||||
|   with_them do | ||||
|     describe ".resolve_type" do | ||||
|       it 'knows the correct type for objects' do | ||||
|         expect( | ||||
|           described_class.resolve_type(widget_class.new(build(:work_item)), {}) | ||||
|         ).to eq(widget_type_name) | ||||
|       end | ||||
| 
 | ||||
|       it 'raises an error for an unknown type' do | ||||
|         project = build(:project) | ||||
| 
 | ||||
|         expect { described_class.resolve_type(project, {}) } | ||||
|           .to raise_error("Unknown GraphQL type for widget #{project}") | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it 'raises an error for an unknown type' do | ||||
|       project = build(:project) | ||||
| 
 | ||||
|       expect { described_class.resolve_type(project, {}) } | ||||
|         .to raise_error("Unknown GraphQL type for widget #{project}") | ||||
|     describe '.orphan_types' do | ||||
|       it 'includes the type' do | ||||
|         expect(described_class.orphan_types).to include(widget_type_name) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do | |||
| 
 | ||||
|   describe '#valid_sign_in?' do | ||||
|     before do | ||||
|       gl_user.save! | ||||
|       Namespace.with_disabled_organization_validation { gl_user.save! } | ||||
|     end | ||||
| 
 | ||||
|     it 'returns true' do | ||||
|  |  | |||
|  | @ -30,6 +30,10 @@ RSpec.describe Gitlab::Auth::OAuth::User, feature_category: :system_access do | |||
|   let(:ldap_user) { Gitlab::Auth::Ldap::Person.new(Net::LDAP::Entry.new, 'ldapmain') } | ||||
|   let(:ldap_user_2) { Gitlab::Auth::Ldap::Person.new(Net::LDAP::Entry.new, 'ldapmain') } | ||||
| 
 | ||||
|   around do |example| | ||||
|     Namespace.with_disabled_organization_validation { example.run } | ||||
|   end | ||||
| 
 | ||||
|   describe '.find_by_uid_and_provider' do | ||||
|     let(:provider) { 'provider' } | ||||
|     let(:uid) { 'uid' } | ||||
|  |  | |||
|  | @ -0,0 +1,15 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe Gitlab::BackgroundMigration::BackfillEvidencesProjectId, | ||||
|   feature_category: :release_evidence, | ||||
|   schema: 20240716133948 do | ||||
|   include_examples 'desired sharding key backfill job' do | ||||
|     let(:batch_table) { :evidences } | ||||
|     let(:backfill_column) { :project_id } | ||||
|     let(:backfill_via_table) { :releases } | ||||
|     let(:backfill_via_column) { :project_id } | ||||
|     let(:backfill_via_foreign_key) { :release_id } | ||||
|   end | ||||
| end | ||||
|  | @ -43,18 +43,8 @@ RSpec.describe Gitlab::Git::ObjectPool, feature_category: :source_code_managemen | |||
|       subject.create # rubocop:disable Rails/SaveBang | ||||
|     end | ||||
| 
 | ||||
|     context "when the pool doesn't exist yet" do | ||||
|       it 'creates the pool' do | ||||
|         expect(subject.exists?).to be(true) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when the pool already exists' do | ||||
|       it 'raises an FailedPrecondition' do | ||||
|         expect do | ||||
|           subject.create # rubocop:disable Rails/SaveBang | ||||
|         end.to raise_error(GRPC::FailedPrecondition) | ||||
|       end | ||||
|     it 'creates the pool' do | ||||
|       expect(subject.exists?).to be(true) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,33 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| require_migration! | ||||
| 
 | ||||
| RSpec.describe QueueBackfillEvidencesProjectId, feature_category: :release_evidence do | ||||
|   let!(:batched_migration) { described_class::MIGRATION } | ||||
| 
 | ||||
|   it 'schedules a new batched migration' do | ||||
|     reversible_migration do |migration| | ||||
|       migration.before -> { | ||||
|         expect(batched_migration).not_to have_scheduled_batched_migration | ||||
|       } | ||||
| 
 | ||||
|       migration.after -> { | ||||
|         expect(batched_migration).to have_scheduled_batched_migration( | ||||
|           table_name: :evidences, | ||||
|           column_name: :id, | ||||
|           interval: described_class::DELAY_INTERVAL, | ||||
|           batch_size: described_class::BATCH_SIZE, | ||||
|           sub_batch_size: described_class::SUB_BATCH_SIZE, | ||||
|           gitlab_schema: :gitlab_main_cell, | ||||
|           job_arguments: [ | ||||
|             :project_id, | ||||
|             :releases, | ||||
|             :project_id, | ||||
|             :release_id | ||||
|           ] | ||||
|         ) | ||||
|       } | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -42,8 +42,8 @@ RSpec.describe Analytics::CycleAnalytics::Stage, feature_category: :value_stream | |||
| 
 | ||||
|   describe '.distinct_stages_within_hierarchy' do | ||||
|     let_it_be(:group) { create(:group) } | ||||
|     let_it_be(:sub_group) { create(:group, parent: group) } | ||||
|     let_it_be(:project) { create(:project, group: sub_group).reload } | ||||
|     let_it_be(:sub_group) { create(:group, organization: group.organization, parent: group) } | ||||
|     let_it_be(:project) { create(:project, organization: group.organization, group: sub_group).reload } | ||||
| 
 | ||||
|     before do | ||||
|       # event identifiers are the same | ||||
|  |  | |||
|  | @ -314,7 +314,7 @@ RSpec.describe Namespaces::ProjectNamespace, 'Routable', :with_clean_rails_cache | |||
| 
 | ||||
|   it 'skips route creation for the resource' do | ||||
|     expect do | ||||
|       described_class.create!(project: nil, parent: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC, path: 'foo', name: 'foo') | ||||
|       described_class.create!(project: nil, organization: group.organization, parent: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC, path: 'foo', name: 'foo') | ||||
|     end.not_to change { Route.count } | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -271,7 +271,7 @@ RSpec.describe Group, feature_category: :groups_and_projects do | |||
| 
 | ||||
|         it 'does not allow a subgroup to have the same name as an existing subgroup' do | ||||
|           sub_group1 = create(:group, parent: group, name: "SG", path: 'api') | ||||
|           sub_group2 = described_class.new(parent: group, name: "SG", path: 'api2') | ||||
|           sub_group2 = described_class.new(parent: group, name: "SG", path: 'api2', organization: sub_group1.organization) | ||||
| 
 | ||||
|           expect(sub_group1).to be_valid | ||||
|           expect(sub_group2).not_to be_valid | ||||
|  |  | |||
|  | @ -1277,7 +1277,7 @@ RSpec.describe Member, feature_category: :groups_and_projects do | |||
|   end | ||||
| 
 | ||||
|   context 'for updating organization_users' do | ||||
|     let_it_be(:group) { create(:group, :with_organization) } | ||||
|     let_it_be(:group) { create(:group) } | ||||
|     let_it_be(:user) { create(:user) } | ||||
|     let(:member) { create(:group_member, source: group, user: user) } | ||||
| 
 | ||||
|  | @ -1362,12 +1362,6 @@ RSpec.describe Member, feature_category: :groups_and_projects do | |||
| 
 | ||||
|         it_behaves_like 'does not create an organization_user entry' | ||||
|       end | ||||
| 
 | ||||
|       context 'when organization does not exist' do | ||||
|         let(:member) { create(:group_member, user: user) } | ||||
| 
 | ||||
|         it_behaves_like 'does not create an organization_user entry' | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when updating' do | ||||
|  |  | |||
|  | @ -90,6 +90,7 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do | |||
|     it { is_expected.to validate_presence_of(:path) } | ||||
|     it { is_expected.to validate_length_of(:path).is_at_most(255) } | ||||
|     it { is_expected.to validate_presence_of(:owner) } | ||||
|     it { is_expected.to validate_presence_of(:organization) } | ||||
|     it { is_expected.to validate_numericality_of(:max_artifacts_size).only_integer.is_greater_than(0) } | ||||
| 
 | ||||
|     context 'validating the parent of a namespace' do | ||||
|  | @ -702,6 +703,54 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do | |||
|     it { is_expected.to include_module(Namespaces::Traversal::LinearScopes) } | ||||
|   end | ||||
| 
 | ||||
|   context 'when feature flag require_organization is disabled' do | ||||
|     before do | ||||
|       stub_feature_flags(require_organization: false) | ||||
|     end | ||||
| 
 | ||||
|     it 'does not require organization' do | ||||
|       namespace.organization = nil | ||||
| 
 | ||||
|       expect(namespace.valid?).to eq(true) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   context 'when feature flag require_organization is enabled' do | ||||
|     it 'does require organization' do | ||||
|       namespace.organization = nil | ||||
| 
 | ||||
|       Namespace.with_disabled_organization_validation do | ||||
|         expect(namespace.valid?).to eq(false) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     describe '.with_disabled_organization_validation', :request_store do | ||||
|       it 'does not require organization' do | ||||
|         namespace.organization = nil | ||||
| 
 | ||||
|         Namespace.with_disabled_organization_validation do | ||||
|           expect(namespace.valid?).to eq(true) | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'with nested calls' do | ||||
|         it 'only last call will enable the validation' do | ||||
|           result = [] | ||||
|           Namespace.with_disabled_organization_validation do | ||||
|             result << described_class.new.require_organization? | ||||
|             Namespace.with_disabled_organization_validation do | ||||
|               result << described_class.new.require_organization? | ||||
|             end | ||||
|             result << described_class.new.require_organization? | ||||
|           end | ||||
| 
 | ||||
|           expect(result.any?(true)).to be false | ||||
|           expect(described_class.new.require_organization?).to be true | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#traversal_ids' do | ||||
|     let(:namespace) { build(:group) } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe Namespaces::ProjectNamespace, type: :model do | ||||
| RSpec.describe Namespaces::ProjectNamespace, type: :model, feature_category: :groups_and_projects do | ||||
|   let_it_be(:organization) { create(:organization) } | ||||
| 
 | ||||
|   describe 'relationships' do | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ RSpec.describe Preloaders::ProjectPolicyPreloader do | |||
|     control = ActiveRecord::QueryRecorder.new { authorize_all_projects(user) } | ||||
| 
 | ||||
|     new_project1 = create(:project, :private, maintainers: user) | ||||
|     new_project2 = create(:project, :private, namespace: root_parent) | ||||
|     new_project2 = create(:project, :private, namespace: root_parent, maintainers: user) | ||||
| 
 | ||||
|     another_root = create(:group, :private, name: 'root-3', path: 'root-3') | ||||
|     new_project3 = create(:project, :private, namespace: another_root, maintainers: user) | ||||
|  |  | |||
|  | @ -59,7 +59,7 @@ RSpec.describe 'getting merge request listings nested in a project', feature_cat | |||
|       # We cannot disable SQL query limiting here, since the transaction does not | ||||
|       # begin until we enter the controller. | ||||
|       headers = { | ||||
|         'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/322979' | ||||
|         'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/469250;205' | ||||
|       } | ||||
| 
 | ||||
|       post_graphql(query, current_user: current_user, headers: headers) | ||||
|  |  | |||
|  | @ -64,7 +64,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor | |||
|     it 'executes a limited number of queries', :use_clean_rails_redis_caching do | ||||
|       control = ActiveRecord::QueryRecorder.new { perform_archive_upload } | ||||
| 
 | ||||
|       expect(control.count).to be <= 114 | ||||
|       expect(control.count).to be <= 115 | ||||
|     end | ||||
| 
 | ||||
|     it 'schedules an import using a namespace' do | ||||
|  |  | |||
|  | @ -700,15 +700,6 @@ RSpec.describe MergeRequests::UpdateService, :mailer, feature_category: :code_re | |||
|         describe 'recording the first reviewer assigned at timestamp' do | ||||
|           subject(:metrics) { merge_request.reload.metrics } | ||||
| 
 | ||||
|           context 'when store_first_reviewer_assignment_timestamp_in_metrics feature flag is off' do | ||||
|             it 'does not record anything' do | ||||
|               stub_feature_flags(store_first_reviewer_assignment_timestamp_in_metrics: false) | ||||
|               update_merge_request(reviewer_ids: [user2.id]) | ||||
| 
 | ||||
|               expect(metrics.reviewer_first_assigned_at).to eq(nil) | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           it 'sets the current timestamp' do | ||||
|             freeze_time do | ||||
|               update_merge_request(reviewer_ids: [user2.id]) | ||||
|  |  | |||
|  | @ -393,7 +393,7 @@ RSpec.describe ResourceAccessTokens::CreateService, feature_category: :system_ac | |||
|     end | ||||
| 
 | ||||
|     context 'when resource organization is not set', :enable_admin_mode do | ||||
|       let_it_be(:resource) { create(:project, :private, organization: nil) } | ||||
|       let_it_be(:resource) { create(:project, :private, organization_id: nil) } | ||||
|       let_it_be(:default_organization) { Organizations::Organization.default_organization } | ||||
|       let(:user) { create(:admin) } | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ RSpec.describe Users::RegistrationsBuildService, feature_category: :system_acces | |||
|       it 'creates the user_detail record' do | ||||
|         user = service.execute | ||||
| 
 | ||||
|         expect { user.save! }.to change { UserDetail.count }.by(1) | ||||
|         expect { Namespace.with_disabled_organization_validation { user.save! } }.to change { UserDetail.count }.by(1) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -205,6 +205,7 @@ RSpec.configure do |config| | |||
|   config.include UserWithNamespaceShim | ||||
|   config.include OrphanFinalArtifactsCleanupHelpers, :orphan_final_artifacts_cleanup | ||||
|   config.include ClickHouseHelpers, :click_house | ||||
|   config.include DisableNamespaceOrganizationValidationHelper | ||||
| 
 | ||||
|   config.include_context 'when rendered has no HTML escapes', type: :view | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,70 @@ | |||
| --- | ||||
| - ee/spec/controllers/ee/groups_controller_spec.rb | ||||
| - ee/spec/controllers/ee/registrations_controller_spec.rb | ||||
| - ee/spec/controllers/ee/omniauth_callbacks_controller_spec.rb | ||||
| - ee/spec/controllers/groups/omniauth_callbacks_controller_spec.rb | ||||
| - ee/spec/controllers/ldap/omniauth_callbacks_controller_spec.rb | ||||
| - ee/spec/controllers/registrations/groups_controller_spec.rb | ||||
| - ee/spec/features/groups_spec.rb | ||||
| - ee/spec/features/projects/settings/merge_requests_settings_spec.rb | ||||
| - ee/spec/features/registrations/combined_registration_spec.rb | ||||
| - ee/spec/features/registrations/saas/sso_signin_standard_flow_company_creating_project_spec.rb | ||||
| - ee/spec/features/registrations/saas/standard_flow_company_creating_project_spec.rb | ||||
| - ee/spec/features/registrations/saas/standard_flow_just_me_creating_project_spec.rb | ||||
| - ee/spec/features/registrations/saas/standard_flow_just_me_importing_project_spec.rb | ||||
| - ee/spec/features/registrations/saas/subscription_flow_company_paid_plan_spec.rb | ||||
| - ee/spec/features/registrations/saas/subscription_flow_just_me_paid_plan_spec.rb | ||||
| - ee/spec/features/registrations/saas/trial_flow_company_creating_project_spec.rb | ||||
| - ee/spec/features/registrations/saas/trial_flow_company_importing_project_spec.rb | ||||
| - ee/spec/features/registrations/saas/trial_flow_just_me_creating_project_spec.rb | ||||
| - ee/spec/features/registrations/saas/trial_flow_just_me_importing_project_spec.rb | ||||
| - ee/spec/features/registrations/sign_up_with_trial_from_external_site_without_confirmation_spec.rb | ||||
| - ee/spec/features/registrations/start_trial_from_external_site_without_confirmation_spec.rb | ||||
| - ee/spec/features/subscriptions/subscription_flow_for_existing_user_with_eligible_group_spec.rb | ||||
| - ee/spec/features/trials/saas/creation_with_multiple_existing_namespace_flow_spec.rb | ||||
| - ee/spec/features/trials/saas/creation_with_no_existing_namespace_flow_spec.rb | ||||
| - ee/spec/features/trials/saas/creation_with_one_existing_namespace_flow_spec.rb | ||||
| - ee/spec/lib/gitlab/auth/group_saml/user_spec.rb | ||||
| - ee/spec/lib/gitlab/auth/ldap/user_spec.rb | ||||
| - ee/spec/lib/gitlab/auth/oidc/user_spec.rb | ||||
| - ee/spec/lib/gitlab/auth/saml/user_spec.rb | ||||
| - ee/spec/lib/ee/gitlab/scim/provisioning_service_spec.rb | ||||
| - ee/spec/lib/ee/gitlab/scim/group/provisioning_service_spec.rb | ||||
| - ee/spec/services/ee/groups/create_service_spec.rb | ||||
| - ee/spec/services/ee/users/create_service_spec.rb | ||||
| - ee/spec/services/users/service_accounts/create_service_spec.rb | ||||
| - ee/spec/services/epics/update_dates_service_spec.rb | ||||
| - ee/spec/services/gitlab_subscriptions/trials/create_service_spec.rb | ||||
| - ee/spec/services/registrations/import_namespace_create_service_spec.rb | ||||
| - ee/spec/services/registrations/standard_namespace_create_service_spec.rb | ||||
| - spec/controllers/admin/groups_controller_spec.rb | ||||
| - spec/controllers/admin/users_controller_spec.rb | ||||
| - spec/controllers/groups_controller_spec.rb | ||||
| - spec/controllers/import/bitbucket_controller_spec.rb | ||||
| - spec/controllers/omniauth_callbacks_controller_spec.rb | ||||
| - spec/controllers/registrations_controller_spec.rb | ||||
| - spec/features/admin/admin_groups_spec.rb | ||||
| - spec/features/dashboard/group_spec.rb | ||||
| - spec/features/file_uploads/group_import_spec.rb | ||||
| - spec/features/groups/import_export/import_file_spec.rb | ||||
| - spec/features/groups_spec.rb | ||||
| - spec/frontend/fixtures/groups.rb | ||||
| - spec/graphql/types/group_type_spec.rb | ||||
| - spec/graphql/types/project_type_spec.rb | ||||
| - spec/lib/gitlab/auth/atlassian/user_spec.rb | ||||
| - spec/lib/gitlab/auth/ldap/user_spec.rb | ||||
| - spec/lib/gitlab/auth/o_auth/user_spec.rb | ||||
| - spec/lib/gitlab/auth/saml/user_spec.rb | ||||
| - spec/lib/gitlab/import/placeholder_user_creator_spec.rb | ||||
| - spec/lib/gitlab/import/source_user_mapper_spec.rb | ||||
| - spec/lib/gitlab/seeders/ci/runner/runner_fleet_seeder_spec.rb | ||||
| - spec/models/hooks/system_hook_spec.rb | ||||
| - spec/requests/api/groups_spec.rb | ||||
| - spec/requests/import/gitlab_groups_controller_spec.rb | ||||
| - spec/services/users/create_service_spec.rb | ||||
| - spec/services/users/registrations_build_service_spec.rb | ||||
| - spec/services/groups/create_service_spec.rb | ||||
| - spec/services/groups/nested_create_service_spec.rb | ||||
| - spec/services/resource_access_tokens/create_service_spec.rb | ||||
| - spec/tasks/gitlab/seed/runner_fleet_rake_spec.rb | ||||
| - spec/tasks/gitlab/update_templates_rake_spec.rb | ||||
|  | @ -0,0 +1,32 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module DisableNamespaceOrganizationValidationHelper | ||||
|   extend ActiveSupport::Concern | ||||
| 
 | ||||
|   SPECS_FOR_CODE_TO_FIX = File.join(__dir__, 'disable_namespace_organization_validation.yml') | ||||
| 
 | ||||
|   class << self | ||||
|     include Gitlab::Utils::StrongMemoize | ||||
| 
 | ||||
|     def todo_list | ||||
|       YAML.load_file(SPECS_FOR_CODE_TO_FIX).filter_map { |path| full_path(path) } || [] | ||||
|     end | ||||
|     strong_memoize_attr :todo_list | ||||
| 
 | ||||
|     def full_path(path) | ||||
|       return unless File.exist?(path) | ||||
| 
 | ||||
|       Pathname.new(path).realpath.to_s | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   included do | ||||
|     spec_file = Pathname.new(example.metadata[:example_group][:file_path]).realpath.to_s | ||||
| 
 | ||||
|     if spec_file.in?(DisableNamespaceOrganizationValidationHelper.todo_list) | ||||
|       around do |example| | ||||
|         ::Gitlab::SafeRequestStore.ensure_request_store { example.run } | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
		Loading…
	
		Reference in New Issue