Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-09-11 18:10:00 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-09-11 18:10:00 +0300
commitd06e4d855aed9148571c7fd95fe02f208a017dd3 (patch)
tree83a1fe4bc449255b426f6b87fa4a37732a5c1fa1
parent320d8adff14c100cd8a6798880b7eeff8e137f15 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/integrations/index/components/integrations_table.vue35
-rw-r--r--app/assets/stylesheets/_page_specific_files.scss1
-rw-r--r--app/assets/stylesheets/pages/projects.scss (renamed from app/assets/stylesheets/page_bundles/projects.scss)6
-rw-r--r--app/finders/ci/jobs_finder.rb22
-rw-r--r--app/finders/groups/accepting_group_transfers_finder.rb12
-rw-r--r--app/finders/groups/base.rb4
-rw-r--r--app/finders/packages/npm/packages_for_user_finder.rb18
-rw-r--r--app/graphql/resolvers/ci/all_jobs_resolver.rb11
-rw-r--r--app/graphql/types/ci/job_failure_reason_enum.rb15
-rw-r--r--app/models/namespace.rb18
-rw-r--r--app/views/admin/projects/index.html.haml1
-rw-r--r--app/views/admin/projects/show.html.haml1
-rw-r--r--app/views/dashboard/projects/index.html.haml1
-rw-r--r--app/views/groups/projects.html.haml1
-rw-r--r--app/views/layouts/header/_super_sidebar_logged_out.haml2
-rw-r--r--app/views/layouts/project_settings.html.haml1
-rw-r--r--app/views/projects/blame/show.html.haml1
-rw-r--r--app/views/projects/blob/_blob.html.haml1
-rw-r--r--app/views/projects/commits/show.html.haml1
-rw-r--r--app/views/projects/find_file/show.html.haml1
-rw-r--r--app/views/projects/new.html.haml1
-rw-r--r--app/views/projects/tree/_tree_header.html.haml2
-rw-r--r--config/application.rb1
-rw-r--r--config/feature_flags/development/exact_matches_first_group_transfer.yml8
-rw-r--r--config/feature_flags/development/loose_foreign_keys_batch_load_using_union.yml2
-rw-r--r--db/migrate/20230901170145_update_vulnerability_reads_trigger_to_set_has_merge_request.rb211
-rw-r--r--db/migrate/20230905061815_add_foreign_key_for_ci_pipeline_messages_pipeline_id_bigint.rb20
-rw-r--r--db/migrate/20230907162613_add_force_full_reconciliation_to_workspaces.rb9
-rw-r--r--db/post_migrate/20230903064537_add_ci_job_artifacts_file_final_path_index.rb17
-rw-r--r--db/post_migrate/20230905071915_prepare_async_foreign_key_for_ci_pipeline_messages_pipeline_id_bigint.rb15
-rw-r--r--db/post_migrate/20230906181457_add_index_to_violations_on_target_proj_id.rb16
-rw-r--r--db/schema_migrations/202309011701451
-rw-r--r--db/schema_migrations/202309030645371
-rw-r--r--db/schema_migrations/202309050618151
-rw-r--r--db/schema_migrations/202309050719151
-rw-r--r--db/schema_migrations/202309061814571
-rw-r--r--db/schema_migrations/202309071626131
-rw-r--r--db/structure.sql24
-rw-r--r--doc/api/graphql/reference/index.md42
-rw-r--r--doc/architecture/blueprints/cells/diagrams/cells-and-fulfillment.drawio.pngbin192221 -> 43255 bytes
-rw-r--r--doc/architecture/blueprints/cells/diagrams/term-cell.drawio.pngbin93379 -> 31802 bytes
-rw-r--r--doc/architecture/blueprints/cells/diagrams/term-cluster.drawio.pngbin436724 -> 102686 bytes
-rw-r--r--doc/architecture/blueprints/cells/diagrams/term-organization.drawio.pngbin169719 -> 39566 bytes
-rw-r--r--doc/architecture/blueprints/cells/diagrams/term-top-level-group.drawio.pngbin65137 -> 15865 bytes
-rw-r--r--doc/architecture/blueprints/code_search_with_zoekt/diagrams/sharding_proposal_2023-08.drawio.pngbin562288 -> 167701 bytes
-rw-r--r--doc/architecture/blueprints/gitaly_adaptive_concurrency_limit/adaptive_concurrency_limit_flow.pngbin129675 -> 36052 bytes
-rw-r--r--doc/ci/migration/examples/img/maven-freestyle-plugin.pngbin47991 -> 16615 bytes
-rw-r--r--doc/ci/migration/examples/img/maven-freestyle-shell.pngbin62207 -> 21651 bytes
-rw-r--r--doc/operations/img/tracing_details_v16_3.pngbin128734 -> 29316 bytes
-rw-r--r--doc/user/analytics/img/issues_closed_analytics_v16_4.pngbin63447 -> 17190 bytes
-rw-r--r--doc/user/analytics/index.md1
-rw-r--r--doc/user/gitlab_com/index.md2
-rw-r--r--doc/user/group/issues_analytics/img/issues_closed_analytics_v16_4.pngbin63447 -> 17190 bytes
-rw-r--r--doc/user/project/working_with_projects.md6
-rw-r--r--lib/api/concerns/packages/npm_endpoints.rb27
-rw-r--r--lib/api/npm_group_packages.rb4
-rw-r--r--lib/api/npm_instance_packages.rb4
-rw-r--r--lib/gitlab/sql/pattern.rb20
-rw-r--r--lib/gitlab/utils/override.rb3
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/finders/ci/jobs_finder_spec.rb38
-rw-r--r--spec/finders/groups/accepting_group_transfers_finder_spec.rb33
-rw-r--r--spec/finders/packages/npm/packages_for_user_finder_spec.rb41
-rw-r--r--spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb18
-rw-r--r--spec/lib/gitlab/sql/pattern_spec.rb46
-rw-r--r--spec/models/namespace_spec.rb17
-rw-r--r--spec/requests/api/npm_group_packages_spec.rb37
-rw-r--r--spec/requests/api/npm_instance_packages_spec.rb29
-rw-r--r--spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb64
-rw-r--r--spec/views/layouts/header/_super_sidebar_logged_out.html.haml_spec.rb4
70 files changed, 758 insertions, 169 deletions
diff --git a/app/assets/javascripts/integrations/index/components/integrations_table.vue b/app/assets/javascripts/integrations/index/components/integrations_table.vue
index 37d622a941e..fc5048490a7 100644
--- a/app/assets/javascripts/integrations/index/components/integrations_table.vue
+++ b/app/assets/javascripts/integrations/index/components/integrations_table.vue
@@ -1,5 +1,12 @@
<script>
-import { GlAvatarLabeled, GlAvatarLink, GlIcon, GlTable, GlTooltipDirective } from '@gitlab/ui';
+import {
+ GlAvatarLabeled,
+ GlAvatarLink,
+ GlButton,
+ GlIcon,
+ GlTable,
+ GlTooltipDirective,
+} from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -8,6 +15,7 @@ export default {
components: {
GlAvatarLabeled,
GlAvatarLink,
+ GlButton,
GlIcon,
GlTable,
TimeAgoTooltip,
@@ -55,13 +63,7 @@ export default {
{
key: 'title',
label: __('Integration'),
- thClass: 'gl-w-quarter gl-xs-w-full',
- },
- {
- key: 'description',
- label: __('Description'),
- thClass: 'gl-display-none d-sm-table-cell',
- tdClass: 'gl-display-none d-sm-table-cell',
+ thClass: 'd-sm-table-cell',
},
);
@@ -69,11 +71,17 @@ export default {
fields.push({
key: 'updated_at',
label: this.showUpdatedAt ? __('Last updated') : '',
- thClass: 'gl-w-20 gl-text-right',
- tdClass: 'gl-text-right',
+ thClass: 'gl-display-none d-sm-table-cell gl-text-right',
+ tdClass: 'gl-text-right gl-display-none d-sm-table-cell gl-vertical-align-middle!',
});
}
+ fields.push({
+ key: 'edit_path',
+ label: '',
+ thClass: 'gl-w-15',
+ });
+
return fields;
},
filteredIntegrations() {
@@ -113,6 +121,7 @@ export default {
>
<gl-avatar-labeled
:label="item.title"
+ :sub-label="item.description"
:entity-id="item.id"
:entity-name="item.title"
:src="item.icon"
@@ -126,5 +135,11 @@ export default {
<template #cell(updated_at)="{ item }">
<time-ago-tooltip v-if="showUpdatedAt && item.updated_at" :time="item.updated_at" />
</template>
+
+ <template #cell(edit_path)="{ item }">
+ <gl-button :href="item.edit_path">
+ {{ __('Configure') }}
+ </gl-button>
+ </template>
</gl-table>
</template>
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index 72351f8ba0c..47701d0490a 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -9,5 +9,6 @@
@import './pages/notes';
@import './pages/pipelines';
@import './pages/profile';
+@import './pages/projects';
@import './pages/registry';
@import './pages/settings';
diff --git a/app/assets/stylesheets/page_bundles/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 68139c55f5a..9ce470dbcf2 100644
--- a/app/assets/stylesheets/page_bundles/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -1,5 +1,3 @@
-@import 'mixins_and_variables_and_functions';
-
.new_project,
.edit-project,
.import-project {
@@ -8,7 +6,7 @@
}
.project-path .form-control {
- border-radius: $gl-border-radius-base;
+ border-radius: $border-radius-base;
}
.input-group {
@@ -49,7 +47,7 @@
}
+ .btn-default {
- border-radius: 0 $gl-border-radius-base $gl-border-radius-base 0;
+ border-radius: 0 $border-radius-base $border-radius-base 0;
}
}
}
diff --git a/app/finders/ci/jobs_finder.rb b/app/finders/ci/jobs_finder.rb
index b35637d0e4f..efacd8143bc 100644
--- a/app/finders/ci/jobs_finder.rb
+++ b/app/finders/ci/jobs_finder.rb
@@ -16,9 +16,7 @@ module Ci
def execute
builds = init_collection.order_id_desc
- builds = filter_by_with_artifacts(builds)
- builds = filter_by_runner_types(builds)
- filter_by_scope(builds)
+ filter_builds(builds)
rescue Gitlab::Access::AccessDeniedError
type.none
end
@@ -59,6 +57,13 @@ module Ci
params[:include_retried] ? jobs_scope : jobs_scope.latest
end
+ # Overriden in EE
+ def filter_builds(builds)
+ builds = filter_by_with_artifacts(builds)
+ builds = filter_by_runner_types(builds)
+ filter_by_scope(builds)
+ end
+
def filter_by_scope(builds)
return filter_by_statuses!(builds) if params[:scope].is_a?(Array)
@@ -80,16 +85,15 @@ module Ci
builds.with_runner_type(params[:runner_type])
end
+ # Overriden in EE
def use_runner_type_filter?
params[:runner_type].present? && Feature.enabled?(:admin_jobs_filter_runner_type, project, type: :ops)
end
def filter_by_with_artifacts(builds)
- if params[:with_artifacts]
- builds.with_any_artifacts
- else
- builds
- end
+ return builds.with_any_artifacts if params[:with_artifacts]
+
+ builds
end
def filter_by_statuses!(builds)
@@ -111,3 +115,5 @@ module Ci
end
end
end
+
+Ci::JobsFinder.prepend_mod
diff --git a/app/finders/groups/accepting_group_transfers_finder.rb b/app/finders/groups/accepting_group_transfers_finder.rb
index c95318d0098..ddf1ed2662a 100644
--- a/app/finders/groups/accepting_group_transfers_finder.rb
+++ b/app/finders/groups/accepting_group_transfers_finder.rb
@@ -14,7 +14,13 @@ module Groups
return Group.none unless can_transfer_group?
items = find_all_groups
- items = by_search(items)
+
+ if exact_matches_first_enabled?
+ # Search will perform an ORDER BY to ensure exact matches are returned first.
+ return by_search(items, exact_matches_first: true) if params[:search].present?
+ else
+ items = by_search(items)
+ end
sort(items)
end
@@ -60,5 +66,9 @@ module Groups
def can_transfer_group?
Ability.allowed?(current_user, :admin_group, group_to_be_transferred)
end
+
+ def exact_matches_first_enabled?
+ Feature.enabled?(:exact_matches_first_group_transfer, group_to_be_transferred)
+ end
end
end
diff --git a/app/finders/groups/base.rb b/app/finders/groups/base.rb
index 9d2f9f60a63..26d2ad85fd4 100644
--- a/app/finders/groups/base.rb
+++ b/app/finders/groups/base.rb
@@ -8,10 +8,10 @@ module Groups
items.reorder(Group.arel_table[:path].asc, Group.arel_table[:id].asc) # rubocop: disable CodeReuse/ActiveRecord
end
- def by_search(items)
+ def by_search(items, exact_matches_first: false)
return items if params[:search].blank?
- items.search(params[:search], include_parents: true)
+ items.search(params[:search], include_parents: true, exact_matches_first: exact_matches_first)
end
end
end
diff --git a/app/finders/packages/npm/packages_for_user_finder.rb b/app/finders/packages/npm/packages_for_user_finder.rb
new file mode 100644
index 00000000000..f42e49f9184
--- /dev/null
+++ b/app/finders/packages/npm/packages_for_user_finder.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Packages
+ module Npm
+ class PackagesForUserFinder < ::Packages::GroupOrProjectPackageFinder
+ def execute
+ packages
+ end
+
+ private
+
+ def packages
+ base.npm
+ .with_name(@params[:package_name])
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci/all_jobs_resolver.rb b/app/graphql/resolvers/ci/all_jobs_resolver.rb
index 85b0f8da877..3012a7defa6 100644
--- a/app/graphql/resolvers/ci/all_jobs_resolver.rb
+++ b/app/graphql/resolvers/ci/all_jobs_resolver.rb
@@ -17,15 +17,18 @@ module Resolvers
description: 'Filter jobs by runner type if ' \
'feature flag `:admin_jobs_filter_runner_type` is enabled.'
- def resolve_with_lookahead(statuses: nil, runner_types: nil)
- jobs = ::Ci::JobsFinder.new(current_user: current_user,
-params: { scope: statuses, runner_type: runner_types }).execute
+ def resolve_with_lookahead(**args)
+ jobs = ::Ci::JobsFinder.new(current_user: current_user, params: params_data(args)).execute
apply_lookahead(jobs)
end
private
+ def params_data(args)
+ { scope: args[:statuses], runner_type: args[:runner_types] }
+ end
+
def preloads
{
previous_stage_jobs_or_needs: [:needs, :pipeline],
@@ -55,3 +58,5 @@ params: { scope: statuses, runner_type: runner_types }).execute
end
end
end
+
+Resolvers::Ci::AllJobsResolver.prepend_mod
diff --git a/app/graphql/types/ci/job_failure_reason_enum.rb b/app/graphql/types/ci/job_failure_reason_enum.rb
new file mode 100644
index 00000000000..3b9c13536d6
--- /dev/null
+++ b/app/graphql/types/ci/job_failure_reason_enum.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class JobFailureReasonEnum < BaseEnum
+ graphql_name 'CiJobFailureReason'
+
+ ::Enums::Ci::CommitStatus.failure_reasons.each_key do |reason|
+ value reason.to_s.upcase,
+ description: "A job that failed due to #{reason.to_s.tr('_', ' ')}.",
+ value: reason
+ end
+ end
+ end
+end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index ced855c7287..e9d5d940458 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -233,16 +233,26 @@ class Namespace < ApplicationRecord
# query - The search query as a String.
#
# Returns an ActiveRecord::Relation.
- def search(query, include_parents: false, use_minimum_char_limit: true)
+ def search(query, include_parents: false, use_minimum_char_limit: true, exact_matches_first: false)
if include_parents
- without_project_namespaces
+ route_columns = [Route.arel_table[:path], Route.arel_table[:name]]
+ namespaces = without_project_namespaces
.where(id: Route.for_routable_type(Namespace.name)
.allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/420046")
- .fuzzy_search(query, [Route.arel_table[:path], Route.arel_table[:name]],
+ .fuzzy_search(query, route_columns,
use_minimum_char_limit: use_minimum_char_limit)
.select(:source_id))
+
+ if exact_matches_first
+ namespaces = namespaces
+ .joins(:route)
+ .allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/420046")
+ .order(exact_matches_first_sql(query, route_columns))
+ end
+
+ namespaces
else
- without_project_namespaces.fuzzy_search(query, [:path, :name], use_minimum_char_limit: use_minimum_char_limit)
+ without_project_namespaces.fuzzy_search(query, [:path, :name], use_minimum_char_limit: use_minimum_char_limit, exact_matches_first: exact_matches_first)
end
end
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 412d8e64e89..31ec4935f64 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,6 +1,5 @@
- page_title _('Projects')
- add_page_specific_style 'page_bundles/search'
-- add_page_specific_style 'page_bundles/projects'
- params[:visibility_level] ||= []
.top-area.gl-flex-direction-column-reverse
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 6be5aa003fc..85dce00752b 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -1,5 +1,4 @@
- add_page_specific_style 'page_bundles/members'
-- add_page_specific_style 'page_bundles/projects'
- add_to_breadcrumbs _("Projects"), admin_projects_path
- breadcrumb_title @project.full_name
- page_title @project.full_name, _("Projects")
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index c74a9f4cbe6..140bc6e06c3 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -5,7 +5,6 @@
- page_title _("Projects")
- add_page_specific_style 'page_bundles/dashboard_projects'
-- add_page_specific_style 'page_bundles/projects'
= render "projects/last_push"
- if show_projects?(@projects, params)
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index 76758769d01..22e9f9f5071 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -1,6 +1,5 @@
- breadcrumb_title _("Projects")
- page_title _("Projects")
-- add_page_specific_style 'page_bundles/projects'
- @force_desktop_expanded_sidebar = true
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-search-settings-section' }, header_options: { class: 'gl-new-card-header gl-display-flex' }, body_options: { class: 'gl-new-card-body' }) do |c|
diff --git a/app/views/layouts/header/_super_sidebar_logged_out.haml b/app/views/layouts/header/_super_sidebar_logged_out.haml
index 67322aced74..31dfdfb2bb3 100644
--- a/app/views/layouts/header/_super_sidebar_logged_out.haml
+++ b/app/views/layouts/header/_super_sidebar_logged_out.haml
@@ -44,4 +44,4 @@
- if allow_signup?
%li
= render Pajamas::ButtonComponent.new(href: new_user_registration_path, variant: :confirm) do
- = _('Register')
+ = Gitlab.com? ? _('Get free trial') : _('Register')
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index 7e5dd0d37c9..29e30c4434f 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -1,7 +1,6 @@
- page_title _("Settings")
- nav "project"
- add_page_specific_style 'page_bundles/settings'
-- add_page_specific_style 'page_bundles/projects'
- enable_search_settings locals: { container_class: 'gl-my-5' }
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index bd0f4577a32..e2cad2fb3d7 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,6 +1,5 @@
- page_title _("Blame"), @blob.path, @ref
- add_page_specific_style 'page_bundles/tree'
-- add_page_specific_style 'page_bundles/projects'
- blame_streaming_url = blame_pages_streaming_url(@id, @project)
- if @blame_mode.streaming? && @blame_pagination.total_extra_pages > 0
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 2d9b7ada015..543bdaf46df 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -2,7 +2,6 @@
- project = @project.present(current_user: current_user)
- ref = local_assigns[:ref] || @ref
- expanded = params[:expanded].present?
-- add_page_specific_style 'page_bundles/projects'
-# If the blob has a RichViewer we preload the content except for GeoJSON since it is handled by Vue
- if blob.rich_viewer && blob.extension != 'geojson'
- add_page_startup_api_call local_assigns.fetch(:viewer_url) { url_for(safe_params.merge(viewer: blob.rich_viewer.type, format: :json)) }
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index be2bf43cbb9..1034f06f722 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -1,7 +1,6 @@
- breadcrumb_title _("Commits")
- add_page_specific_style 'page_bundles/tree'
- add_page_specific_style 'page_bundles/merge_request'
-- add_page_specific_style 'page_bundles/projects'
- page_title _("Commits"), @ref
= content_for :meta_tags do
diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml
index 2c53b060c11..541b8c1147d 100644
--- a/app/views/projects/find_file/show.html.haml
+++ b/app/views/projects/find_file/show.html.haml
@@ -1,6 +1,5 @@
- page_title _("Find File"), @ref
- add_page_specific_style 'page_bundles/tree'
-- add_page_specific_style 'page_bundles/projects'
.file-finder-holder.tree-holder.clearfix.js-file-finder.gl-pt-4{ 'data-file-find-url': "#{escape_javascript(project_files_path(@project, @ref, format: :json))}", 'data-find-tree-url': escape_javascript(project_tree_path(@project, @ref)), 'data-blob-url-template': escape_javascript(project_blob_path(@project, @ref)) }
.nav-block.gl-xs-mr-0
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 76e20dcc105..59a21cecd39 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -3,7 +3,6 @@
- page_title _('New Project')
- header_title _("Projects"), dashboard_projects_path
- add_page_specific_style 'page_bundles/new_namespace'
-- add_page_specific_style 'page_bundles/projects'
.project-edit-container
.project-edit-errors
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index c44499d69b5..a4ed19c2fc9 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -1,5 +1,3 @@
-- add_page_specific_style 'page_bundles/projects'
-
.tree-ref-container.gl-display-flex.gl-flex-wrap.gl-gap-2.mb-2.mb-md-0
.tree-ref-holder.gl-max-w-26{ data: { qa_selector: 'ref_dropdown_container' } }
#js-tree-ref-switcher{ data: { project_id: @project.id, ref_type: @ref_type.to_s, project_root_path: project_path(@project) } }
diff --git a/config/application.rb b/config/application.rb
index 3f095ee0e10..7bce5afa1af 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -339,7 +339,6 @@ module Gitlab
config.assets.precompile << "page_bundles/profile_two_factor_auth.css"
config.assets.precompile << "page_bundles/profiles/preferences.css"
config.assets.precompile << "page_bundles/project.css"
- config.assets.precompile << "page_bundles/projects.css"
config.assets.precompile << "page_bundles/projects_edit.css"
config.assets.precompile << "page_bundles/promotions.css"
config.assets.precompile << "page_bundles/releases.css"
diff --git a/config/feature_flags/development/exact_matches_first_group_transfer.yml b/config/feature_flags/development/exact_matches_first_group_transfer.yml
new file mode 100644
index 00000000000..f5701c78177
--- /dev/null
+++ b/config/feature_flags/development/exact_matches_first_group_transfer.yml
@@ -0,0 +1,8 @@
+---
+name: exact_matches_first_group_transfer
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130773
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/424242
+milestone: '16.4'
+type: development
+group: group::project management
+default_enabled: false
diff --git a/config/feature_flags/development/loose_foreign_keys_batch_load_using_union.yml b/config/feature_flags/development/loose_foreign_keys_batch_load_using_union.yml
index 0b0ed16c1cd..4e4a1e262b0 100644
--- a/config/feature_flags/development/loose_foreign_keys_batch_load_using_union.yml
+++ b/config/feature_flags/development/loose_foreign_keys_batch_load_using_union.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/421422
milestone: '16.3'
type: development
group: group::tenant scale
-default_enabled: false
+default_enabled: true
diff --git a/db/migrate/20230901170145_update_vulnerability_reads_trigger_to_set_has_merge_request.rb b/db/migrate/20230901170145_update_vulnerability_reads_trigger_to_set_has_merge_request.rb
new file mode 100644
index 00000000000..7b5a4645f85
--- /dev/null
+++ b/db/migrate/20230901170145_update_vulnerability_reads_trigger_to_set_has_merge_request.rb
@@ -0,0 +1,211 @@
+# frozen_string_literal: true
+
+class UpdateVulnerabilityReadsTriggerToSetHasMergeRequest < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def up
+ execute(<<~SQL)
+ CREATE OR REPLACE FUNCTION insert_or_update_vulnerability_reads()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+ DECLARE
+ severity smallint;
+ state smallint;
+ report_type smallint;
+ resolved_on_default_branch boolean;
+ present_on_default_branch boolean;
+ namespace_id bigint;
+ has_issues boolean;
+ has_merge_request boolean;
+ BEGIN
+ IF (NEW.vulnerability_id IS NULL AND (TG_OP = 'INSERT' OR TG_OP = 'UPDATE')) THEN
+ RETURN NULL;
+ END IF;
+
+ IF (TG_OP = 'UPDATE' AND OLD.vulnerability_id IS NOT NULL AND NEW.vulnerability_id IS NOT NULL) THEN
+ RETURN NULL;
+ END IF;
+
+ SELECT
+ vulnerabilities.severity, vulnerabilities.state, vulnerabilities.report_type, vulnerabilities.resolved_on_default_branch, vulnerabilities.present_on_default_branch
+ INTO
+ severity, state, report_type, resolved_on_default_branch, present_on_default_branch
+ FROM
+ vulnerabilities
+ WHERE
+ vulnerabilities.id = NEW.vulnerability_id;
+
+ IF present_on_default_branch IS NOT true THEN
+ RETURN NULL;
+ END IF;
+
+ SELECT
+ projects.namespace_id
+ INTO
+ namespace_id
+ FROM
+ projects
+ WHERE
+ projects.id = NEW.project_id;
+
+ SELECT
+ EXISTS (SELECT 1 FROM vulnerability_issue_links WHERE vulnerability_issue_links.vulnerability_id = NEW.vulnerability_id)
+ INTO
+ has_issues;
+
+ SELECT
+ EXISTS (SELECT 1 FROM vulnerability_merge_request_links WHERE vulnerability_merge_request_links.vulnerability_id = NEW.vulnerability_id)
+ INTO
+ has_merge_request;
+
+ INSERT INTO vulnerability_reads (vulnerability_id, namespace_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues, has_merge_request)
+ VALUES (NEW.vulnerability_id, namespace_id, NEW.project_id, NEW.scanner_id, report_type, severity, state, resolved_on_default_branch, NEW.uuid::uuid, NEW.location->>'image', NEW.location->'kubernetes_resource'->>'agent_id', CAST(NEW.location->'kubernetes_resource'->>'agent_id' AS bigint), has_issues, has_merge_request)
+ ON CONFLICT(vulnerability_id) DO NOTHING;
+ RETURN NULL;
+ END
+ $$
+ SQL
+
+ execute(<<~SQL)
+ CREATE OR REPLACE FUNCTION insert_vulnerability_reads_from_vulnerability()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+ DECLARE
+ scanner_id bigint;
+ uuid uuid;
+ location_image text;
+ cluster_agent_id text;
+ casted_cluster_agent_id bigint;
+ namespace_id bigint;
+ has_issues boolean;
+ has_merge_request boolean;
+ BEGIN
+ SELECT
+ v_o.scanner_id, v_o.uuid, v_o.location->>'image', v_o.location->'kubernetes_resource'->>'agent_id', CAST(v_o.location->'kubernetes_resource'->>'agent_id' AS bigint), projects.namespace_id
+ INTO
+ scanner_id, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, namespace_id
+ FROM
+ vulnerability_occurrences v_o
+ INNER JOIN projects ON projects.id = v_o.project_id
+ WHERE
+ v_o.vulnerability_id = NEW.id
+ LIMIT 1;
+
+ SELECT
+ EXISTS (SELECT 1 FROM vulnerability_issue_links WHERE vulnerability_issue_links.vulnerability_id = NEW.id)
+ INTO
+ has_issues;
+
+ SELECT
+ EXISTS (SELECT 1 FROM vulnerability_merge_request_links WHERE vulnerability_merge_request_links.vulnerability_id = NEW.id)
+ INTO
+ has_merge_request;
+
+ INSERT INTO vulnerability_reads (vulnerability_id, namespace_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues, has_merge_request)
+ VALUES (NEW.id, namespace_id, NEW.project_id, scanner_id, NEW.report_type, NEW.severity, NEW.state, NEW.resolved_on_default_branch, uuid::uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues, has_merge_request)
+ ON CONFLICT(vulnerability_id) DO NOTHING;
+ RETURN NULL;
+ END
+ $$
+ SQL
+ end
+
+ def down
+ execute(<<~SQL)
+ CREATE OR REPLACE FUNCTION insert_or_update_vulnerability_reads()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+ DECLARE
+ severity smallint;
+ state smallint;
+ report_type smallint;
+ resolved_on_default_branch boolean;
+ present_on_default_branch boolean;
+ namespace_id bigint;
+ has_issues boolean;
+ BEGIN
+ IF (NEW.vulnerability_id IS NULL AND (TG_OP = 'INSERT' OR TG_OP = 'UPDATE')) THEN
+ RETURN NULL;
+ END IF;
+
+ IF (TG_OP = 'UPDATE' AND OLD.vulnerability_id IS NOT NULL AND NEW.vulnerability_id IS NOT NULL) THEN
+ RETURN NULL;
+ END IF;
+
+ SELECT
+ vulnerabilities.severity, vulnerabilities.state, vulnerabilities.report_type, vulnerabilities.resolved_on_default_branch, vulnerabilities.present_on_default_branch
+ INTO
+ severity, state, report_type, resolved_on_default_branch, present_on_default_branch
+ FROM
+ vulnerabilities
+ WHERE
+ vulnerabilities.id = NEW.vulnerability_id;
+
+ IF present_on_default_branch IS NOT true THEN
+ RETURN NULL;
+ END IF;
+
+ SELECT
+ projects.namespace_id
+ INTO
+ namespace_id
+ FROM
+ projects
+ WHERE
+ projects.id = NEW.project_id;
+
+ SELECT
+ EXISTS (SELECT 1 FROM vulnerability_issue_links WHERE vulnerability_issue_links.vulnerability_id = NEW.vulnerability_id)
+ INTO
+ has_issues;
+
+ INSERT INTO vulnerability_reads (vulnerability_id, namespace_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues)
+ VALUES (NEW.vulnerability_id, namespace_id, NEW.project_id, NEW.scanner_id, report_type, severity, state, resolved_on_default_branch, NEW.uuid::uuid, NEW.location->>'image', NEW.location->'kubernetes_resource'->>'agent_id', CAST(NEW.location->'kubernetes_resource'->>'agent_id' AS bigint), has_issues)
+ ON CONFLICT(vulnerability_id) DO NOTHING;
+ RETURN NULL;
+ END
+ $$
+ SQL
+
+ execute(<<~SQL)
+ CREATE OR REPLACE FUNCTION insert_vulnerability_reads_from_vulnerability()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+ DECLARE
+ scanner_id bigint;
+ uuid uuid;
+ location_image text;
+ cluster_agent_id text;
+ casted_cluster_agent_id bigint;
+ namespace_id bigint;
+ has_issues boolean;
+ BEGIN
+ SELECT
+ v_o.scanner_id, v_o.uuid, v_o.location->>'image', v_o.location->'kubernetes_resource'->>'agent_id', CAST(v_o.location->'kubernetes_resource'->>'agent_id' AS bigint), projects.namespace_id
+ INTO
+ scanner_id, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, namespace_id
+ FROM
+ vulnerability_occurrences v_o
+ INNER JOIN projects ON projects.id = v_o.project_id
+ WHERE
+ v_o.vulnerability_id = NEW.id
+ LIMIT 1;
+
+ SELECT
+ EXISTS (SELECT 1 FROM vulnerability_issue_links WHERE vulnerability_issue_links.vulnerability_id = NEW.id)
+ INTO
+ has_issues;
+
+ INSERT INTO vulnerability_reads (vulnerability_id, namespace_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues)
+ VALUES (NEW.id, namespace_id, NEW.project_id, scanner_id, NEW.report_type, NEW.severity, NEW.state, NEW.resolved_on_default_branch, uuid::uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues)
+ ON CONFLICT(vulnerability_id) DO NOTHING;
+ RETURN NULL;
+ END
+ $$
+ SQL
+ end
+end
diff --git a/db/migrate/20230905061815_add_foreign_key_for_ci_pipeline_messages_pipeline_id_bigint.rb b/db/migrate/20230905061815_add_foreign_key_for_ci_pipeline_messages_pipeline_id_bigint.rb
new file mode 100644
index 00000000000..72ff8399a16
--- /dev/null
+++ b/db/migrate/20230905061815_add_foreign_key_for_ci_pipeline_messages_pipeline_id_bigint.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddForeignKeyForCiPipelineMessagesPipelineIdBigint < Gitlab::Database::Migration[2.1]
+ TABLE_NAME = :ci_pipeline_messages
+ REFERENCING_TABLE_NAME = :ci_pipelines
+ COLUMN_NAME = :pipeline_id_convert_to_bigint
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key(
+ TABLE_NAME, REFERENCING_TABLE_NAME,
+ column: COLUMN_NAME, on_delete: :cascade, validate: false, reverse_lock_order: true
+ )
+ end
+
+ def down
+ remove_foreign_key_if_exists TABLE_NAME, column: COLUMN_NAME
+ end
+end
diff --git a/db/migrate/20230907162613_add_force_full_reconciliation_to_workspaces.rb b/db/migrate/20230907162613_add_force_full_reconciliation_to_workspaces.rb
new file mode 100644
index 00000000000..bd0b9b9bf69
--- /dev/null
+++ b/db/migrate/20230907162613_add_force_full_reconciliation_to_workspaces.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddForceFullReconciliationToWorkspaces < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def change
+ add_column :workspaces, :force_full_reconciliation, :boolean, default: false, null: false
+ end
+end
diff --git a/db/post_migrate/20230903064537_add_ci_job_artifacts_file_final_path_index.rb b/db/post_migrate/20230903064537_add_ci_job_artifacts_file_final_path_index.rb
new file mode 100644
index 00000000000..e1e5694f4d4
--- /dev/null
+++ b/db/post_migrate/20230903064537_add_ci_job_artifacts_file_final_path_index.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddCiJobArtifactsFileFinalPathIndex < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_ci_job_artifacts_on_file_final_path'
+ WHERE_CLAUSE = 'file_final_path IS NOT NULL'
+
+ # TODO: Index to be created synchronously in https://gitlab.com/gitlab-org/gitlab/-/issues/423990
+ def up
+ prepare_async_index :ci_job_artifacts, :file_final_path, name: INDEX_NAME, where: WHERE_CLAUSE
+ end
+
+ def down
+ unprepare_async_index :ci_job_artifacts, :file_final_path, name: INDEX_NAME, where: WHERE_CLAUSE
+ end
+end
diff --git a/db/post_migrate/20230905071915_prepare_async_foreign_key_for_ci_pipeline_messages_pipeline_id_bigint.rb b/db/post_migrate/20230905071915_prepare_async_foreign_key_for_ci_pipeline_messages_pipeline_id_bigint.rb
new file mode 100644
index 00000000000..ce576704d4c
--- /dev/null
+++ b/db/post_migrate/20230905071915_prepare_async_foreign_key_for_ci_pipeline_messages_pipeline_id_bigint.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class PrepareAsyncForeignKeyForCiPipelineMessagesPipelineIdBigint < Gitlab::Database::Migration[2.1]
+ TABLE_NAME = :ci_pipeline_messages
+ COLUMN_NAME = :pipeline_id_convert_to_bigint
+ FK_NAME = :fk_0946fea681
+
+ def up
+ prepare_async_foreign_key_validation TABLE_NAME, COLUMN_NAME, name: FK_NAME
+ end
+
+ def down
+ unprepare_async_foreign_key_validation TABLE_NAME, COLUMN_NAME, name: FK_NAME
+ end
+end
diff --git a/db/post_migrate/20230906181457_add_index_to_violations_on_target_proj_id.rb b/db/post_migrate/20230906181457_add_index_to_violations_on_target_proj_id.rb
new file mode 100644
index 00000000000..13c76a3c2de
--- /dev/null
+++ b/db/post_migrate/20230906181457_add_index_to_violations_on_target_proj_id.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddIndexToViolationsOnTargetProjId < Gitlab::Database::Migration[2.1]
+ TABLE_NAME = 'merge_requests_compliance_violations'
+ # Use funciton based naming as suggested in docs:
+ # https://docs.gitlab.com/ee/development/migration_style_guide.html#truncate-long-index-names
+ INDEX_NAME = 'i_compliance_violations_for_export'
+
+ def up
+ prepare_async_index TABLE_NAME, [:target_project_id, :id], name: INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index TABLE_NAME, INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20230901170145 b/db/schema_migrations/20230901170145
new file mode 100644
index 00000000000..ae389df1a50
--- /dev/null
+++ b/db/schema_migrations/20230901170145
@@ -0,0 +1 @@
+86b17eb7dd562a935cea98c9a1e0815110dea9c6994f0c2fa9db5b37d5a3af27 \ No newline at end of file
diff --git a/db/schema_migrations/20230903064537 b/db/schema_migrations/20230903064537
new file mode 100644
index 00000000000..763aa7ff01c
--- /dev/null
+++ b/db/schema_migrations/20230903064537
@@ -0,0 +1 @@
+6eda15f0921c135f38f3d35edcdb8deefd8e1735abf537c94dd8c6475600060b \ No newline at end of file
diff --git a/db/schema_migrations/20230905061815 b/db/schema_migrations/20230905061815
new file mode 100644
index 00000000000..12ea99e3e4a
--- /dev/null
+++ b/db/schema_migrations/20230905061815
@@ -0,0 +1 @@
+c32510034870dea5f26ab8fd64b034919355038a2e24f38bdd7c9051059346ec \ No newline at end of file
diff --git a/db/schema_migrations/20230905071915 b/db/schema_migrations/20230905071915
new file mode 100644
index 00000000000..8b4985f2141
--- /dev/null
+++ b/db/schema_migrations/20230905071915
@@ -0,0 +1 @@
+c3be3211b1b7a08cb93ca79b569a4ee4412fe42066573c938fd920d9aee9163a \ No newline at end of file
diff --git a/db/schema_migrations/20230906181457 b/db/schema_migrations/20230906181457
new file mode 100644
index 00000000000..65e99ba4f0f
--- /dev/null
+++ b/db/schema_migrations/20230906181457
@@ -0,0 +1 @@
+183a4dd3ea67df81f38744550919d10d76b0a9e44eaf1cb949211d938b8c8f56 \ No newline at end of file
diff --git a/db/schema_migrations/20230907162613 b/db/schema_migrations/20230907162613
new file mode 100644
index 00000000000..f3442502b6e
--- /dev/null
+++ b/db/schema_migrations/20230907162613
@@ -0,0 +1 @@
+bad1a624184b8f0bfe57dbc36b4ec8478edeaa2dc6366eb5e5d31cdc7c0c8595 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 75fb3c6ef28..6148a375c48 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -82,6 +82,7 @@ DECLARE
present_on_default_branch boolean;
namespace_id bigint;
has_issues boolean;
+ has_merge_request boolean;
BEGIN
IF (NEW.vulnerability_id IS NULL AND (TG_OP = 'INSERT' OR TG_OP = 'UPDATE')) THEN
RETURN NULL;
@@ -118,8 +119,13 @@ BEGIN
INTO
has_issues;
- INSERT INTO vulnerability_reads (vulnerability_id, namespace_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues)
- VALUES (NEW.vulnerability_id, namespace_id, NEW.project_id, NEW.scanner_id, report_type, severity, state, resolved_on_default_branch, NEW.uuid::uuid, NEW.location->>'image', NEW.location->'kubernetes_resource'->>'agent_id', CAST(NEW.location->'kubernetes_resource'->>'agent_id' AS bigint), has_issues)
+ SELECT
+ EXISTS (SELECT 1 FROM vulnerability_merge_request_links WHERE vulnerability_merge_request_links.vulnerability_id = NEW.vulnerability_id)
+ INTO
+ has_merge_request;
+
+ INSERT INTO vulnerability_reads (vulnerability_id, namespace_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues, has_merge_request)
+ VALUES (NEW.vulnerability_id, namespace_id, NEW.project_id, NEW.scanner_id, report_type, severity, state, resolved_on_default_branch, NEW.uuid::uuid, NEW.location->>'image', NEW.location->'kubernetes_resource'->>'agent_id', CAST(NEW.location->'kubernetes_resource'->>'agent_id' AS bigint), has_issues, has_merge_request)
ON CONFLICT(vulnerability_id) DO NOTHING;
RETURN NULL;
END
@@ -147,6 +153,7 @@ DECLARE
casted_cluster_agent_id bigint;
namespace_id bigint;
has_issues boolean;
+ has_merge_request boolean;
BEGIN
SELECT
v_o.scanner_id, v_o.uuid, v_o.location->>'image', v_o.location->'kubernetes_resource'->>'agent_id', CAST(v_o.location->'kubernetes_resource'->>'agent_id' AS bigint), projects.namespace_id
@@ -164,8 +171,13 @@ BEGIN
INTO
has_issues;
- INSERT INTO vulnerability_reads (vulnerability_id, namespace_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues)
- VALUES (NEW.id, namespace_id, NEW.project_id, scanner_id, NEW.report_type, NEW.severity, NEW.state, NEW.resolved_on_default_branch, uuid::uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues)
+ SELECT
+ EXISTS (SELECT 1 FROM vulnerability_merge_request_links WHERE vulnerability_merge_request_links.vulnerability_id = NEW.id)
+ INTO
+ has_merge_request;
+
+ INSERT INTO vulnerability_reads (vulnerability_id, namespace_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues, has_merge_request)
+ VALUES (NEW.id, namespace_id, NEW.project_id, scanner_id, NEW.report_type, NEW.severity, NEW.state, NEW.resolved_on_default_branch, uuid::uuid, location_image, cluster_agent_id, casted_cluster_agent_id, has_issues, has_merge_request)
ON CONFLICT(vulnerability_id) DO NOTHING;
RETURN NULL;
END
@@ -25240,6 +25252,7 @@ CREATE TABLE workspaces (
deployment_resource_version text,
personal_access_token_id bigint,
config_version integer DEFAULT 1 NOT NULL,
+ force_full_reconciliation boolean DEFAULT false NOT NULL,
CONSTRAINT check_15543fb0fa CHECK ((char_length(name) <= 64)),
CONSTRAINT check_157d5f955c CHECK ((char_length(namespace) <= 64)),
CONSTRAINT check_2b401b0034 CHECK ((char_length(deployment_resource_version) <= 64)),
@@ -36185,6 +36198,9 @@ ALTER TABLE ONLY user_interacted_projects
ALTER TABLE ONLY merge_request_assignment_events
ADD CONSTRAINT fk_08f7602bfd FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
+ALTER TABLE ONLY ci_pipeline_messages
+ ADD CONSTRAINT fk_0946fea681 FOREIGN KEY (pipeline_id_convert_to_bigint) REFERENCES ci_pipelines(id) ON DELETE CASCADE NOT VALID;
+
ALTER TABLE ONLY remote_development_agent_configs
ADD CONSTRAINT fk_0a3c0ada56 FOREIGN KEY (cluster_agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE;
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index c28597d84c3..03b49cb1075 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -462,6 +462,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="queryjobsfailurereason"></a>`failureReason` **{warning-solid}** | [`CiJobFailureReason`](#cijobfailurereason) | **Introduced** in 16.4. This feature is an Experiment. It can be changed or removed at any time. Filter jobs by failure reason. Currently only `RUNNER_SYSTEM_FAILURE` together with `runnerTypes: INSTANCE_TYPE` is supported. |
| <a id="queryjobsrunnertypes"></a>`runnerTypes` **{warning-solid}** | [`[CiRunnerType!]`](#cirunnertype) | **Introduced** in 16.4. This feature is an Experiment. It can be changed or removed at any time. Filter jobs by runner type if feature flag `:admin_jobs_filter_runner_type` is enabled. |
| <a id="queryjobsstatuses"></a>`statuses` | [`[CiJobStatus!]`](#cijobstatus) | Filter jobs by status. |
@@ -26308,6 +26309,47 @@ Values for sorting inherited variables.
| <a id="cigroupvariablessortkey_asc"></a>`KEY_ASC` | Key by ascending order. |
| <a id="cigroupvariablessortkey_desc"></a>`KEY_DESC` | Key by descending order. |
+### `CiJobFailureReason`
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="cijobfailurereasonapi_failure"></a>`API_FAILURE` | A job that failed due to api failure. |
+| <a id="cijobfailurereasonarchived_failure"></a>`ARCHIVED_FAILURE` | A job that failed due to archived failure. |
+| <a id="cijobfailurereasonbridge_pipeline_is_child_pipeline"></a>`BRIDGE_PIPELINE_IS_CHILD_PIPELINE` | A job that failed due to bridge pipeline is child pipeline. |
+| <a id="cijobfailurereasonbuilds_disabled"></a>`BUILDS_DISABLED` | A job that failed due to builds disabled. |
+| <a id="cijobfailurereasonci_quota_exceeded"></a>`CI_QUOTA_EXCEEDED` | A job that failed due to ci quota exceeded. |
+| <a id="cijobfailurereasondata_integrity_failure"></a>`DATA_INTEGRITY_FAILURE` | A job that failed due to data integrity failure. |
+| <a id="cijobfailurereasondeployment_rejected"></a>`DEPLOYMENT_REJECTED` | A job that failed due to deployment rejected. |
+| <a id="cijobfailurereasondownstream_bridge_project_not_found"></a>`DOWNSTREAM_BRIDGE_PROJECT_NOT_FOUND` | A job that failed due to downstream bridge project not found. |
+| <a id="cijobfailurereasondownstream_pipeline_creation_failed"></a>`DOWNSTREAM_PIPELINE_CREATION_FAILED` | A job that failed due to downstream pipeline creation failed. |
+| <a id="cijobfailurereasonenvironment_creation_failure"></a>`ENVIRONMENT_CREATION_FAILURE` | A job that failed due to environment creation failure. |
+| <a id="cijobfailurereasonfailed_outdated_deployment_job"></a>`FAILED_OUTDATED_DEPLOYMENT_JOB` | A job that failed due to failed outdated deployment job. |
+| <a id="cijobfailurereasonforward_deployment_failure"></a>`FORWARD_DEPLOYMENT_FAILURE` | A job that failed due to forward deployment failure. |
+| <a id="cijobfailurereasoninsufficient_bridge_permissions"></a>`INSUFFICIENT_BRIDGE_PERMISSIONS` | A job that failed due to insufficient bridge permissions. |
+| <a id="cijobfailurereasoninsufficient_upstream_permissions"></a>`INSUFFICIENT_UPSTREAM_PERMISSIONS` | A job that failed due to insufficient upstream permissions. |
+| <a id="cijobfailurereasoninvalid_bridge_trigger"></a>`INVALID_BRIDGE_TRIGGER` | A job that failed due to invalid bridge trigger. |
+| <a id="cijobfailurereasonip_restriction_failure"></a>`IP_RESTRICTION_FAILURE` | A job that failed due to ip restriction failure. |
+| <a id="cijobfailurereasonjob_execution_timeout"></a>`JOB_EXECUTION_TIMEOUT` | A job that failed due to job execution timeout. |
+| <a id="cijobfailurereasonmissing_dependency_failure"></a>`MISSING_DEPENDENCY_FAILURE` | A job that failed due to missing dependency failure. |
+| <a id="cijobfailurereasonno_matching_runner"></a>`NO_MATCHING_RUNNER` | A job that failed due to no matching runner. |
+| <a id="cijobfailurereasonpipeline_loop_detected"></a>`PIPELINE_LOOP_DETECTED` | A job that failed due to pipeline loop detected. |
+| <a id="cijobfailurereasonproject_deleted"></a>`PROJECT_DELETED` | A job that failed due to project deleted. |
+| <a id="cijobfailurereasonprotected_environment_failure"></a>`PROTECTED_ENVIRONMENT_FAILURE` | A job that failed due to protected environment failure. |
+| <a id="cijobfailurereasonreached_max_descendant_pipelines_depth"></a>`REACHED_MAX_DESCENDANT_PIPELINES_DEPTH` | A job that failed due to reached max descendant pipelines depth. |
+| <a id="cijobfailurereasonreached_max_pipeline_hierarchy_size"></a>`REACHED_MAX_PIPELINE_HIERARCHY_SIZE` | A job that failed due to reached max pipeline hierarchy size. |
+| <a id="cijobfailurereasonrunner_system_failure"></a>`RUNNER_SYSTEM_FAILURE` | A job that failed due to runner system failure. |
+| <a id="cijobfailurereasonrunner_unsupported"></a>`RUNNER_UNSUPPORTED` | A job that failed due to runner unsupported. |
+| <a id="cijobfailurereasonscheduler_failure"></a>`SCHEDULER_FAILURE` | A job that failed due to scheduler failure. |
+| <a id="cijobfailurereasonscript_failure"></a>`SCRIPT_FAILURE` | A job that failed due to script failure. |
+| <a id="cijobfailurereasonsecrets_provider_not_found"></a>`SECRETS_PROVIDER_NOT_FOUND` | A job that failed due to secrets provider not found. |
+| <a id="cijobfailurereasonstale_schedule"></a>`STALE_SCHEDULE` | A job that failed due to stale schedule. |
+| <a id="cijobfailurereasonstuck_or_timeout_failure"></a>`STUCK_OR_TIMEOUT_FAILURE` | A job that failed due to stuck or timeout failure. |
+| <a id="cijobfailurereasontrace_size_exceeded"></a>`TRACE_SIZE_EXCEEDED` | A job that failed due to trace size exceeded. |
+| <a id="cijobfailurereasonunknown_failure"></a>`UNKNOWN_FAILURE` | A job that failed due to unknown failure. |
+| <a id="cijobfailurereasonunmet_prerequisites"></a>`UNMET_PREREQUISITES` | A job that failed due to unmet prerequisites. |
+| <a id="cijobfailurereasonupstream_bridge_project_not_found"></a>`UPSTREAM_BRIDGE_PROJECT_NOT_FOUND` | A job that failed due to upstream bridge project not found. |
+| <a id="cijobfailurereasonuser_blocked"></a>`USER_BLOCKED` | A job that failed due to user blocked. |
+
### `CiJobKind`
| Value | Description |
diff --git a/doc/architecture/blueprints/cells/diagrams/cells-and-fulfillment.drawio.png b/doc/architecture/blueprints/cells/diagrams/cells-and-fulfillment.drawio.png
index c5fff9dbca5..fc32c694ddc 100644
--- a/doc/architecture/blueprints/cells/diagrams/cells-and-fulfillment.drawio.png
+++ b/doc/architecture/blueprints/cells/diagrams/cells-and-fulfillment.drawio.png
Binary files differ
diff --git a/doc/architecture/blueprints/cells/diagrams/term-cell.drawio.png b/doc/architecture/blueprints/cells/diagrams/term-cell.drawio.png
index 84a6d6d1745..639026c801f 100644
--- a/doc/architecture/blueprints/cells/diagrams/term-cell.drawio.png
+++ b/doc/architecture/blueprints/cells/diagrams/term-cell.drawio.png
Binary files differ
diff --git a/doc/architecture/blueprints/cells/diagrams/term-cluster.drawio.png b/doc/architecture/blueprints/cells/diagrams/term-cluster.drawio.png
index a6fd790ba5e..c5e3a0f7c71 100644
--- a/doc/architecture/blueprints/cells/diagrams/term-cluster.drawio.png
+++ b/doc/architecture/blueprints/cells/diagrams/term-cluster.drawio.png
Binary files differ
diff --git a/doc/architecture/blueprints/cells/diagrams/term-organization.drawio.png b/doc/architecture/blueprints/cells/diagrams/term-organization.drawio.png
index f1cb7cd92fe..9bfdba43309 100644
--- a/doc/architecture/blueprints/cells/diagrams/term-organization.drawio.png
+++ b/doc/architecture/blueprints/cells/diagrams/term-organization.drawio.png
Binary files differ
diff --git a/doc/architecture/blueprints/cells/diagrams/term-top-level-group.drawio.png b/doc/architecture/blueprints/cells/diagrams/term-top-level-group.drawio.png
index f5535409945..c8f6393f9fc 100644
--- a/doc/architecture/blueprints/cells/diagrams/term-top-level-group.drawio.png
+++ b/doc/architecture/blueprints/cells/diagrams/term-top-level-group.drawio.png
Binary files differ
diff --git a/doc/architecture/blueprints/code_search_with_zoekt/diagrams/sharding_proposal_2023-08.drawio.png b/doc/architecture/blueprints/code_search_with_zoekt/diagrams/sharding_proposal_2023-08.drawio.png
index 0927a144454..c45745c9dd2 100644
--- a/doc/architecture/blueprints/code_search_with_zoekt/diagrams/sharding_proposal_2023-08.drawio.png
+++ b/doc/architecture/blueprints/code_search_with_zoekt/diagrams/sharding_proposal_2023-08.drawio.png
Binary files differ
diff --git a/doc/architecture/blueprints/gitaly_adaptive_concurrency_limit/adaptive_concurrency_limit_flow.png b/doc/architecture/blueprints/gitaly_adaptive_concurrency_limit/adaptive_concurrency_limit_flow.png
index 0475a32e933..ce6bb1a8dfc 100644
--- a/doc/architecture/blueprints/gitaly_adaptive_concurrency_limit/adaptive_concurrency_limit_flow.png
+++ b/doc/architecture/blueprints/gitaly_adaptive_concurrency_limit/adaptive_concurrency_limit_flow.png
Binary files differ
diff --git a/doc/ci/migration/examples/img/maven-freestyle-plugin.png b/doc/ci/migration/examples/img/maven-freestyle-plugin.png
index ab3ece9bf5f..f6d6de2b8c0 100644
--- a/doc/ci/migration/examples/img/maven-freestyle-plugin.png
+++ b/doc/ci/migration/examples/img/maven-freestyle-plugin.png
Binary files differ
diff --git a/doc/ci/migration/examples/img/maven-freestyle-shell.png b/doc/ci/migration/examples/img/maven-freestyle-shell.png
index f60d5320cb9..1bb213582cc 100644
--- a/doc/ci/migration/examples/img/maven-freestyle-shell.png
+++ b/doc/ci/migration/examples/img/maven-freestyle-shell.png
Binary files differ
diff --git a/doc/operations/img/tracing_details_v16_3.png b/doc/operations/img/tracing_details_v16_3.png
index b6d0e89c6ec..2b371228cec 100644
--- a/doc/operations/img/tracing_details_v16_3.png
+++ b/doc/operations/img/tracing_details_v16_3.png
Binary files differ
diff --git a/doc/user/analytics/img/issues_closed_analytics_v16_4.png b/doc/user/analytics/img/issues_closed_analytics_v16_4.png
index e3002928b68..5e1fe4eaa8c 100644
--- a/doc/user/analytics/img/issues_closed_analytics_v16_4.png
+++ b/doc/user/analytics/img/issues_closed_analytics_v16_4.png
Binary files differ
diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md
index 023c1cd81fc..8142f390c3b 100644
--- a/doc/user/analytics/index.md
+++ b/doc/user/analytics/index.md
@@ -7,6 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Analyze GitLab usage **(FREE ALL)**
GitLab provides different types of analytics insights at the instance, group, and project level.
+These insights appear on the left sidebar, under [**Analyze**](../project/settings/index.md#remove-project-analytics-from-the-left-sidebar).
## Instance-level analytics
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 9152f01fffb..6d996446b76 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -225,7 +225,7 @@ or [purchase additional storage](https://about.gitlab.com/pricing/licensing-faq/
NOTE:
`git push` and GitLab project imports are limited to 5 GB per request through
-Cloudflare. Git LFS and imports other than a file upload are not affected by
+Cloudflare. Imports other than a file upload are not affected by
this limit. Repository limits apply to both public and private projects.
## Default import sources
diff --git a/doc/user/group/issues_analytics/img/issues_closed_analytics_v16_4.png b/doc/user/group/issues_analytics/img/issues_closed_analytics_v16_4.png
index e3002928b68..5e1fe4eaa8c 100644
--- a/doc/user/group/issues_analytics/img/issues_closed_analytics_v16_4.png
+++ b/doc/user/group/issues_analytics/img/issues_closed_analytics_v16_4.png
Binary files differ
diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md
index 973a689a554..277c43a14cd 100644
--- a/doc/user/project/working_with_projects.md
+++ b/doc/user/project/working_with_projects.md
@@ -420,6 +420,12 @@ download starts, the `insteadOf` configuration sends the traffic to the secondar
When working with projects, you might encounter the following issues, or require alternate methods to complete specific tasks.
+### `An error occurred while fetching commit data`
+
+When you visit a project, the message `An error occurred while fetching commit data` might be displayed
+if you use an ad blocker in your browser. The solution is to disable your ad blocker
+for the GitLab instance you are trying to access.
+
### Find projects using an SQL query
While in [a Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session), you can find and store an array of projects based on a SQL query:
diff --git a/lib/api/concerns/packages/npm_endpoints.rb b/lib/api/concerns/packages/npm_endpoints.rb
index a045a3d4828..4278510e999 100644
--- a/lib/api/concerns/packages/npm_endpoints.rb
+++ b/lib/api/concerns/packages/npm_endpoints.rb
@@ -197,7 +197,7 @@ module API
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get '*package_name', format: false, requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
package_name = params[:package_name]
- packages =
+ available_packages =
if Feature.enabled?(:npm_allow_packages_in_multiple_projects)
finder_for_endpoint_scope(package_name).execute
else
@@ -205,7 +205,8 @@ module API
.execute
end
- redirect_request = project_or_nil.blank? || packages.empty?
+ # In order to redirect a request, packages should not exist (without taking the user into account).
+ redirect_request = project_or_nil.blank? || available_packages.empty?
redirect_registry_request(
forward_to_registry: redirect_request,
@@ -213,9 +214,25 @@ module API
target: project_or_nil,
package_name: package_name
) do
- authorize_read_package!(project)
+ if endpoint_scope == :project || Feature.disabled?(:npm_allow_packages_in_multiple_projects)
+ authorize_read_package!(project)
+ elsif Feature.enabled?(:npm_allow_packages_in_multiple_projects)
+ available_packages_to_user = ::Packages::Npm::PackagesForUserFinder.new(
+ current_user,
+ group_or_namespace,
+ package_name: params[:package_name]
+ ).execute
+
+ if available_packages.any? && available_packages_to_user.empty?
+ forbidden! if current_user
+
+ not_found!('Packages')
+ end
+
+ available_packages = available_packages_to_user
+ end
- not_found!('Packages') if packages.empty?
+ not_found!('Packages') if available_packages.empty?
if endpoint_scope == :project && Feature.enabled?(:npm_metadata_cache, project)
if metadata_cache&.file&.exists?
@@ -228,7 +245,7 @@ module API
enqueue_sync_metadata_cache_worker(project, package_name)
end
- metadata = generate_metadata_service(packages).execute.payload
+ metadata = generate_metadata_service(available_packages).execute.payload
present metadata, with: ::API::Entities::NpmPackage
end
end
diff --git a/lib/api/npm_group_packages.rb b/lib/api/npm_group_packages.rb
index 1aa3135b186..f2b8e1840a1 100644
--- a/lib/api/npm_group_packages.rb
+++ b/lib/api/npm_group_packages.rb
@@ -11,6 +11,10 @@ module API
def endpoint_scope
:group
end
+
+ def group_or_namespace
+ group
+ end
end
params do
diff --git a/lib/api/npm_instance_packages.rb b/lib/api/npm_instance_packages.rb
index ea92818e76c..1805edceb2c 100644
--- a/lib/api/npm_instance_packages.rb
+++ b/lib/api/npm_instance_packages.rb
@@ -10,6 +10,10 @@ module API
def endpoint_scope
:instance
end
+
+ def group_or_namespace
+ top_namespace_from(params[:package_name])
+ end
end
namespace 'packages/npm' do
diff --git a/lib/gitlab/sql/pattern.rb b/lib/gitlab/sql/pattern.rb
index dbcca2c1a33..9fedb2d174f 100644
--- a/lib/gitlab/sql/pattern.rb
+++ b/lib/gitlab/sql/pattern.rb
@@ -9,12 +9,28 @@ module Gitlab
REGEX_QUOTED_TERM = /(?<=\A| )"[^"]+"(?= |\z)/
class_methods do
- def fuzzy_search(query, columns, use_minimum_char_limit: true)
+ def fuzzy_search(query, columns, use_minimum_char_limit: true, exact_matches_first: false)
matches = columns.map do |col|
fuzzy_arel_match(col, query, use_minimum_char_limit: use_minimum_char_limit)
end.compact.reduce(:or)
- where(matches)
+ matches = where(matches)
+
+ return matches unless exact_matches_first
+
+ matches.order(exact_matches_first_sql(query, columns))
+ end
+
+ def exact_matches_first_sql(query, columns)
+ cases_sql = columns.map do |column|
+ arel_column = column.is_a?(Arel::Attributes::Attribute) ? column : arel_table[column]
+ match_sql = arel_column.matches(sanitize_sql_like(query)).to_sql
+ "WHEN #{match_sql} THEN 1"
+ end
+
+ cases_sql << "ELSE 2"
+
+ Arel.sql("CASE\n#{cases_sql.join("\n")}\nEND")
end
def to_pattern(query, use_minimum_char_limit: true)
diff --git a/lib/gitlab/utils/override.rb b/lib/gitlab/utils/override.rb
index 10370811bb5..9bc37746349 100644
--- a/lib/gitlab/utils/override.rb
+++ b/lib/gitlab/utils/override.rb
@@ -3,6 +3,9 @@
require 'gitlab/utils/all'
require_relative '../environment'
+# See https://docs.gitlab.com/ee/development/utilities.html#override for
+# more information
+
module Gitlab
module Utils
module Override
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 6856142418c..a92f8978c2b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12341,6 +12341,9 @@ msgstr ""
msgid "Configuration help"
msgstr ""
+msgid "Configure"
+msgstr ""
+
msgid "Configure %{italic_start}What's new%{italic_end} drawer and content."
msgstr ""
@@ -21271,6 +21274,9 @@ msgstr ""
msgid "Get a support subscription"
msgstr ""
+msgid "Get free trial"
+msgstr ""
+
msgid "Get more information about troubleshooting pipelines"
msgstr ""
diff --git a/spec/finders/ci/jobs_finder_spec.rb b/spec/finders/ci/jobs_finder_spec.rb
index e86ac65df61..57046baafab 100644
--- a/spec/finders/ci/jobs_finder_spec.rb
+++ b/spec/finders/ci/jobs_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::JobsFinder, '#execute', feature_category: :runner_fleet do
+RSpec.describe Ci::JobsFinder, '#execute', feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:project) { create(:project, :private, public_builds: false) }
@@ -13,8 +13,8 @@ RSpec.describe Ci::JobsFinder, '#execute', feature_category: :runner_fleet do
let(:params) { {} }
- context 'when project, pipeline or runner are blank' do
- subject { described_class.new(current_user: current_user, params: params).execute }
+ context 'when project, pipeline, and runner are blank' do
+ subject(:finder_execute) { described_class.new(current_user: current_user, params: params).execute }
context 'with admin' do
let(:current_user) { admin }
@@ -278,28 +278,30 @@ RSpec.describe Ci::JobsFinder, '#execute', feature_category: :runner_fleet do
let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) }
let_it_be(:job_4) { create(:ci_build, :success, runner: runner) }
- subject { described_class.new(current_user: user, runner: runner, params: params).execute }
+ subject(:execute) { described_class.new(current_user: user, runner: runner, params: params).execute }
- context 'with admin and admin mode enabled', :enable_admin_mode do
+ context 'when current user is an admin' do
let(:user) { admin }
- it 'returns jobs for the specified project' do
- expect(subject).to match_array([job_4])
- end
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'returns jobs for the specified project' do
+ expect(subject).to contain_exactly job_4
+ end
- context "with params" do
- using RSpec::Parameterized::TableSyntax
+ context 'with params' do
+ using RSpec::Parameterized::TableSyntax
- where(:param_runner_type, :param_scope, :expected_jobs) do
- 'project_type' | 'success' | lazy { [job_4] }
- 'instance_type' | nil | lazy { [] }
- nil | 'pending' | lazy { [] }
- end
+ where(:param_runner_type, :param_scope, :expected_jobs) do
+ 'project_type' | 'success' | lazy { [job_4] }
+ 'instance_type' | nil | lazy { [] }
+ nil | 'pending' | lazy { [] }
+ end
- with_them do
- let(:params) { { runner_type: param_runner_type, scope: param_scope } }
+ with_them do
+ let(:params) { { runner_type: param_runner_type, scope: param_scope } }
- it { is_expected.to match_array(expected_jobs) }
+ it { is_expected.to match_array(expected_jobs) }
+ end
end
end
end
diff --git a/spec/finders/groups/accepting_group_transfers_finder_spec.rb b/spec/finders/groups/accepting_group_transfers_finder_spec.rb
index 18407dd0196..2a61150acf9 100644
--- a/spec/finders/groups/accepting_group_transfers_finder_spec.rb
+++ b/spec/finders/groups/accepting_group_transfers_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::AcceptingGroupTransfersFinder do
+RSpec.describe Groups::AcceptingGroupTransfersFinder, feature_category: :groups_and_projects do
let_it_be(:current_user) { create(:user) }
let_it_be(:great_grandparent_group) do
@@ -119,6 +119,37 @@ RSpec.describe Groups::AcceptingGroupTransfersFinder do
expect(result).to contain_exactly(great_grandparent_group)
end
end
+
+ context 'on searching with multiple matches' do
+ let(:params) { { search: 'great-grandparent-group' } }
+ let(:other_groups) { [] }
+
+ before do
+ 2.times do
+ # app/finders/group/base.rb adds an ORDER BY path, so create a group with 1 in the front.
+ group = create(:group, parent: great_grandparent_group, path: "1-#{SecureRandom.hex}")
+ group.add_owner(current_user)
+ other_groups << group
+ end
+ end
+
+ it 'prioritizes exact matches first' do
+ expect(result.first).to eq(great_grandparent_group)
+ expect(result[1..]).to match_array(other_groups)
+ end
+
+ context 'when exact_matches_first_group_transfer feature flag is disabled' do
+ let(:expected_groups) { other_groups + [great_grandparent_group] }
+
+ before do
+ stub_feature_flags(exact_matches_first_group_transfer: false)
+ end
+
+ it 'returns matching groups sorted by namespace path' do
+ expect(result).to match_array(expected_groups.sort_by(&:path))
+ end
+ end
+ end
end
end
end
diff --git a/spec/finders/packages/npm/packages_for_user_finder_spec.rb b/spec/finders/packages/npm/packages_for_user_finder_spec.rb
new file mode 100644
index 00000000000..e2dc21e1008
--- /dev/null
+++ b/spec/finders/packages/npm/packages_for_user_finder_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Packages::Npm::PackagesForUserFinder, feature_category: :package_registry do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:project2) { create(:project, group: group) }
+ let_it_be(:package) { create(:npm_package, project: project) }
+ let_it_be(:package_name) { package.name }
+ let_it_be(:package_with_diff_name) { create(:npm_package, project: project) }
+ let_it_be(:package_with_diff_project) { create(:npm_package, name: package_name, project: project2) }
+ let_it_be(:maven_package) { create(:maven_package, name: package_name, project: project) }
+
+ let(:finder) { described_class.new(user, project_or_group, package_name: package_name) }
+
+ describe '#execute' do
+ subject { finder.execute }
+
+ shared_examples 'searches for packages' do
+ it { is_expected.to contain_exactly(package) }
+ end
+
+ context 'with a project' do
+ let(:project_or_group) { project }
+
+ it_behaves_like 'searches for packages'
+ end
+
+ context 'with a group' do
+ let(:project_or_group) { group }
+
+ before_all do
+ project.add_reporter(user)
+ end
+
+ it_behaves_like 'searches for packages'
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb
index 933abf31470..6b9e3a484b1 100644
--- a/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Resolvers::Ci::AllJobsResolver, feature_category: :continuous_integration do
include GraphqlHelpers
+ let_it_be(:instance_runner) { create(:ci_runner, :instance) }
let_it_be(:successful_job) { create(:ci_build, :success, name: 'successful_job') }
let_it_be(:successful_job_two) { create(:ci_build, :success, name: 'successful_job_two') }
let_it_be(:failed_job) { create(:ci_build, :failed, name: 'failed_job') }
@@ -12,11 +13,11 @@ RSpec.describe Resolvers::Ci::AllJobsResolver, feature_category: :continuous_int
let(:args) { {} }
- subject { resolve_jobs(args) }
-
describe '#resolve' do
- context 'with admin' do
- let(:current_user) { create(:admin) }
+ subject(:request) { resolve_jobs(args) }
+
+ context 'when current user is an admin' do
+ let_it_be(:current_user) { create(:admin) }
shared_examples 'executes as admin' do
context "with argument `statuses`" do
@@ -40,8 +41,7 @@ RSpec.describe Resolvers::Ci::AllJobsResolver, feature_category: :continuous_int
context "with argument `runner_types`" do
let_it_be(:successful_job_with_instance_runner) do
- create(:ci_build, :success, name: 'successful_job_with_instance_runner',
- runner: create(:ci_runner, :instance))
+ create(:ci_build, :success, name: 'successful_job_with_instance_runner', runner: instance_runner)
end
context 'with feature flag :admin_jobs_filter_runner_type enabled' do
@@ -80,7 +80,7 @@ RSpec.describe Resolvers::Ci::AllJobsResolver, feature_category: :continuous_int
:ci_build,
:success,
name: 'successful_job_with_instance_runner',
- runner: create(:ci_runner, :instance)
+ runner: instance_runner
)
end
@@ -132,7 +132,9 @@ RSpec.describe Resolvers::Ci::AllJobsResolver, feature_category: :continuous_int
end
context 'with unauthorized user' do
- let(:current_user) { nil }
+ let_it_be(:unauth_user) { create(:user) }
+
+ let(:current_user) { unauth_user }
it { is_expected.to be_empty }
end
diff --git a/spec/lib/gitlab/sql/pattern_spec.rb b/spec/lib/gitlab/sql/pattern_spec.rb
index a34ddf8773c..7bd2ddf2889 100644
--- a/spec/lib/gitlab/sql/pattern_spec.rb
+++ b/spec/lib/gitlab/sql/pattern_spec.rb
@@ -9,36 +9,44 @@ RSpec.describe Gitlab::SQL::Pattern do
let_it_be(:issue1) { create(:issue, title: 'noise foo noise', description: 'noise bar noise') }
let_it_be(:issue2) { create(:issue, title: 'noise baz noise', description: 'noise foo noise') }
let_it_be(:issue3) { create(:issue, title: 'Oh', description: 'Ah') }
+ let_it_be(:issue4) { create(:issue, title: 'beep beep', description: 'beep beep') }
+ let_it_be(:issue5) { create(:issue, title: 'beep', description: 'beep') }
- subject(:fuzzy_search) { Issue.fuzzy_search(query, columns) }
+ subject(:fuzzy_search) { Issue.fuzzy_search(query, columns, exact_matches_first: exact_matches_first) }
- where(:query, :columns, :expected) do
- 'foo' | [Issue.arel_table[:title]] | %i[issue1]
+ where(:query, :columns, :exact_matches_first, :expected) do
+ 'foo' | [Issue.arel_table[:title]] | false | %i[issue1]
- 'foo' | %i[title] | %i[issue1]
- 'foo' | %w[title] | %i[issue1]
- 'foo' | %i[description] | %i[issue2]
- 'foo' | %i[title description] | %i[issue1 issue2]
- 'bar' | %i[title description] | %i[issue1]
- 'baz' | %i[title description] | %i[issue2]
- 'qux' | %i[title description] | []
+ 'foo' | %i[title] | false | %i[issue1]
+ 'foo' | %w[title] | false | %i[issue1]
+ 'foo' | %i[description] | false | %i[issue2]
+ 'foo' | %i[title description] | false | %i[issue1 issue2]
+ 'bar' | %i[title description] | false | %i[issue1]
+ 'baz' | %i[title description] | false | %i[issue2]
+ 'qux' | %i[title description] | false | []
- 'oh' | %i[title description] | %i[issue3]
- 'OH' | %i[title description] | %i[issue3]
- 'ah' | %i[title description] | %i[issue3]
- 'AH' | %i[title description] | %i[issue3]
- 'oh' | %i[title] | %i[issue3]
- 'ah' | %i[description] | %i[issue3]
+ 'oh' | %i[title description] | false | %i[issue3]
+ 'OH' | %i[title description] | false | %i[issue3]
+ 'ah' | %i[title description] | false | %i[issue3]
+ 'AH' | %i[title description] | false | %i[issue3]
+ 'oh' | %i[title] | false | %i[issue3]
+ 'ah' | %i[description] | false | %i[issue3]
- '' | %i[title] | %i[issue1 issue2 issue3]
- %w[a b] | %i[title] | %i[issue1 issue2 issue3]
+ '' | %i[title] | false | %i[issue1 issue2 issue3 issue4 issue5]
+ %w[a b] | %i[title] | false | %i[issue1 issue2 issue3 issue4 issue5]
+
+ 'beep' | %i[title] | true | %i[issue5 issue4]
end
with_them do
let(:expected_issues) { expected.map { |sym| send(sym) } }
it 'finds the expected issues' do
- expect(fuzzy_search).to match_array(expected_issues)
+ if exact_matches_first
+ expect(fuzzy_search).to eq(expected_issues)
+ else
+ expect(fuzzy_search).to match_array(expected_issues)
+ end
end
end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index a03dac83113..37d967cbc7c 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -1079,17 +1079,30 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do
end
it 'defaults use_minimum_char_limit to true' do
- expect(described_class).to receive(:fuzzy_search).with(anything, anything, use_minimum_char_limit: true).once
+ expect(described_class).to receive(:fuzzy_search).with(anything, anything, use_minimum_char_limit: true, exact_matches_first: false).once
described_class.search('my namespace')
end
it 'passes use_minimum_char_limit if it is set' do
- expect(described_class).to receive(:fuzzy_search).with(anything, anything, use_minimum_char_limit: false).once
+ expect(described_class).to receive(:fuzzy_search).with(anything, anything, use_minimum_char_limit: false, exact_matches_first: false).once
described_class.search('my namespace', use_minimum_char_limit: false)
end
+ context 'with multiple matching namespaces' do
+ let_it_be(:first_group) { create(:group, name: 'some name', path: 'z-path') }
+ let_it_be(:second_group) { create(:group, name: 'some name too', path: 'a-path') }
+
+ it 'returns exact matches first' do
+ expect(described_class.search('some name', exact_matches_first: true).to_a).to eq([first_group, second_group])
+ end
+
+ it 'returns exact matches first when parents are included' do
+ expect(described_class.search('some name', include_parents: true, exact_matches_first: true).to_a).to eq([first_group, second_group])
+ end
+ end
+
context 'with project namespaces' do
let_it_be(:project) { create(:project, namespace: parent_group, path: 'some-new-path') }
let_it_be(:project_namespace) { project.project_namespace }
diff --git a/spec/requests/api/npm_group_packages_spec.rb b/spec/requests/api/npm_group_packages_spec.rb
index 6968e30e2b0..7fba75b0630 100644
--- a/spec/requests/api/npm_group_packages_spec.rb
+++ b/spec/requests/api/npm_group_packages_spec.rb
@@ -11,43 +11,12 @@ RSpec.describe API::NpmGroupPackages, feature_category: :package_registry do
let(:url) { api("/groups/#{group.id}/-/packages/npm/#{package_name}") }
it_behaves_like 'handling get metadata requests', scope: :group
-
- context 'with a duplicate package name in another project' do
+ it_behaves_like 'rejects invalid package names' do
subject { get(url) }
-
- before do
- group.add_developer(user)
- end
-
- let_it_be(:project2) { create(:project, :public, namespace: namespace) }
- let_it_be(:package2) do
- create(:npm_package,
- project: project2,
- name: "@#{group.path}/scoped_package",
- version: '1.2.0')
- end
-
- it_behaves_like 'rejects invalid package names'
-
- it 'includes all matching package versions in the response' do
- subject
-
- expect(json_response['versions'].keys).to match_array([package.version, package2.version])
- end
-
- context 'with the feature flag disabled' do
- before do
- stub_feature_flags(npm_allow_packages_in_multiple_projects: false)
- end
-
- it 'returns matching package versions from only one project' do
- subject
-
- expect(json_response['versions'].keys).to match_array([package2.version])
- end
- end
end
+ it_behaves_like 'handling get metadata requests for packages in multiple projects'
+
context 'with mixed group and project visibilities' do
subject { get(url, headers: headers) }
diff --git a/spec/requests/api/npm_instance_packages_spec.rb b/spec/requests/api/npm_instance_packages_spec.rb
index 4f965d86d66..7b74a052860 100644
--- a/spec/requests/api/npm_instance_packages_spec.rb
+++ b/spec/requests/api/npm_instance_packages_spec.rb
@@ -17,34 +17,7 @@ RSpec.describe API::NpmInstancePackages, feature_category: :package_registry do
it_behaves_like 'handling get metadata requests', scope: :instance
it_behaves_like 'rejects invalid package names'
-
- context 'with a duplicate package name in another project' do
- let_it_be(:project2) { create(:project, :public, namespace: namespace) }
- let_it_be(:package2) do
- create(:npm_package,
- project: project2,
- name: "@#{group.path}/scoped_package",
- version: '1.2.0')
- end
-
- it 'includes all matching package versions in the response' do
- subject
-
- expect(json_response['versions'].keys).to match_array([package.version, package2.version])
- end
-
- context 'with the feature flag disabled' do
- before do
- stub_feature_flags(npm_allow_packages_in_multiple_projects: false)
- end
-
- it 'returns matching package versions from only one project' do
- subject
-
- expect(json_response['versions'].keys).to match_array([package2.version])
- end
- end
- end
+ it_behaves_like 'handling get metadata requests for packages in multiple projects'
context 'when metadata cache exists' do
let_it_be(:npm_metadata_cache) { create(:npm_metadata_cache, package_name: package.name, project_id: project.id) }
diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
index db547fd7838..5f043cdd996 100644
--- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
@@ -876,3 +876,67 @@ RSpec.shared_examples 'rejects invalid package names' do
expect(Gitlab::Json.parse(response.body)).to eq({ 'error' => 'package_name should be a valid file path' })
end
end
+
+RSpec.shared_examples 'handling get metadata requests for packages in multiple projects' do
+ let_it_be(:project2) { create(:project, namespace: namespace) }
+ let_it_be(:package2) do
+ create(:npm_package,
+ project: project2,
+ name: "@#{group.path}/scoped_package",
+ version: '1.2.0')
+ end
+
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ subject { get(url, headers: headers) }
+
+ before_all do
+ project.update!(visibility: 'private')
+
+ group.add_guest(user)
+ project.add_reporter(user)
+ project2.add_reporter(user)
+ end
+
+ it 'includes all matching package versions in the response' do
+ subject
+
+ expect(json_response['versions'].keys).to match_array([package.version, package2.version])
+ end
+
+ context 'with the feature flag disabled' do
+ before do
+ stub_feature_flags(npm_allow_packages_in_multiple_projects: false)
+ end
+
+ it 'returns matching package versions from only one project' do
+ subject
+
+ expect(json_response['versions'].keys).to match_array([package2.version])
+ end
+ end
+
+ context 'with limited access to the project with the last package version' do
+ before_all do
+ project2.add_guest(user)
+ end
+
+ it 'includes matching package versions from authorized projects in the response' do
+ subject
+
+ expect(json_response['versions'].keys).to contain_exactly(package.version)
+ end
+ end
+
+ context 'with limited access to the project with the first package version' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'includes matching package versions from authorized projects in the response' do
+ subject
+
+ expect(json_response['versions'].keys).to contain_exactly(package2.version)
+ end
+ end
+end
diff --git a/spec/views/layouts/header/_super_sidebar_logged_out.html.haml_spec.rb b/spec/views/layouts/header/_super_sidebar_logged_out.html.haml_spec.rb
index 89a03d72a90..f81e8c5badf 100644
--- a/spec/views/layouts/header/_super_sidebar_logged_out.html.haml_spec.rb
+++ b/spec/views/layouts/header/_super_sidebar_logged_out.html.haml_spec.rb
@@ -19,6 +19,10 @@ RSpec.describe 'layouts/header/_super_sidebar_logged_out', feature_category: :na
expect(rendered).to have_content('Pricing')
expect(rendered).to have_content('Contact Sales')
end
+
+ it 'renders the free trial button' do
+ expect(rendered).to have_content('Get free trial')
+ end
end
context 'on self-managed' do