diff --git a/app/assets/javascripts/content_editor/extensions/copy_paste.js b/app/assets/javascripts/content_editor/extensions/copy_paste.js index 23f2da7bc28..1164e7ead33 100644 --- a/app/assets/javascripts/content_editor/extensions/copy_paste.js +++ b/app/assets/javascripts/content_editor/extensions/copy_paste.js @@ -172,6 +172,8 @@ export default Extension.create({ return true; } + if (!textContent) return false; + const hasHTML = clipboardData.types.some((type) => type === HTML_FORMAT); const hasVsCode = clipboardData.types.some((type) => type === VS_CODE_FORMAT); const vsCodeMeta = hasVsCode ? JSON.parse(clipboardData.getData(VS_CODE_FORMAT)) : {}; diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index b11f7b1ba76..f1e46262b2f 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -3,6 +3,7 @@ import '~/lib/utils/jquery_at_who'; import { escape as lodashEscape, sortBy, template, escapeRegExp } from 'lodash'; import * as Emoji from '~/emoji'; import axios from '~/lib/utils/axios_utils'; +import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import { loadingIconForLegacyJS } from '~/loading_icon_for_legacy_js'; import { s__, __, sprintf } from '~/locale'; import { isUserBusy } from '~/set_status_modal/utils'; @@ -26,6 +27,8 @@ export const CONTACT_STATE_ACTIVE = 'active'; export const CONTACTS_ADD_COMMAND = '/add_contacts'; export const CONTACTS_REMOVE_COMMAND = '/remove_contacts'; +const useMentionsBackendFiltering = window.gon.features?.mentionAutocompleteBackendFiltering; + /** * Escapes user input before we pass it to at.js, which * renders it as HTML in the autocomplete dropdown. @@ -62,6 +65,8 @@ export function showAndHideHelper($input, alias = '') { }); } +// This should be kept in sync with the backend filtering in +// `User#gfm_autocomplete_search` and `Namespace#gfm_autocomplete_search` function createMemberSearchString(member) { return `${member.name.replace(/ /g, '')} ${member.username}`; } @@ -344,6 +349,7 @@ class GfmAutoComplete { } setupMembers($input) { + const instance = this; const fetchData = this.fetchData.bind(this); const MEMBER_COMMAND = { ASSIGN: '/assign', @@ -383,6 +389,7 @@ class GfmAutoComplete { // eslint-disable-next-line no-template-curly-in-string insertTpl: '${atwho-at}${username}', limit: 10, + delay: useMentionsBackendFiltering ? DEFAULT_DEBOUNCE_AND_THROTTLE_MS : null, searchKey: 'search', alwaysHighlightFirst: true, skipSpecialCharacterTest: true, @@ -409,16 +416,19 @@ class GfmAutoComplete { const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers); return match && match.length ? match[1] : null; }, - filter(query, data, searchKey) { - if (GfmAutoComplete.isLoading(data)) { + filter(query, data) { + if (useMentionsBackendFiltering) { + if (GfmAutoComplete.isLoading(data) || instance.previousQuery !== query) { + instance.previousQuery = query; + + fetchData(this.$inputor, this.at, query); + return data; + } + } else if (GfmAutoComplete.isLoading(data)) { fetchData(this.$inputor, this.at); return data; } - if (data === GfmAutoComplete.defaultLoadingData) { - return $.fn.atwho.default.callbacks.filter(query, data, searchKey); - } - if (command === MEMBER_COMMAND.ASSIGN) { // Only include members which are not assigned to Issuable currently return data.filter((member) => !assignees.includes(member.search)); @@ -988,6 +998,11 @@ GfmAutoComplete.atTypeMap = { }; GfmAutoComplete.typesWithBackendFiltering = ['vulnerabilities']; + +if (useMentionsBackendFiltering) { + GfmAutoComplete.typesWithBackendFiltering.push('members'); +} + GfmAutoComplete.isTypeWithBackendFiltering = (type) => GfmAutoComplete.typesWithBackendFiltering.includes(GfmAutoComplete.atTypeMap[type]); @@ -1040,6 +1055,8 @@ GfmAutoComplete.Members = { // `member.search` is a name:username string like `MargeSimpson msimpson` return member.search.toLowerCase().includes(query); }, + // This should be kept in sync with the backend sorting in + // `User#gfm_autocomplete_search` and `Namespace#gfm_autocomplete_search` sort(query, members) { const lowercaseQuery = query.toLowerCase(); const { nameOrUsernameStartsWith, nameOrUsernameIncludes } = GfmAutoComplete.Members; diff --git a/app/controllers/groups/autocomplete_sources_controller.rb b/app/controllers/groups/autocomplete_sources_controller.rb index 7a490b34511..191720f69a0 100644 --- a/app/controllers/groups/autocomplete_sources_controller.rb +++ b/app/controllers/groups/autocomplete_sources_controller.rb @@ -10,7 +10,7 @@ class Groups::AutocompleteSourcesController < Groups::ApplicationController urgency :low, [:issues, :labels, :milestones, :commands, :merge_requests, :members] def members - render json: ::Groups::ParticipantsService.new(@group, current_user).execute(target) + render json: ::Groups::ParticipantsService.new(@group, current_user, params).execute(target) end def issues diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb index ff3484d3020..dc10004c62b 100644 --- a/app/controllers/projects/autocomplete_sources_controller.rb +++ b/app/controllers/projects/autocomplete_sources_controller.rb @@ -15,7 +15,7 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController urgency :low, [:issues, :labels, :milestones, :commands, :contacts] def members - render json: ::Projects::ParticipantsService.new(@project, current_user).execute(target) + render json: ::Projects::ParticipantsService.new(@project, current_user, params).execute(target) end def issues diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index d0eabf8d837..c1de24f300b 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -71,6 +71,7 @@ class Projects::IssuesController < Projects::ApplicationController push_frontend_feature_flag(:display_work_item_epic_issue_sidebar, project) push_force_frontend_feature_flag(:linked_work_items, project.linked_work_items_feature_flag_enabled?) push_frontend_feature_flag(:notifications_todos_buttons, current_user) + push_frontend_feature_flag(:mention_autocomplete_backend_filtering, project) end around_action :allow_gitaly_ref_name_caching, only: [:discussions] diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 0899e303305..09ba6e40efd 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -46,6 +46,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:notifications_todos_buttons, current_user) push_frontend_feature_flag(:mr_request_changes, current_user) push_frontend_feature_flag(:merge_blocked_component, current_user) + push_frontend_feature_flag(:mention_autocomplete_backend_filtering, project) end around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :diffs, :discussions] diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index e3ead1b04d0..71e5ea6de48 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -18,7 +18,7 @@ class GroupMember < Member default_scope { where(source_type: SOURCE_TYPE) } # rubocop:disable Cop/DefaultScope - scope :of_groups, ->(groups) { where(source_id: groups&.select(:id)) } + scope :of_groups, ->(groups) { where(source_id: groups) } scope :of_ldap_type, -> { where(ldap: true) } scope :count_users_by_group_id, -> { group(:source_id).count } diff --git a/app/models/namespace.rb b/app/models/namespace.rb index c665c2278a5..0b6a5b3752d 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -263,6 +263,28 @@ class Namespace < ApplicationRecord end end + # This should be kept in sync with the frontend filtering in + # https://gitlab.com/gitlab-org/gitlab/-/blob/5d34e3488faa3982d30d7207773991c1e0b6368a/app/assets/javascripts/gfm_auto_complete.js#L68 and + # https://gitlab.com/gitlab-org/gitlab/-/blob/5d34e3488faa3982d30d7207773991c1e0b6368a/app/assets/javascripts/gfm_auto_complete.js#L1053 + def gfm_autocomplete_search(query) + without_project_namespaces + .allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/420046") + .joins(:route) + .where( + "REPLACE(routes.name, ' ', '') ILIKE :pattern OR routes.path ILIKE :pattern", + pattern: "%#{sanitize_sql_like(query)}%" + ) + .order( + Arel.sql(sanitize_sql( + [ + "CASE WHEN starts_with(REPLACE(routes.name, ' ', ''), :pattern) OR starts_with(routes.path, :pattern) THEN 1 ELSE 2 END", + { pattern: query } + ] + )), + 'routes.path' + ) + end + def clean_path(path, limited_to: Namespace.all) slug = Gitlab::Slug::Path.new(path).generate path = Namespaces::RandomizedSuffixPath.new(slug) diff --git a/app/models/resource_milestone_event.rb b/app/models/resource_milestone_event.rb index d305a4ace51..2b93334f721 100644 --- a/app/models/resource_milestone_event.rb +++ b/app/models/resource_milestone_event.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class ResourceMilestoneEvent < ResourceTimeboxEvent + include EachBatch + belongs_to :milestone scope :include_relations, -> { includes(:user, milestone: [:project, :group]) } diff --git a/app/models/user.rb b/app/models/user.rb index 8ae89d60b0b..3770a14bdc1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -847,6 +847,25 @@ class User < MainClusterwide::ApplicationRecord scope.reorder(order) end + # This should be kept in sync with the frontend filtering in + # https://gitlab.com/gitlab-org/gitlab/-/blob/5d34e3488faa3982d30d7207773991c1e0b6368a/app/assets/javascripts/gfm_auto_complete.js#L68 and + # https://gitlab.com/gitlab-org/gitlab/-/blob/5d34e3488faa3982d30d7207773991c1e0b6368a/app/assets/javascripts/gfm_auto_complete.js#L1053 + def gfm_autocomplete_search(query) + where( + "REPLACE(users.name, ' ', '') ILIKE :pattern OR users.username ILIKE :pattern", + pattern: "%#{sanitize_sql_like(query)}%" + ).order( + Arel.sql(sanitize_sql( + [ + "CASE WHEN starts_with(REPLACE(users.name, ' ', ''), :pattern) OR starts_with(users.username, :pattern) THEN 1 ELSE 2 END", + { pattern: query } + ] + )), + :username, + :id + ) + end + # Limits the result set to users _not_ in the given query/list of IDs. # # users - The list of users to ignore. This can be an diff --git a/app/services/concerns/users/participable_service.rb b/app/services/concerns/users/participable_service.rb index a54c4947b0b..f84793d869c 100644 --- a/app/services/concerns/users/participable_service.rb +++ b/app/services/concerns/users/participable_service.rb @@ -3,6 +3,9 @@ module Users module ParticipableService extend ActiveSupport::Concern + include Gitlab::Utils::StrongMemoize + + SEARCH_LIMIT = 10 included do attr_reader :noteable @@ -25,6 +28,16 @@ module Users sorted(users) end + def filter_and_sort_users(users_relation) + if params[:search] + users_relation.gfm_autocomplete_search(params[:search]).limit(SEARCH_LIMIT).tap do |users| + preload_status(users) + end + else + sorted(users_relation) + end + end + def sorted(users) users.uniq.to_a.compact.sort_by(&:username).tap do |users| preload_status(users) @@ -34,8 +47,15 @@ module Users def groups return [] unless current_user - current_user.authorized_groups.with_route.sort_by(&:full_path) + relation = current_user.authorized_groups + + if params[:search] + relation.gfm_autocomplete_search(params[:search]).limit(SEARCH_LIMIT).to_a + else + relation.with_route.sort_by(&:full_path) + end end + strong_memoize_attr :groups def render_participants_as_hash(participants) participants.map { |participant| participant_as_hash(participant) } @@ -74,11 +94,14 @@ module Users end def group_counts - @group_counts ||= GroupMember - .of_groups(current_user.authorized_groups) + groups_for_count = params[:search] ? groups : current_user.authorized_groups + + GroupMember + .of_groups(groups_for_count) .non_request .count_users_by_group_id end + strong_memoize_attr :group_counts def preload_status(users) users.each { |u| lazy_user_availability(u) } diff --git a/app/services/groups/participants_service.rb b/app/services/groups/participants_service.rb index 7b68b435f14..ae1a917f022 100644 --- a/app/services/groups/participants_service.rb +++ b/app/services/groups/participants_service.rb @@ -29,7 +29,9 @@ module Groups def group_hierarchy_users return [] unless group - sorted(Autocomplete::GroupUsersFinder.new(group: group).execute) + relation = Autocomplete::GroupUsersFinder.new(group: group).execute + + filter_and_sort_users(relation) end end end diff --git a/app/services/import/github_service.rb b/app/services/import/github_service.rb index a96bfd74cd0..e28c58794a9 100644 --- a/app/services/import/github_service.rb +++ b/app/services/import/github_service.rb @@ -139,7 +139,8 @@ module Import .new(project) .write( timeout_strategy: params[:timeout_strategy] || ProjectImportData::PESSIMISTIC_TIMEOUT, - optional_stages: params[:optional_stages] + optional_stages: params[:optional_stages], + extended_events: Feature.enabled?(:github_import_extended_events, current_user) ) end end diff --git a/app/services/milestones/promote_service.rb b/app/services/milestones/promote_service.rb index 4417f17f33e..d657b8b3255 100644 --- a/app/services/milestones/promote_service.rb +++ b/app/services/milestones/promote_service.rb @@ -63,9 +63,12 @@ module Milestones def update_children(group_milestone, milestone_ids) issues = Issue.where(project_id: group_project_ids, milestone_id: milestone_ids) merge_requests = MergeRequest.where(source_project_id: group_project_ids, milestone_id: milestone_ids) + milestone_events = ResourceMilestoneEvent.where(milestone_id: milestone_ids) - [issues, merge_requests].each do |issuable_collection| - issuable_collection.update_all(milestone_id: group_milestone.id) + [issues, merge_requests, milestone_events].each do |collection| + collection.each_batch do |batch| + batch.update_all(milestone_id: group_milestone.id) + end end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index fe19d1f051d..188f12a287b 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -18,13 +18,17 @@ module Projects end def project_members - @project_members ||= sorted(project.authorized_users) + filter_and_sort_users(project_members_relation) end def all_members return [] if Feature.enabled?(:disable_all_mention) - [{ username: "all", name: "All Project and Group Members", count: project_members.count }] + [{ username: "all", name: "All Project and Group Members", count: project_members_relation.count }] + end + + def project_members_relation + project.authorized_users end end end diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 97acafe24d0..0f56ae92557 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -12,4 +12,4 @@ cancel_path: cancel_import_github_path, details_path: details_import_github_path, status_import_github_group_path: status_import_github_group_path(format: :json), - optional_stages: Gitlab::GithubImport::Settings.stages_array + optional_stages: Gitlab::GithubImport::Settings.stages_array(current_user) diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index ec5156bb1d0..e4329ae2a8b 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -1353,6 +1353,15 @@ :weight: 1 :idempotent: false :tags: [] +- :name: github_importer:github_import_replay_events + :worker_name: Gitlab::GithubImport::ReplayEventsWorker + :feature_category: :importers + :has_external_dependencies: true + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: github_importer:github_import_stage_finish_import :worker_name: Gitlab::GithubImport::Stage::FinishImportWorker :feature_category: :importers diff --git a/app/workers/gitlab/github_import/advance_stage_worker.rb b/app/workers/gitlab/github_import/advance_stage_worker.rb index 417b8598547..805252927ff 100644 --- a/app/workers/gitlab/github_import/advance_stage_worker.rb +++ b/app/workers/gitlab/github_import/advance_stage_worker.rb @@ -22,14 +22,20 @@ module Gitlab sidekiq_options dead: false # The known importer stages and their corresponding Sidekiq workers. + # + # Note: AdvanceStageWorker is not used for the repository, base_data, and pull_requests stages. + # They are included in the list for us to easily see all stage workers and the order in which they are executed. STAGES = { + repository: Stage::ImportRepositoryWorker, + base_data: Stage::ImportBaseDataWorker, + pull_requests: Stage::ImportPullRequestsWorker, collaborators: Stage::ImportCollaboratorsWorker, - pull_requests_merged_by: Stage::ImportPullRequestsMergedByWorker, - pull_request_review_requests: Stage::ImportPullRequestsReviewRequestsWorker, - pull_request_reviews: Stage::ImportPullRequestsReviewsWorker, + pull_requests_merged_by: Stage::ImportPullRequestsMergedByWorker, # Skipped on extended_events + pull_request_review_requests: Stage::ImportPullRequestsReviewRequestsWorker, # Skipped on extended_events + pull_request_reviews: Stage::ImportPullRequestsReviewsWorker, # Skipped on extended_events issues_and_diff_notes: Stage::ImportIssuesAndDiffNotesWorker, issue_events: Stage::ImportIssueEventsWorker, - notes: Stage::ImportNotesWorker, + notes: Stage::ImportNotesWorker, # Skipped on extended_events attachments: Stage::ImportAttachmentsWorker, protected_branches: Stage::ImportProtectedBranchesWorker, lfs_objects: Stage::ImportLfsObjectsWorker, diff --git a/app/workers/gitlab/github_import/replay_events_worker.rb b/app/workers/gitlab/github_import/replay_events_worker.rb new file mode 100644 index 00000000000..680d5ec2d7d --- /dev/null +++ b/app/workers/gitlab/github_import/replay_events_worker.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Gitlab + module GithubImport + class ReplayEventsWorker + include ObjectImporter + + idempotent! + + def representation_class + Representation::ReplayEvent + end + + def importer_class + Importer::ReplayEventsImporter + end + + def object_type + :replay_event + end + + def increment_object_counter?(_object) + false + end + end + end +end diff --git a/app/workers/gitlab/github_import/stage/import_collaborators_worker.rb b/app/workers/gitlab/github_import/stage/import_collaborators_worker.rb index b5b1601e3ed..38e1fd52889 100644 --- a/app/workers/gitlab/github_import/stage/import_collaborators_worker.rb +++ b/app/workers/gitlab/github_import/stage/import_collaborators_worker.rb @@ -42,9 +42,15 @@ module Gitlab def move_to_next_stage(project, waiters = {}) AdvanceStageWorker.perform_async( - project.id, waiters.deep_stringify_keys, 'pull_requests_merged_by' + project.id, waiters.deep_stringify_keys, next_stage(project) ) end + + def next_stage(project) + return 'issues_and_diff_notes' if import_settings(project).extended_events? + + 'pull_requests_merged_by' + end end end end diff --git a/app/workers/gitlab/github_import/stage/import_issue_events_worker.rb b/app/workers/gitlab/github_import/stage/import_issue_events_worker.rb index 27d14a1a108..9618500604a 100644 --- a/app/workers/gitlab/github_import/stage/import_issue_events_worker.rb +++ b/app/workers/gitlab/github_import/stage/import_issue_events_worker.rb @@ -15,7 +15,7 @@ module Gitlab # client - An instance of Gitlab::GithubImport::Client. # project - An instance of Project. def import(client, project) - return skip_to_next_stage(project) if import_settings(project).disabled?(:single_endpoint_issue_events_import) + return skip_to_next_stage(project) if skip_to_next_stage?(project) importer = ::Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter info(project.id, message: "starting importer", importer: importer.name) @@ -25,13 +25,26 @@ module Gitlab private + def skip_to_next_stage?(project) + # This stage is mandatory when using extended_events + return false if import_settings(project).extended_events? + + import_settings(project).disabled?(:single_endpoint_issue_events_import) + end + def skip_to_next_stage(project) info(project.id, message: "skipping importer", importer: "IssueEventsImporter") move_to_next_stage(project) end def move_to_next_stage(project, waiters = {}) - AdvanceStageWorker.perform_async(project.id, waiters.deep_stringify_keys, 'notes') + AdvanceStageWorker.perform_async(project.id, waiters.deep_stringify_keys, next_stage(project)) + end + + def next_stage(project) + return "attachments" if import_settings(project).extended_events? + + "notes" end end end diff --git a/config/feature_flags/development/github_import_extended_events.yml b/config/feature_flags/development/github_import_extended_events.yml new file mode 100644 index 00000000000..e4466bf958f --- /dev/null +++ b/config/feature_flags/development/github_import_extended_events.yml @@ -0,0 +1,8 @@ +--- +name: github_import_extended_events +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139410 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/435089 +milestone: '16.8' +type: development +group: group::import and integrate +default_enabled: false diff --git a/config/feature_flags/development/mention_autocomplete_backend_filtering.yml b/config/feature_flags/development/mention_autocomplete_backend_filtering.yml new file mode 100644 index 00000000000..b971308fe67 --- /dev/null +++ b/config/feature_flags/development/mention_autocomplete_backend_filtering.yml @@ -0,0 +1,8 @@ +--- +name: mention_autocomplete_backend_filtering +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131250 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/435106 +milestone: '16.7' +type: development +group: group::project management +default_enabled: false diff --git a/db/post_migrate/20231204042048_finalize_system_note_metadata_bigint_conversion.rb b/db/post_migrate/20231204042048_finalize_system_note_metadata_bigint_conversion.rb index 2238c4e34c5..cd6550d6bad 100644 --- a/db/post_migrate/20231204042048_finalize_system_note_metadata_bigint_conversion.rb +++ b/db/post_migrate/20231204042048_finalize_system_note_metadata_bigint_conversion.rb @@ -6,7 +6,7 @@ class FinalizeSystemNoteMetadataBigintConversion < Gitlab::Database::Migration[2 disable_ddl_transaction! restrict_gitlab_migration gitlab_schema: :gitlab_main - milestone '16.7' + milestone '16.8' TABLE_NAME = :system_note_metadata diff --git a/db/post_migrate/20231219044006_drop_note_mentions_temp_index.rb b/db/post_migrate/20231219044006_drop_note_mentions_temp_index.rb new file mode 100644 index 00000000000..948530d4c02 --- /dev/null +++ b/db/post_migrate/20231219044006_drop_note_mentions_temp_index.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class DropNoteMentionsTempIndex < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + + milestone '16.8' + + INDEX_NAME = 'note_mentions_temp_index' + + def up + remove_concurrent_index_by_name :notes, INDEX_NAME + end + + def down + add_concurrent_index :notes, [:id, :noteable_type], where: "note ~~ '%@%'::text", name: INDEX_NAME + end +end diff --git a/db/schema_migrations/20231219044006 b/db/schema_migrations/20231219044006 new file mode 100644 index 00000000000..87c1c9fb980 --- /dev/null +++ b/db/schema_migrations/20231219044006 @@ -0,0 +1 @@ +d48e67240f21ae70e7bae4d1c28fd2070b82e6415539da302248897847678915 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index af8a2cde1a2..775df880e1b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -35341,8 +35341,6 @@ CREATE UNIQUE INDEX merge_request_user_mentions_on_mr_id_and_note_id_index ON me CREATE UNIQUE INDEX merge_request_user_mentions_on_mr_id_index ON merge_request_user_mentions USING btree (merge_request_id) WHERE (note_id IS NULL); -CREATE INDEX note_mentions_temp_index ON notes USING btree (id, noteable_type) WHERE (note ~~ '%@%'::text); - CREATE UNIQUE INDEX one_canonical_wiki_page_slug_per_metadata ON wiki_page_slugs USING btree (wiki_page_meta_id) WHERE (canonical = true); CREATE INDEX p_ci_builds_scheduled_at_idx ON ONLY p_ci_builds USING btree (scheduled_at) WHERE ((scheduled_at IS NOT NULL) AND ((type)::text = 'Ci::Build'::text) AND ((status)::text = 'scheduled'::text)); diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md index 56397a29d47..36b1ca5dec5 100644 --- a/doc/user/application_security/sast/index.md +++ b/doc/user/application_security/sast/index.md @@ -593,6 +593,7 @@ Some analyzers can be customized with CI/CD variables. | `PHPCS_SECURITY_AUDIT_PHP_EXTENSIONS` | phpcs-security-audit | Comma separated list of additional PHP Extensions. | | `SAST_SEMGREP_METRICS` | Semgrep | Set to `"false"` to disable sending anonymized scan metrics to [r2c](https://semgrep.dev). Default: `true`. Introduced in GitLab 14.0. GitLab team members can view more information in this confidential issue: `https://gitlab.com/gitlab-org/gitlab/-/issues/330565`. | | `SAST_SCANNER_ALLOWED_CLI_OPTS` | Semgrep | CLI options (arguments with value, or flags) that are passed to the underlying security scanner when running scan operation. Only a limited set of [options](#security-scanner-configuration) are accepted. Separate a CLI option and its value using either a blank space or equals (`=`) character. For example: `name1 value1` or `name1=value1`. Multiple options must be separated by blank spaces. For example: `name1 value1 name2 value2`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368565) in GitLab 15.3. | +| `SAST_RULESET_GIT_REFERENCE` | Semgrep and nodejs-scan | Defines a path to a custom ruleset configuration. If a project has a `.gitlab/sast-ruleset.toml` file committed, that local configuration takes precedence and the file from `SAST_RULESET_GIT_REFERENCE` isn’t used. This variable is available for the Ultimate tier only. | #### Security scanner configuration diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md index 5be37bffdfe..a82f0019d19 100644 --- a/doc/user/project/web_ide/index.md +++ b/doc/user/project/web_ide/index.md @@ -17,7 +17,7 @@ The Web IDE is an advanced editor with commit staging. You can use the Web IDE to make changes to multiple files directly from the GitLab UI. For a more basic implementation, see [Web Editor](../repository/web_editor.md). -To pair the Web IDE with a remote development environment, see [remote development](../remote_development/index.md). +To pair the Web IDE with a remote development environment, see [Remote development](../remote_development/index.md). ## Open the Web IDE @@ -45,7 +45,7 @@ To open the Web IDE from a merge request: The Web IDE opens new and modified files in separate tabs and displays changes side by side. To reduce load time, only 10 files with the most lines changed are opened automatically. -In the **Explorer** panel, any new or modified file is indicated +On the left **Explorer** sidebar, any new or modified file is indicated by the merge request icon (**{merge-request}**) next to the file name. To view changes to a file, right-click the file and select **Compare with merge request base**. @@ -63,12 +63,12 @@ To search across open files in the Web IDE: 1. Press Shift+Command+F. 1. In the search box, enter your search term. -## View a list of changed files +## View a list of modified files -To view a list of files you changed in the Web IDE: +To view a list of files you modified in the Web IDE: -- On the activity bar on the left, select **Source Control**, - or press Control+Shift+G. +- On the left activity bar, select **Source Control**, or + press Control+Shift+G. Your `CHANGES`, `STAGED CHANGES`, and `MERGE CHANGES` are displayed. For more information, see the [VS Code documentation](https://code.visualstudio.com/docs/sourcecontrol/overview#_commit). @@ -91,10 +91,11 @@ To restore uncommitted changes in the Web IDE: To upload a file in the Web IDE: -1. On the activity bar on the left, select **Explorer** and - go to the directory where you want to upload the file. -1. Optional. To create a new directory, in the upper right of the - **Explorer** panel, select **New Folder** (**{folder-new}**). +1. On the left activity bar, select **Explorer**, or + press Shift+Command+E. +1. Go to the directory where you want to upload the file. + - To create a new directory, on the left **Explorer** sidebar, + in the upper right, select **New Folder** (**{folder-new}**). 1. Right-click the directory and select **Upload**. 1. Select the file you want to upload. @@ -106,14 +107,14 @@ The new files are uploaded and automatically added to the repository. The Web IDE uses the current branch by default. To switch branches in the Web IDE: -1. On the status bar, in the lower-left corner, select the current branch name. +1. On the bottom status bar, on the left, select the current branch name. 1. Enter or select an existing branch. ## Create a branch To create a branch from the current branch in the Web IDE: -1. On the status bar, in the lower-left corner, select the current branch name. +1. On the bottom status bar, on the left, select the current branch name. 1. From the dropdown list, select **Create new branch**. 1. Enter the new branch name. @@ -123,8 +124,8 @@ If you do not have write access to the repository, **Create new branch** is not To commit changes in the Web IDE: -1. On the activity bar on the left, select **Source Control**, - or press Control+Shift+G. +1. On the left activity bar, select **Source Control**, or + press Control+Shift+G. 1. Enter your commit message. 1. Commit to the current branch or [create a new branch](#create-a-branch). @@ -133,7 +134,7 @@ To commit changes in the Web IDE: To create a [merge request](../merge_requests/index.md) in the Web IDE: 1. [Commit the changes](#commit-changes). -1. In the notification in the lower-right corner, select **Create MR**. +1. In the notification that appears in the lower right, select **Create MR**. For more information, see [View missed notifications](#view-missed-notifications). @@ -172,12 +173,12 @@ In the keyboard shortcuts editor, you can search for: Keybindings are based on your keyboard layout. If you change your keyboard layout, existing keybindings are updated automatically. -## Change themes +## Change the color theme -You can choose between different themes for the Web IDE. -The default theme for the Web IDE is **GitLab Dark**. +You can choose between different color themes for the Web IDE. +The default theme is **GitLab Dark**. -To change the Web IDE theme: +To change the color theme in the Web IDE: 1. On the top menu bar, select **File > Preferences > Theme > Color Theme**, or press Command+K then Command+T. @@ -188,10 +189,10 @@ The active color theme is stored in the [user settings](#edit-settings). ## View missed notifications -When you perform actions in the Web IDE, notifications appear in the lower-right corner. +When you perform actions in the Web IDE, notifications appear in the lower right. To view any notification you might have missed: -1. On the status bar, in the lower-right corner, select the bell icon (**{notifications}**) for a list of notifications. +1. On the bottom status bar, on the right, select the bell icon (**{notifications}**) for a list of notifications. 1. Select the notification you want to view.