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
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-11-21 21:10:53 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-11-21 21:10:53 +0300
commitd5ff0674315196e88f48dc0838486b44cd005628 (patch)
tree654721afd199b913a4845b6a92a20dd230482d4c /app
parentf1c788bb1836083e4f5ed91c1c7494244e6e13ee (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue1
-rw-r--r--app/assets/javascripts/issues/show/components/app.vue7
-rw-r--r--app/assets/javascripts/super_sidebar/components/counter.vue2
-rw-r--r--app/assets/javascripts/super_sidebar/components/help_center.vue6
-rw-r--r--app/assets/javascripts/super_sidebar/components/nav_item.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue5
-rw-r--r--app/assets/stylesheets/framework/brand_logo.scss13
-rw-r--r--app/assets/stylesheets/framework/diffs.scss6
-rw-r--r--app/assets/stylesheets/framework/super_sidebar.scss104
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss8
-rw-r--r--app/assets/stylesheets/pages/issues.scss22
-rw-r--r--app/assets/stylesheets/pages/notes.scss9
-rw-r--r--app/assets/stylesheets/themes/theme_helper.scss67
-rw-r--r--app/models/audit_event.rb4
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/models/ci/sources/pipeline.rb5
-rw-r--r--app/models/concerns/ci/partitionable.rb36
-rw-r--r--app/models/concerns/ci/partitionable/testing.rb47
-rw-r--r--app/models/group.rb4
-rw-r--r--app/models/packages/build_info.rb4
-rw-r--r--app/models/pages/lookup_path.rb40
-rw-r--r--app/models/pages/virtual_domain.rb27
-rw-r--r--app/models/user.rb10
-rw-r--r--app/services/merge_requests/mergeability/detailed_merge_status_service.rb2
-rw-r--r--app/workers/all_queues.yml9
-rw-r--r--app/workers/bulk_imports/transform_references_worker.rb147
27 files changed, 424 insertions, 169 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 8c1cab20ece..c9fd25171aa 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -419,6 +419,7 @@ export default {
<div
:id="`diff-content-${file.file_hash}`"
:class="hasBodyClasses.contentByHash"
+ class="diff-content"
data-testid="content-area"
>
<gl-alert
diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue
index 756585683c8..87021db739a 100644
--- a/app/assets/javascripts/issues/show/components/app.vue
+++ b/app/assets/javascripts/issues/show/components/app.vue
@@ -22,6 +22,8 @@ import PinnedLinks from './pinned_links.vue';
import StickyHeader from './sticky_header.vue';
import TitleComponent from './title.vue';
+const STICKY_HEADER_VISIBLE_CLASS = 'issuable-sticky-header-visible';
+
export default {
components: {
HeaderActions,
@@ -322,6 +324,7 @@ export default {
eventHub.$off('close.form', this.closeForm);
eventHub.$off('open.form', this.openForm);
window.removeEventListener('beforeunload', this.handleBeforeUnloadEvent);
+ this.hideStickyHeader();
},
methods: {
handleBeforeUnloadEvent(e) {
@@ -472,6 +475,8 @@ export default {
hideStickyHeader() {
this.isStickyHeaderShowing = false;
+
+ document.body.classList?.remove(STICKY_HEADER_VISIBLE_CLASS);
},
showStickyHeader() {
@@ -479,6 +484,8 @@ export default {
if (this.$refs.title.$el.offsetTop < window.pageYOffset) {
this.isStickyHeaderShowing = true;
}
+
+ document.body.classList?.add(STICKY_HEADER_VISIBLE_CLASS);
},
handleSaveDescription(description) {
diff --git a/app/assets/javascripts/super_sidebar/components/counter.vue b/app/assets/javascripts/super_sidebar/components/counter.vue
index 49efc5ab5b9..4c49aabcf93 100644
--- a/app/assets/javascripts/super_sidebar/components/counter.vue
+++ b/app/assets/javascripts/super_sidebar/components/counter.vue
@@ -48,7 +48,7 @@ export default {
:is="component"
:aria-label="ariaLabel"
:href="href"
- class="counter gl-display-block gl-flex-grow-1 gl-text-center gl-py-3 gl-bg-gray-10 gl-rounded-base gl-text-gray-900 gl-border-none gl-inset-border-1-gray-a-08 gl-line-height-1 gl-font-sm gl-hover-text-gray-900 gl-hover-text-decoration-none gl-focus--focus"
+ class="counter gl-display-block gl-flex-grow-1 gl-text-center gl-py-3 gl-rounded-base gl-border-none gl-inset-border-1-gray-a-08 gl-line-height-1 gl-font-sm gl-hover-text-decoration-none gl-focus--focus"
>
<gl-icon aria-hidden="true" :name="icon" />
<span v-if="count" aria-hidden="true" class="gl-ml-1">{{ formattedCount }}</span>
diff --git a/app/assets/javascripts/super_sidebar/components/help_center.vue b/app/assets/javascripts/super_sidebar/components/help_center.vue
index 069987d4006..752f077ca02 100644
--- a/app/assets/javascripts/super_sidebar/components/help_center.vue
+++ b/app/assets/javascripts/super_sidebar/components/help_center.vue
@@ -215,7 +215,11 @@ export default {
@hidden="trackDropdownToggle(false)"
>
<template #toggle>
- <gl-button category="tertiary" icon="question-o" class="btn-with-notification">
+ <gl-button
+ category="tertiary"
+ icon="question-o"
+ class="super-sidebar-help-center-toggle btn-with-notification"
+ >
<span
v-if="showWhatsNewNotification"
data-testid="notification-dot"
diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue
index 3ae33bf8b37..f08cf7e5de3 100644
--- a/app/assets/javascripts/super_sidebar/components/nav_item.vue
+++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue
@@ -229,7 +229,7 @@ export default {
>
<div
:class="[isActive ? 'gl-opacity-10' : 'gl-opacity-0']"
- class="active-indicator gl-bg-blue-500 gl-absolute gl-left-2 gl-top-2 gl-bottom-2 gl-transition-slow"
+ class="active-indicator gl-absolute gl-left-2 gl-top-2 gl-bottom-2 gl-transition-slow"
aria-hidden="true"
:style="activeIndicatorStyle"
data-testid="active-indicator"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 24211833026..e80f5c7f092 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -361,7 +361,7 @@ export default {
<template>
<div
ref="gl-form"
- class="js-vue-markdown-field md-area position-relative gfm-form gl-overflow-hidden"
+ class="js-vue-markdown-field md-area position-relative gfm-form"
:data-uploads-path="uploadsPath"
>
<markdown-header
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index cc3c95a047b..5ee19c80592 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -254,7 +254,10 @@ export default {
</script>
<template>
- <div class="md-header gl-border-b gl-border-gray-100 gl-px-3">
+ <div
+ class="md-header gl-bg-white gl-border-b gl-border-gray-100 gl-rounded-lg gl-rounded-bottom-left-none gl-rounded-bottom-right-none gl-px-3"
+ :class="{ 'md-header-preview': previewMarkdown }"
+ >
<div class="gl-display-flex gl-align-items-center gl-flex-wrap">
<div
data-testid="md-header-toolbar"
diff --git a/app/assets/stylesheets/framework/brand_logo.scss b/app/assets/stylesheets/framework/brand_logo.scss
index 1bc1ef797a7..95dcb26c0c5 100644
--- a/app/assets/stylesheets/framework/brand_logo.scss
+++ b/app/assets/stylesheets/framework/brand_logo.scss
@@ -1,6 +1,3 @@
-$brand-logo-light-background: #e0dfe5;
-$brand-logo-dark-background: #53515b;
-
.brand-logo {
display: inline-block;
@include gl-rounded-base;
@@ -16,14 +13,4 @@ $brand-logo-dark-background: #53515b;
&:active {
@include gl-focus;
}
-
- &:hover,
- &:focus,
- &:active {
- background-color: $brand-logo-light-background;
-
- .gl-dark & {
- background-color: $brand-logo-dark-background;
- }
- }
}
diff --git a/app/assets/stylesheets/framework/diffs.scss b/app/assets/stylesheets/framework/diffs.scss
index 8f07ef73554..fff42c0973c 100644
--- a/app/assets/stylesheets/framework/diffs.scss
+++ b/app/assets/stylesheets/framework/diffs.scss
@@ -1,3 +1,5 @@
+$diff-file-header: 41px;
+
// Common
.diff-file {
margin-bottom: $gl-padding;
@@ -38,6 +40,10 @@
&.is-sidebar-moved {
top: calc(#{$calc-application-header-height} + #{$mr-sticky-header-height} - #{$gl-border-size-1});
+
+ + .diff-content .md-header-preview {
+ top: calc(#{$calc-application-header-height} + #{$mr-sticky-header-height} + #{$diff-file-header} - #{$gl-border-size-1});
+ }
}
&::before {
diff --git a/app/assets/stylesheets/framework/super_sidebar.scss b/app/assets/stylesheets/framework/super_sidebar.scss
index fbf9d8c8ca6..add5758090f 100644
--- a/app/assets/stylesheets/framework/super_sidebar.scss
+++ b/app/assets/stylesheets/framework/super_sidebar.scss
@@ -1,5 +1,5 @@
-@mixin active-toggle {
- background-color: $gray-50 !important;
+@mixin active-toggle($background-color: var(--super-sidebar-user-bar-button-hover-bg)) {
+ background-color: $background-color !important;
mix-blend-mode: multiply;
.gl-dark & {
@@ -12,7 +12,7 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
@mixin notification-dot($color, $size, $top, $left) {
background-color: $color;
- border: 2px solid $gray-10; // Same as the sidebar's background color.
+ border: 2px solid var(--super-sidebar-bg);
position: absolute;
height: $size;
width: $size;
@@ -29,13 +29,25 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
}
.super-sidebar {
+ --super-sidebar-bg: var(--gray-10, #{$gray-10});
+ --super-sidebar-primary: var(--blue-500, #{$blue-500});
+ --super-sidebar-notification-dot: var(--blue-500, #{$blue-500});
+ --super-sidebar-user-bar-bg: #{$t-gray-a-04};
+ --super-sidebar-user-bar-button-bg: var(--gray-10, #{$gray-10});
+ --super-sidebar-user-bar-button-hover-bg: var(--gray-50, #{$gray-50});
+ --super-sidebar-user-bar-button-color: var(--gray-900, #{$gray-900});
+ --super-sidebar-user-bar-button-hover-color: var(--gray-900, #{$gray-900});
+ // Separate values provided to use `---gray-600` in dark mode
+ --super-sidebar-user-bar-button-icon-color: var(--gray-600, #{$gray-500});
+ --super-sidebar-user-bar-button-icon-hover-color: var(--gray-700, #{$gray-700});
+
display: flex;
flex-direction: column;
position: fixed;
top: $calc-application-bars-height;
bottom: $calc-application-footer-height;
left: 0;
- background-color: var(--gray-10, $gray-10);
+ background-color: var(--super-sidebar-bg);
border-right: 1px solid $t-gray-a-08;
transform: translate3d(0, 0, 0);
width: $super-sidebar-width;
@@ -55,7 +67,7 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
}
.user-bar {
- background-color: $t-gray-a-04;
+ background-color: var(--super-sidebar-user-bar-bg);
.user-bar-item {
@include gl-rounded-base;
@@ -76,39 +88,77 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
@include active-toggle;
}
}
- }
- .counter .gl-icon,
- .item-icon {
- color: var(--gray-600, $gray-500);
- }
+ .brand-logo {
+ &:hover,
+ &:focus {
+ background-color: var(--super-sidebar-user-bar-button-hover-bg);
+ mix-blend-mode: multiply;
+
+ .gl-dark & {
+ mix-blend-mode: screen;
+ }
+ }
+ }
- .counter:hover,
- .counter:focus,
- .counter[aria-expanded='true'] {
- background-color: $gray-50;
- border-color: transparent;
- mix-blend-mode: multiply;
+ .btn-default-tertiary,
+ .counter {
+ color: var(--super-sidebar-user-bar-button-color);
- .gl-dark & {
- mix-blend-mode: screen;
+ .gl-icon {
+ color: var(--super-sidebar-user-bar-button-icon-color) !important;
+ }
+
+ &:hover,
+ &:focus {
+ background-color: var(--super-sidebar-user-bar-button-hover-bg) !important;
+ color: var(--super-sidebar-user-bar-button-hover-color);
+ }
+ }
+
+ .counter {
+ background-color: var(--super-sidebar-user-bar-button-bg);
+
+ &:hover,
+ &:focus,
+ &[aria-expanded='true'] {
+ background-color: var(--super-sidebar-user-bar-button-hover-bg);
+ border-color: transparent;
+ mix-blend-mode: multiply;
+
+ .gl-icon {
+ color: var(--super-sidebar-user-bar-button-icon-hover-color);
+ }
+
+ .gl-dark & {
+ mix-blend-mode: screen;
+ }
+ }
+
+ &:hover,
+ &[aria-expanded='true'] {
+ box-shadow: none;
+ }
}
+ }
+
+ .item-icon {
+ color: $gray-500;
- .gl-icon {
- color: var(--gray-700, $gray-700);
+ .gl-dark & {
+ color: $gray-600;
}
}
- .counter:hover,
- .counter[aria-expanded='true'] {
- box-shadow: none;
+ .active-indicator {
+ background-color: var(--super-sidebar-primary);
}
.btn-with-notification {
position: relative;
.notification-dot-info {
- @include notification-dot($blue-500, 9px, 5px, 22px);
+ @include notification-dot(var(--super-sidebar-notification-dot), 9px, 5px, 22px);
}
.notification-dot-warning {
@@ -118,7 +168,7 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
&:hover,
&:focus {
.notification {
- border-color: $gray-50; // Same as the button's hover background color.
+ background-color: var(--super-sidebar-user-bar-button-hover-bg);
}
}
}
@@ -137,6 +187,10 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
}
}
+ .super-sidebar-help-center-toggle[aria-expanded='true'] {
+ @include active-toggle($gray-50);
+ }
+
#trial-status-sidebar-widget:hover {
text-decoration: none;
@include gl-text-contrast-light;
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index a908d49ae38..5422fc0ec0a 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -1126,6 +1126,10 @@ $tabs-holder-z-index: 250;
.submit-review-dropdown {
margin-left: $grid-size;
+
+ .md-header {
+ top: -$gl-spacing-scale-2;
+ }
}
}
@@ -1214,3 +1218,7 @@ $tabs-holder-z-index: 250;
@include gl-rounded-top-right-none;
}
}
+
+.merge-request-overview .md-header {
+ top: calc(#{$calc-application-header-height} + #{$mr-sticky-header-height});
+}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index e82a689fe5d..052e3326318 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -268,3 +268,25 @@ ul.related-merge-requests > li gl-emoji {
.issuable-header-slide-leave-to {
transform: translateY(-100%);
}
+
+.issuable-sticky-header-visible {
+ --issuable-sticky-header-height: 40px;
+}
+
+.md-header-preview {
+ z-index: 1;
+ position: sticky;
+ top: calc(#{$calc-application-header-height} + var(--issuable-sticky-header-height, 0px));
+}
+
+.detail-page-description .md-header {
+ top: $calc-application-header-height;
+}
+
+.gl-drawer .md-header {
+ top: 0;
+}
+
+.gl-modal .md-header {
+ top: -$gl-padding-8;
+}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index ec9bdc35337..eb2061081ae 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -264,11 +264,6 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
display: block;
position: relative;
- .timeline-discussion-body {
- overflow-x: auto;
- overflow-y: hidden;
- }
-
.diff-content {
overflow: visible;
padding: 0;
@@ -330,8 +325,6 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
.note-body {
padding: 0 $gl-padding-8 $gl-padding-8;
- overflow-x: auto;
- overflow-y: hidden;
.note-text {
word-wrap: break-word;
@@ -747,8 +740,6 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
}
.timeline-content {
- overflow-x: auto;
- overflow-y: hidden;
border-radius: $gl-border-radius-base;
padding: $gl-padding-8 !important;
@include gl-border;
diff --git a/app/assets/stylesheets/themes/theme_helper.scss b/app/assets/stylesheets/themes/theme_helper.scss
index 9d4f6cabcd9..6839d236cdc 100644
--- a/app/assets/stylesheets/themes/theme_helper.scss
+++ b/app/assets/stylesheets/themes/theme_helper.scss
@@ -273,61 +273,32 @@
$theme-color,
$theme-color-darkest,
) {
- --sidebar-background: #{mix(white, $theme-color-lightest, 50%)};
- --transparent-white-16: rgba(255, 255, 255, 0.16);
- --transparent-white-24: rgba(255, 255, 255, 0.24);
-
.super-sidebar {
- background-color: var(--sidebar-background);
- }
-
- .super-sidebar .user-bar {
- background-color: $theme-color;
-
- .counter {
- background-color: var(--transparent-white-16) !important;
- }
-
- .brand-logo,
- .btn-default-tertiary,
- .counter {
- color: $theme-color-lightest;
- mix-blend-mode: normal;
-
- &:hover,
- &:focus {
- background-color: var(--transparent-white-24) !important;
- color: $white;
- }
-
- .gl-icon {
- color: $theme-color-light;
+ --super-sidebar-bg: #{mix(white, $theme-color-lightest, 50%)};
+ --super-sidebar-user-bar-bg: #{$theme-color};
+ --super-sidebar-user-bar-button-bg: rgba(255, 255, 255, 0.16);
+ --super-sidebar-user-bar-button-hover-bg: rgba(255, 255, 255, 0.24);
+ --super-sidebar-user-bar-button-color: #{$theme-color-lightest};
+ --super-sidebar-user-bar-button-hover-color: #{$white};
+ --super-sidebar-user-bar-button-icon-color: #{$theme-color-light};
+ --super-sidebar-user-bar-button-icon-hover-color: #{$theme-color-light};
+ --super-sidebar-primary: #{$theme-color};
+ --super-sidebar-notification-dot: #{$theme-color-darkest};
+
+ .user-bar {
+ .brand-logo,
+ .btn-default-tertiary,
+ .counter {
+ mix-blend-mode: normal;
}
}
- }
- .super-sidebar hr {
- mix-blend-mode: multiply;
- }
-
- .btn-with-notification {
- &:hover,
- &:focus {
+ hr {
mix-blend-mode: multiply;
}
- .notification-dot-info {
- background-color: $theme-color-darkest;
- border-color: $theme-color-lightest;
-
+ .super-sidebar-context-header {
+ color: var(--super-sidebar-primary);
}
}
-
- .active-indicator {
- background-color: $theme-color;
- }
-
- .super-sidebar-context-header {
- color: $theme-color;
- }
}
diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb
index 163e741d990..a990837826d 100644
--- a/app/models/audit_event.rb
+++ b/app/models/audit_event.rb
@@ -45,6 +45,10 @@ class AuditEvent < ApplicationRecord
# https://gitlab.com/groups/gitlab-org/-/epics/2765
after_validation :parallel_persist
+ def self.supported_keyset_orderings
+ { id: [:desc] }
+ end
+
def self.order_by(method)
case method.to_s
when 'created_asc'
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 4b548405bb9..6d8b2711734 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -217,6 +217,10 @@ module Ci
job_variables_attributes resource_group scheduling_type
ci_stage partition_id id_tokens].freeze
end
+
+ def supported_keyset_orderings
+ { id: [:desc] }
+ end
end
state_machine :status do
diff --git a/app/models/ci/sources/pipeline.rb b/app/models/ci/sources/pipeline.rb
index 475d57ee4c8..3c6a5563846 100644
--- a/app/models/ci/sources/pipeline.rb
+++ b/app/models/ci/sources/pipeline.rb
@@ -6,11 +6,6 @@ module Ci
include Ci::Partitionable
include Ci::NamespacedModelName
include SafelyChangeColumnDefault
- include IgnorableColumns
-
- ignore_columns [
- :pipeline_id_convert_to_bigint, :source_pipeline_id_convert_to_bigint
- ], remove_with: '16.6', remove_after: '2023-10-22'
columns_changing_default :partition_id, :source_partition_id
diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb
index 447603c1635..03cce1edf74 100644
--- a/app/models/concerns/ci/partitionable.rb
+++ b/app/models/concerns/ci/partitionable.rb
@@ -19,42 +19,6 @@ module Ci
extend ActiveSupport::Concern
include ::Gitlab::Utils::StrongMemoize
- module Testing
- InclusionError = Class.new(StandardError)
-
- PARTITIONABLE_MODELS = %w[
- CommitStatus
- Ci::BuildMetadata
- Ci::BuildNeed
- Ci::BuildReportResult
- Ci::BuildRunnerSession
- Ci::BuildTraceChunk
- Ci::BuildTraceMetadata
- Ci::BuildPendingState
- Ci::JobAnnotation
- Ci::JobArtifact
- Ci::JobVariable
- Ci::Pipeline
- Ci::PendingBuild
- Ci::RunningBuild
- Ci::RunnerManagerBuild
- Ci::PipelineVariable
- Ci::Sources::Pipeline
- Ci::Stage
- Ci::UnitTestFailure
- ].freeze
-
- def self.check_inclusion(klass)
- return if PARTITIONABLE_MODELS.include?(klass.name)
-
- raise Partitionable::Testing::InclusionError,
- "#{klass} must be included in PARTITIONABLE_MODELS"
-
- rescue InclusionError => e
- Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
- end
- end
-
included do
Partitionable::Testing.check_inclusion(self)
diff --git a/app/models/concerns/ci/partitionable/testing.rb b/app/models/concerns/ci/partitionable/testing.rb
new file mode 100644
index 00000000000..b961d72db94
--- /dev/null
+++ b/app/models/concerns/ci/partitionable/testing.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Ci
+ module Partitionable
+ module Testing
+ InclusionError = Class.new(StandardError)
+
+ PARTITIONABLE_MODELS = %w[
+ CommitStatus
+ Ci::BuildMetadata
+ Ci::BuildNeed
+ Ci::BuildReportResult
+ Ci::BuildRunnerSession
+ Ci::BuildTraceChunk
+ Ci::BuildTraceMetadata
+ Ci::BuildPendingState
+ Ci::JobAnnotation
+ Ci::JobArtifact
+ Ci::JobVariable
+ Ci::Pipeline
+ Ci::PendingBuild
+ Ci::RunningBuild
+ Ci::RunnerManagerBuild
+ Ci::PipelineVariable
+ Ci::Sources::Pipeline
+ Ci::Stage
+ Ci::UnitTestFailure
+ ].freeze
+
+ def self.check_inclusion(klass)
+ return if partitionable_models.include?(klass.name)
+
+ raise Partitionable::Testing::InclusionError,
+ "#{klass} must be included in PARTITIONABLE_MODELS"
+
+ rescue InclusionError => e
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
+ end
+
+ def self.partitionable_models
+ PARTITIONABLE_MODELS
+ end
+ end
+ end
+end
+
+Ci::Partitionable::Testing.prepend_mod
diff --git a/app/models/group.rb b/app/models/group.rb
index 90b4067b037..59383b51bb9 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -29,6 +29,10 @@ class Group < Namespace
'Group'
end
+ def self.supported_keyset_orderings
+ { name: [:asc] }
+ end
+
has_many :all_group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent
has_many :group_members, -> { where(requested_at: nil).where.not(members: { access_level: Gitlab::Access::MINIMAL_ACCESS }) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
has_many :namespace_members, -> { where(requested_at: nil).where.not(members: { access_level: Gitlab::Access::MINIMAL_ACCESS }).unscope(where: %i[source_id source_type]) },
diff --git a/app/models/packages/build_info.rb b/app/models/packages/build_info.rb
index 61e2194006b..6b200302aae 100644
--- a/app/models/packages/build_info.rb
+++ b/app/models/packages/build_info.rb
@@ -9,4 +9,8 @@ class Packages::BuildInfo < ApplicationRecord
scope :order_by_pipeline_id, -> (direction) { order(pipeline_id: direction) }
scope :with_pipeline_id_less_than, -> (pipeline_id) { where("#{table_name}.pipeline_id < ?", pipeline_id) }
scope :with_pipeline_id_greater_than, -> (pipeline_id) { where("#{table_name}.pipeline_id > ?", pipeline_id) }
+
+ def self.supported_keyset_orderings
+ { id: [:desc] }
+ end
end
diff --git a/app/models/pages/lookup_path.rb b/app/models/pages/lookup_path.rb
index e5e23c3bb84..d79e4a5c12e 100644
--- a/app/models/pages/lookup_path.rb
+++ b/app/models/pages/lookup_path.rb
@@ -4,10 +4,11 @@ module Pages
class LookupPath
include Gitlab::Utils::StrongMemoize
- def initialize(project, trim_prefix: nil, domain: nil)
- @project = project
+ def initialize(deployment:, domain: nil, trim_prefix: nil)
+ @deployment = deployment
+ @project = deployment.project
@domain = domain
- @trim_prefix = trim_prefix || project.full_path
+ @trim_prefix = trim_prefix || @project.full_path
end
def project_id
@@ -45,11 +46,7 @@ module Pages
strong_memoize_attr :source
def prefix
- if url_builder.namespace_pages?
- '/'
- else
- "#{project.full_path.delete_prefix(trim_prefix)}/"
- end
+ ensure_leading_and_trailing_slash(prefix_value)
end
strong_memoize_attr :prefix
@@ -73,23 +70,24 @@ module Pages
private
- attr_reader :project, :trim_prefix, :domain
-
- # project.active_pages_deployments is already loaded from the database,
- # so selecting from the array to avoid N+1
- # this will change with when serving multiple versions on
- # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/133261
- def deployment
- project
- .active_pages_deployments
- .to_a
- .find { |deployment| deployment.path_prefix.blank? }
- end
- strong_memoize_attr :deployment
+ attr_reader :project, :deployment, :trim_prefix, :domain
def url_builder
Gitlab::Pages::UrlBuilder.new(project)
end
strong_memoize_attr :url_builder
+
+ def prefix_value
+ return deployment.path_prefix if url_builder.namespace_pages?
+
+ [project.full_path.delete_prefix(trim_prefix), deployment.path_prefix].compact.join('/')
+ end
+
+ def ensure_leading_and_trailing_slash(value)
+ value
+ .to_s
+ .then { |s| s.start_with?("/") ? s : "/#{s}" }
+ .then { |s| s.end_with?("/") ? s : "#{s}/" }
+ end
end
end
diff --git a/app/models/pages/virtual_domain.rb b/app/models/pages/virtual_domain.rb
index 0a64e91bf60..94ad1491889 100644
--- a/app/models/pages/virtual_domain.rb
+++ b/app/models/pages/virtual_domain.rb
@@ -17,11 +17,7 @@ module Pages
end
def lookup_paths
- projects
- .map { |project| lookup_paths_for(project) }
- .select(&:source) # TODO: remove in https://gitlab.com/gitlab-org/gitlab/-/issues/328715
- .sort_by(&:prefix)
- .reverse
+ projects.flat_map { |project| lookup_paths_for(project) }
end
private
@@ -29,7 +25,26 @@ module Pages
attr_reader :projects, :trim_prefix, :domain
def lookup_paths_for(project)
- Pages::LookupPath.new(project, trim_prefix: trim_prefix, domain: domain)
+ deployments_for(project).map do |deployment|
+ Pages::LookupPath.new(
+ deployment: deployment,
+ trim_prefix: trim_prefix,
+ domain: domain)
+ end
+ end
+
+ def deployments_for(project)
+ if ::Gitlab::Pages.multiple_versions_enabled_for?(project)
+ project.active_pages_deployments
+ else
+ # project.active_pages_deployments is already loaded from the database,
+ # so finding from the array to avoid N+1
+ project
+ .active_pages_deployments
+ .to_a
+ .find { |deployment| deployment.path_prefix.blank? }
+ .then { |deployment| [deployment] }
+ end
end
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index e6b2d3e1342..bb3b0100591 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -603,6 +603,16 @@ class User < MainClusterwide::ApplicationRecord
.trusted_with_spam)
end
+ def self.supported_keyset_orderings
+ {
+ id: [:asc, :desc],
+ name: [:asc, :desc],
+ username: [:asc, :desc],
+ created_at: [:asc, :desc],
+ updated_at: [:asc, :desc]
+ }
+ end
+
strip_attributes! :name
def preferred_language
diff --git a/app/services/merge_requests/mergeability/detailed_merge_status_service.rb b/app/services/merge_requests/mergeability/detailed_merge_status_service.rb
index 06a68466e87..2e28ffc4363 100644
--- a/app/services/merge_requests/mergeability/detailed_merge_status_service.rb
+++ b/app/services/merge_requests/mergeability/detailed_merge_status_service.rb
@@ -61,7 +61,7 @@ module MergeRequests
end
def ci_check_failed_check
- if merge_request.actual_head_pipeline&.running?
+ if merge_request.actual_head_pipeline&.active?
:ci_still_running
else
check_ci_results.payload.fetch(:identifier)
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 0bb88efe183..d30ff5ce4e6 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -2649,6 +2649,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: bulk_imports_transform_references
+ :worker_name: BulkImports::TransformReferencesWorker
+ :feature_category: :importers
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: chat_notification
:worker_name: ChatNotificationWorker
:feature_category: :integrations
diff --git a/app/workers/bulk_imports/transform_references_worker.rb b/app/workers/bulk_imports/transform_references_worker.rb
new file mode 100644
index 00000000000..383ad2fd733
--- /dev/null
+++ b/app/workers/bulk_imports/transform_references_worker.rb
@@ -0,0 +1,147 @@
+# frozen_string_literal: true
+
+module BulkImports
+ class TransformReferencesWorker
+ include ApplicationWorker
+
+ idempotent!
+ data_consistency :delayed
+ sidekiq_options retry: 3, dead: false
+ feature_category :importers
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(object_ids, klass, tracker_id)
+ @tracker = BulkImports::Tracker.find_by_id(tracker_id)
+
+ return unless tracker
+
+ project = tracker.entity.project
+
+ klass.constantize.where(id: object_ids, project: project).find_each do |object|
+ transform_and_save(object)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ attr_reader :tracker
+
+ private
+
+ def transform_and_save(object)
+ body = object_body(object).dup
+
+ return if body.blank?
+
+ object.refresh_markdown_cache!
+
+ body.gsub!(username_regex(mapped_usernames), mapped_usernames)
+
+ if object_has_reference?(body)
+ matching_urls(object).each do |old_url, new_url|
+ body.gsub!(old_url, new_url) if body.include?(old_url)
+ end
+ end
+
+ object.assign_attributes(body_field(object) => body)
+ object.save!(touch: false) if object_body_changed?(object)
+
+ object
+ rescue StandardError => e
+ log_and_fail(e)
+ end
+
+ def object_body(object)
+ call_object_method(object)
+ end
+
+ def object_body_changed?(object)
+ call_object_method(object, suffix: '_changed?')
+ end
+
+ def call_object_method(object, suffix: nil)
+ method = body_field(object)
+ method = "#{method}#{suffix}" if suffix.present?
+
+ object.public_send(method) # rubocop:disable GitlabSecurity/PublicSend -- the method being called is dependent on several factors
+ end
+
+ def body_field(object)
+ object.is_a?(Note) ? 'note' : 'description'
+ end
+
+ def mapped_usernames
+ @mapped_usernames ||= ::BulkImports::UsersMapper.new(context: context)
+ .map_usernames.transform_keys { |key| "@#{key}" }
+ .transform_values { |value| "@#{value}" }
+ end
+
+ def username_regex(mapped_usernames)
+ @username_regex ||= Regexp.new(mapped_usernames.keys.sort_by(&:length)
+ .reverse.map { |x| Regexp.escape(x) }.join('|'))
+ end
+
+ def matching_urls(object)
+ URI.extract(object_body(object), %w[http https]).each_with_object([]) do |url, array|
+ parsed_url = URI.parse(url)
+
+ next unless source_host == parsed_url.host
+ next unless parsed_url.path&.start_with?("/#{source_full_path}")
+
+ array << [url, new_url(object, parsed_url)]
+ end
+ end
+
+ def new_url(object, parsed_old_url)
+ parsed_old_url.host = ::Gitlab.config.gitlab.host
+ parsed_old_url.port = ::Gitlab.config.gitlab.port
+ parsed_old_url.scheme = ::Gitlab.config.gitlab.https ? 'https' : 'http'
+ parsed_old_url.to_s.gsub!(source_full_path, full_path(object))
+ end
+
+ def source_host
+ @source_host ||= URI.parse(context.configuration.url).host
+ end
+
+ def source_full_path
+ @source_full_path ||= context.entity.source_full_path
+ end
+
+ def full_path(object)
+ object.project.full_path
+ end
+
+ def object_has_reference?(body)
+ body.include?(source_full_path)
+ end
+
+ def log_and_fail(exception)
+ Gitlab::ErrorTracking.track_exception(exception, log_params)
+ BulkImports::Failure.create(failure_attributes(exception))
+ end
+
+ def log_params
+ {
+ message: 'Failed to update references',
+ bulk_import_id: context.bulk_import_id,
+ bulk_import_entity_id: tracker.bulk_import_entity_id,
+ source_full_path: context.entity.source_full_path,
+ source_version: context.bulk_import.source_version,
+ importer: 'gitlab_migration'
+ }
+ end
+
+ def failure_attributes(exception)
+ {
+ bulk_import_entity_id: context.entity.id,
+ pipeline_class: 'ReferencesPipeline',
+ exception_class: exception.class.to_s,
+ exception_message: exception.message.truncate(255),
+ correlation_id_value: Labkit::Correlation::CorrelationId.current_or_new_id
+ }
+ end
+
+ def context
+ @context ||= BulkImports::Pipeline::Context.new(tracker)
+ end
+ end
+end