diff options
Diffstat (limited to 'app')
24 files changed, 264 insertions, 112 deletions
diff --git a/app/assets/javascripts/ci/catalog/components/details/ci_resource_header.vue b/app/assets/javascripts/ci/catalog/components/details/ci_resource_header.vue index ff2dcd4393a..0b14a8e81a5 100644 --- a/app/assets/javascripts/ci/catalog/components/details/ci_resource_header.vue +++ b/app/assets/javascripts/ci/catalog/components/details/ci_resource_header.vue @@ -1,6 +1,7 @@ <script> import { GlAvatar, GlAvatarLink, GlBadge } from '@gitlab/ui'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import { cleanLeadingSeparator } from '~/lib/utils/url_utility'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiResourceAbout from './ci_resource_about.vue'; import CiResourceHeaderSkeletonLoader from './ci_resource_header_skeleton_loader.vue'; @@ -47,9 +48,6 @@ export default { entityId() { return getIdFromGraphQLId(this.resource.id); }, - fullPath() { - return `${this.rootNamespace.fullPath}/${this.rootNamespace.name}`; - }, hasLatestVersion() { return this.latestVersion?.tagName; }, @@ -59,12 +57,12 @@ export default { latestVersion() { return this.resource.latestVersion; }, - rootNamespace() { - return this.resource.rootNamespace; - }, versionBadgeText() { return this.latestVersion.tagName; }, + webPath() { + return cleanLeadingSeparator(this.resource?.webPath); + }, }, }; </script> @@ -86,7 +84,7 @@ export default { class="gl-display-flex gl-flex-direction-column gl-align-items-flex-start gl-justify-content-center" > <div class="gl-font-sm gl-text-secondary"> - {{ fullPath }} + {{ webPath }} </div> <span class="gl-display-flex"> <div class="gl-font-lg gl-font-weight-bold">{{ resource.name }}</div> diff --git a/app/assets/javascripts/ci/catalog/components/list/catalog_list_skeleton_loader.vue b/app/assets/javascripts/ci/catalog/components/list/catalog_list_skeleton_loader.vue index 3722b8e6c59..5de71fa1fc5 100644 --- a/app/assets/javascripts/ci/catalog/components/list/catalog_list_skeleton_loader.vue +++ b/app/assets/javascripts/ci/catalog/components/list/catalog_list_skeleton_loader.vue @@ -37,21 +37,19 @@ export default { > <!-- Catalog project avatar --> <rect x="0" y="0" width="48" height="48" rx="4" ry="4" /> - <!-- namespace path --> - <rect x="60" y="4" width="400" height="16" rx="2" ry="2" /> + <!-- resource path --> + <rect x="60" y="0" width="200" height="10" rx="2" ry="2" /> + <!-- resource name --> + <rect x="60" y="14" width="400" height="16" rx="2" ry="2" /> <!-- Project description --> - <rect x="60" y="30" width="500" height="12" rx="2" ry="2" /> + <rect x="60" y="34" width="500" height="12" rx="2" ry="2" /> <!-- Release date line --> <rect :x="coordinates.releaseDateX" y="30" width="200" height="12" rx="2" ry="2" /> <!-- Favorites --> - <rect :x="coordinates.statsX" y="4" width="16" height="16" rx="2" ry="2" /> - <rect :x="coordinates.statsX + 18" y="7" width="18" height="10" rx="2" ry="2" /> - - <!-- Forks --> <rect :x="coordinates.statsX + 50" y="4" width="16" height="16" rx="2" ry="2" /> - <rect :x="coordinates.statsX + 68" y="7" width="18" height="10" rx="2" ry="2" /> + <rect :x="coordinates.statsX + 70" y="7" width="18" height="10" rx="2" ry="2" /> </gl-skeleton-loader> </div> </template> diff --git a/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue b/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue index 5bbf11e541f..504d3d38275 100644 --- a/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue +++ b/app/assets/javascripts/ci/catalog/components/list/ci_resources_list_item.vue @@ -3,6 +3,7 @@ import { GlAvatar, GlBadge, GlIcon, GlLink, GlSprintf, GlTooltipDirective } from import { s__ } from '~/locale'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { formatDate, getTimeago } from '~/lib/utils/datetime_utility'; +import { cleanLeadingSeparator } from '~/lib/utils/url_utility'; import { CI_RESOURCE_DETAILS_PAGE_NAME } from '../../router/constants'; export default { @@ -45,6 +46,9 @@ export default { starCount() { return this.resource?.starCount || 0; }, + starIcon() { + return this.starCount > 0 ? 'star' : 'star-o'; + }, hasReleasedVersion() { return Boolean(this.latestVersion?.releasedAt); }, @@ -57,12 +61,12 @@ export default { releasedAt() { return getTimeago().format(this.latestVersion?.releasedAt); }, - resourcePath() { - return `${this.resource.rootNamespace?.name} / ${this.resource.rootNamespace?.fullPath} / `; - }, tagName() { return this.latestVersion?.tagName || this.$options.i18n.unreleased; }, + webPath() { + return cleanLeadingSeparator(this.resource?.webPath); + }, }, methods: { navigateToDetailsPage(e) { @@ -98,20 +102,21 @@ export default { @click="navigateToDetailsPage" /> <div class="gl-display-flex gl-flex-direction-column gl-flex-grow-1"> - <div class="gl-display-flex gl-flex-wrap gl-gap-2 gl-mb-2"> + <span class="gl-font-sm gl-mb-1">{{ webPath }}</span> + <div class="gl-display-flex gl-flex-wrap gl-gap-2 gl-mb-1"> <gl-link class="gl-text-gray-900! gl-mr-1" :href="detailsPageHref" data-testid="ci-resource-link" @click="navigateToDetailsPage" > - {{ resourcePath }} <b> {{ resource.name }}</b> + <b> {{ resource.name }}</b> </gl-link> <div class="gl-display-flex gl-flex-grow-1 gl-md-justify-content-space-between"> - <gl-badge size="sm">{{ tagName }}</gl-badge> + <gl-badge size="sm" class="gl-h-5 gl-align-self-center">{{ tagName }}</gl-badge> <span class="gl-display-flex gl-align-items-center gl-ml-5"> <span class="gl--flex-center" data-testid="stats-favorites"> - <gl-icon name="star" :size="14" class="gl-mr-1" /> + <gl-icon :name="starIcon" :size="14" class="gl-mr-2" /> <span class="gl-mr-3">{{ starCount }}</span> </span> </span> diff --git a/app/assets/javascripts/ci/catalog/graphql/fragments/catalog_resource.fragment.graphql b/app/assets/javascripts/ci/catalog/graphql/fragments/catalog_resource.fragment.graphql index a86db4c1b03..0dc16e3492d 100644 --- a/app/assets/javascripts/ci/catalog/graphql/fragments/catalog_resource.fragment.graphql +++ b/app/assets/javascripts/ci/catalog/graphql/fragments/catalog_resource.fragment.graphql @@ -15,10 +15,5 @@ fragment CatalogResourceFields on CiCatalogResource { webUrl } } - rootNamespace { - id - fullPath - name - } webPath } diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue index 55ae390216d..d2c1c914028 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue @@ -22,14 +22,14 @@ export default { variables() { return this.mergeRequestQueryVariables; }, - update: (data) => data.project.mergeRequest.userPermissions, + update: (data) => data.project?.mergeRequest.userPermissions || {}, }, state: { query: conflictsStateQuery, variables() { return this.mergeRequestQueryVariables; }, - update: (data) => data.project.mergeRequest, + update: (data) => data.project?.mergeRequest || {}, }, }, props: { diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index 45d3aa564a5..2440fc7d433 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -3,12 +3,10 @@ import { isEmpty } from 'lodash'; import { GlAlert, GlSkeletonLoader, - GlLoadingIcon, GlIcon, GlButton, GlTooltipDirective, GlEmptyState, - GlIntersectionObserver, } from '@gitlab/ui'; import noAccessSvg from '@gitlab/svgs/dist/illustrations/analytics/no-access.svg?raw'; import { s__ } from '~/locale'; @@ -17,7 +15,6 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { isLoggedIn } from '~/lib/utils/common_utils'; import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; -import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; import { WORKSPACE_PROJECT } from '~/issues/constants'; import { i18n, @@ -52,6 +49,7 @@ import WorkItemDetailModal from './work_item_detail_modal.vue'; import WorkItemAwardEmoji from './work_item_award_emoji.vue'; import WorkItemRelationships from './work_item_relationships/work_item_relationships.vue'; import WorkItemTypeIcon from './work_item_type_icon.vue'; +import WorkItemStickyHeader from './work_item_sticky_header.vue'; export default { i18n, @@ -62,7 +60,6 @@ export default { components: { GlAlert, GlButton, - GlLoadingIcon, GlSkeletonLoader, GlIcon, GlEmptyState, @@ -78,9 +75,8 @@ export default { WorkItemNotes, WorkItemDetailModal, AbuseCategorySelector, - GlIntersectionObserver, - ConfidentialityBadge, WorkItemRelationships, + WorkItemStickyHeader, }, mixins: [glFeatureFlagMixin()], inject: ['fullPath', 'isGroup', 'reportAbusePath'], @@ -235,7 +231,7 @@ export default { return this.isWidgetPresent(WIDGET_TYPE_CURRENT_USER_TODOS); }, showWorkItemCurrentUserTodos() { - return this.$options.isLoggedIn && this.workItemCurrentUserTodos; + return Boolean(this.$options.isLoggedIn && this.workItemCurrentUserTodos); }, currentUserTodos() { return this.workItemCurrentUserTodos?.currentUserTodos?.nodes; @@ -537,62 +533,25 @@ export default { :update-in-progress="updateInProgress" /> </div> - <gl-intersection-observer + <work-item-sticky-header v-if="showIntersectionObserver" - @appear="hideStickyHeader" - @disappear="showStickyHeader" - > - <transition name="issuable-header-slide"> - <div - v-if="isStickyHeaderShowing" - class="issue-sticky-header gl-fixed gl-bg-white gl-border-b gl-z-index-3 gl-py-2" - data-testid="work-item-sticky-header" - > - <div - class="gl-align-items-center gl-mx-auto gl-px-5 gl-display-flex gl-max-w-container-xl" - > - <span class="gl-text-truncate gl-font-weight-bold gl-pr-3 gl-mr-auto"> - {{ workItem.title }} - </span> - <gl-loading-icon v-if="updateInProgress" class="gl-mr-3" /> - <confidentiality-badge - v-if="workItem.confidential" - class="gl-mr-3" - :issuable-type="workItemType" - :workspace-type="$options.WORKSPACE_PROJECT" - /> - <work-item-todos - v-if="showWorkItemCurrentUserTodos" - :work-item-id="workItem.id" - :work-item-iid="workItemIid" - :work-item-fullpath="projectFullPath" - :current-user-todos="currentUserTodos" - @error="updateError = $event" - /> - <work-item-actions - :full-path="fullPath" - :work-item-id="workItem.id" - :subscribed-to-notifications="workItemNotificationsSubscribed" - :work-item-type="workItemType" - :work-item-type-id="workItemTypeId" - :can-delete="canDelete" - :can-update="canUpdate" - :is-confidential="workItem.confidential" - :is-parent-confidential="parentWorkItemConfidentiality" - :work-item-reference="workItem.reference" - :work-item-create-note-email="workItem.createNoteEmail" - :is-modal="isModal" - @deleteWorkItem=" - $emit('deleteWorkItem', { workItemType, workItemId: workItem.id }) - " - @toggleWorkItemConfidentiality="toggleConfidentiality" - @error="updateError = $event" - @promotedToObjective="$emit('promotedToObjective', workItemIid)" - /> - </div> - </div> - </transition> - </gl-intersection-observer> + :current-user-todos="currentUserTodos" + :show-work-item-current-user-todos="showWorkItemCurrentUserTodos" + :parent-work-item-confidentiality="parentWorkItemConfidentiality" + :update-in-progress="updateInProgress" + :full-path="fullPath" + :is-modal="isModal" + :work-item="workItem" + :is-sticky-header-showing="isStickyHeaderShowing" + :work-item-parent-id="workItemParentId" + :work-item-notifications-subscribed="workItemNotificationsSubscribed" + @hideStickyHeader="hideStickyHeader" + @showStickyHeader="showStickyHeader" + @deleteWorkItem="$emit('deleteWorkItem', { workItemType, workItemId: workItem.id })" + @toggleWorkItemConfidentiality="toggleConfidentiality" + @error="updateError = $event" + @promotedToObjective="$emit('promotedToObjective', workItemIid)" + /> <div data-testid="work-item-overview" :class="{ 'work-item-overview': workItemsMvc2Enabled }" diff --git a/app/assets/javascripts/work_items/components/work_item_sticky_header.vue b/app/assets/javascripts/work_items/components/work_item_sticky_header.vue new file mode 100644 index 00000000000..ec6d2c21748 --- /dev/null +++ b/app/assets/javascripts/work_items/components/work_item_sticky_header.vue @@ -0,0 +1,142 @@ +<script> +import { GlLoadingIcon, GlIntersectionObserver } from '@gitlab/ui'; +import { WORKSPACE_PROJECT } from '~/issues/constants'; +import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; +import WorkItemActions from './work_item_actions.vue'; +import WorkItemTodos from './work_item_todos.vue'; + +export default { + components: { + GlIntersectionObserver, + GlLoadingIcon, + WorkItemActions, + WorkItemTodos, + ConfidentialityBadge, + }, + props: { + workItem: { + type: Object, + required: true, + }, + fullPath: { + type: String, + required: true, + }, + isStickyHeaderShowing: { + type: Boolean, + required: true, + }, + workItemNotificationsSubscribed: { + type: Boolean, + required: true, + }, + workItemParentId: { + type: String, + required: false, + default: null, + }, + updateInProgress: { + type: Boolean, + required: false, + default: false, + }, + parentWorkItemConfidentiality: { + type: Boolean, + required: false, + default: false, + }, + showWorkItemCurrentUserTodos: { + type: Boolean, + required: false, + default: false, + }, + isModal: { + type: Boolean, + required: false, + default: false, + }, + currentUserTodos: { + type: Array, + required: false, + default: () => [], + }, + }, + computed: { + canUpdate() { + return this.workItem.userPermissions?.updateWorkItem; + }, + canDelete() { + return this.workItem.userPermissions?.deleteWorkItem; + }, + workItemType() { + return this.workItem.workItemType?.name; + }, + workItemTypeId() { + return this.workItem.workItemType?.id; + }, + projectFullPath() { + return this.workItem.namespace?.fullPath; + }, + }, + WORKSPACE_PROJECT, +}; +</script> + +<template> + <gl-intersection-observer + @appear="$emit('hideStickyHeader')" + @disappear="$emit('showStickyHeader')" + > + <transition name="issuable-header-slide"> + <div + v-if="isStickyHeaderShowing" + class="issue-sticky-header gl-fixed gl-bg-white gl-border-b gl-z-index-3 gl-py-2" + data-testid="work-item-sticky-header" + > + <div + class="gl-align-items-center gl-mx-auto gl-px-5 gl-display-flex gl-max-w-container-xl gl-gap-3" + > + <span class="gl-text-truncate gl-font-weight-bold gl-pr-3 gl-mr-auto"> + {{ workItem.title }} + </span> + <gl-loading-icon v-if="updateInProgress" /> + <confidentiality-badge + v-if="workItem.confidential" + :issuable-type="workItemType" + :workspace-type="$options.WORKSPACE_PROJECT" + /> + <work-item-todos + v-if="showWorkItemCurrentUserTodos" + :work-item-id="workItem.id" + :work-item-iid="workItem.iid" + :work-item-fullpath="projectFullPath" + :current-user-todos="currentUserTodos" + @error="$emit('error')" + /> + <work-item-actions + :full-path="fullPath" + :work-item-id="workItem.id" + :subscribed-to-notifications="workItemNotificationsSubscribed" + :work-item-type="workItemType" + :work-item-type-id="workItemTypeId" + :can-delete="canDelete" + :can-update="canUpdate" + :is-confidential="workItem.confidential" + :is-parent-confidential="parentWorkItemConfidentiality" + :work-item-reference="workItem.reference" + :work-item-create-note-email="workItem.createNoteEmail" + :work-item-state="workItem.state" + :work-item-parent-id="workItemParentId" + :is-modal="isModal" + @deleteWorkItem="$emit('deleteWorkItem')" + @toggleWorkItemConfidentiality=" + $emit('toggleWorkItemConfidentiality', !workItem.confidential) + " + @error="$emit('error')" + @promotedToObjective="$emit('promotedToObjective')" + /> + </div> + </div> + </transition> + </gl-intersection-observer> +</template> diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss index 5422fc0ec0a..e65f716d71a 100644 --- a/app/assets/stylesheets/page_bundles/merge_requests.scss +++ b/app/assets/stylesheets/page_bundles/merge_requests.scss @@ -552,7 +552,7 @@ $tabs-holder-z-index: 250; .mr-widget-section:not(:first-child) > div, .mr-widget-section:not(:first-child) > section, - .mr-widget-section .mr-widget-section > div { + .mr-widget-section .mr-widget-section > div:not(:first-child) { border-top: solid 1px var(--border-color, $border-color); } diff --git a/app/finders/events_finder.rb b/app/finders/events_finder.rb index 7bccfe453ab..4ed447a90ce 100644 --- a/app/finders/events_finder.rb +++ b/app/finders/events_finder.rb @@ -61,7 +61,6 @@ class EventsFinder def by_current_user_access(events) events.merge(Project.public_or_visible_to_user(current_user)) .joins(:project) - .allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/417462") end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb index 6348bceb157..5b4b83652df 100644 --- a/app/finders/members_finder.rb +++ b/app/finders/members_finder.rb @@ -90,10 +90,8 @@ class MembersFinder # enumerate the columns here since we are enumerating them in the union and want to be immune to # column caching issues when adding/removing columns - members = Member.select(*Member.column_names) - .includes(:user).from([Arel.sql("(#{sql}) AS #{Member.table_name}")]) # rubocop: disable CodeReuse/ActiveRecord - # The left join with the table users in the method distinct_on needs to be resolved - members.allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/417456") + Member.select(*Member.column_names) + .preload(:user).from([Arel.sql("(#{sql}) AS #{Member.table_name}")]) # rubocop: disable CodeReuse/ActiveRecord -- TODO: Usage of `from` forces us to use this. end def distinct_on(union) @@ -102,8 +100,7 @@ class MembersFinder <<~SQL SELECT DISTINCT ON (user_id, invite_email) #{member_columns} FROM (#{union.to_sql}) AS #{member_union_table} - LEFT JOIN users on users.id = member_union.user_id - LEFT JOIN project_authorizations on project_authorizations.user_id = users.id + LEFT JOIN project_authorizations on project_authorizations.user_id = member_union.user_id AND project_authorizations.project_id = #{project.id} ORDER BY user_id, diff --git a/app/models/ci/build_need.rb b/app/models/ci/build_need.rb index 1831b7868f9..fb0a02ca56e 100644 --- a/app/models/ci/build_need.rb +++ b/app/models/ci/build_need.rb @@ -11,7 +11,12 @@ module Ci columns_changing_default :partition_id - belongs_to :build, class_name: "Ci::Processable", foreign_key: :build_id, inverse_of: :needs + belongs_to :build, + ->(need) { in_partition(need) }, + class_name: 'Ci::Processable', + foreign_key: :build_id, + partition_foreign_key: :partition_id, + inverse_of: :needs partitionable scope: :build @@ -19,7 +24,10 @@ module Ci validates :name, presence: true, length: { maximum: MAX_JOB_NAME_LENGTH } validates :optional, inclusion: { in: [true, false] } - scope :scoped_build, -> { where("#{Ci::Build.quoted_table_name}.id = #{quoted_table_name}.build_id") } + scope :scoped_build, -> { + where(arel_table[:build_id].eq(Ci::Build.arel_table[:id])) + .where(arel_table[:partition_id].eq(Ci::Build.arel_table[:partition_id])) + } scope :artifacts, -> { where(artifacts: true) } end end diff --git a/app/models/ci/build_report_result.rb b/app/models/ci/build_report_result.rb index 90b621b8da1..1dabf05e03c 100644 --- a/app/models/ci/build_report_result.rb +++ b/app/models/ci/build_report_result.rb @@ -9,7 +9,11 @@ module Ci self.primary_key = :build_id - belongs_to :build, class_name: "Ci::Build", inverse_of: :report_results + belongs_to :build, + ->(report_result) { in_partition(report_result) }, + class_name: 'Ci::Build', + partition_foreign_key: :partition_id, + inverse_of: :report_results belongs_to :project, class_name: "Project", inverse_of: :build_report_results partitionable scope: :build diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb index e197217bb70..223cb9c5d68 100644 --- a/app/models/ci/build_runner_session.rb +++ b/app/models/ci/build_runner_session.rb @@ -15,7 +15,11 @@ module Ci self.table_name = 'ci_builds_runner_session' - belongs_to :build, class_name: 'Ci::Build', inverse_of: :runner_session + belongs_to :build, + ->(runner_session) { in_partition(runner_session) }, + class_name: 'Ci::Build', + partition_foreign_key: :partition_id, + inverse_of: :runner_session partitionable scope: :build diff --git a/app/models/ci/build_trace_metadata.rb b/app/models/ci/build_trace_metadata.rb index 525cb08f2ca..859dad29a06 100644 --- a/app/models/ci/build_trace_metadata.rb +++ b/app/models/ci/build_trace_metadata.rb @@ -11,7 +11,11 @@ module Ci self.table_name = 'ci_build_trace_metadata' self.primary_key = :build_id - belongs_to :build, class_name: 'Ci::Build' + belongs_to :build, + ->(trace_metadata) { in_partition(trace_metadata) }, + class_name: 'Ci::Build', + partition_foreign_key: :partition_id, + inverse_of: :trace_metadata belongs_to :trace_artifact, class_name: 'Ci::JobArtifact' partitionable scope: :build diff --git a/app/models/ci/job_annotation.rb b/app/models/ci/job_annotation.rb index a6ce4196cc1..d56830433b1 100644 --- a/app/models/ci/job_annotation.rb +++ b/app/models/ci/job_annotation.rb @@ -8,7 +8,11 @@ module Ci self.table_name = :p_ci_job_annotations self.primary_key = :id - belongs_to :job, class_name: 'Ci::Build', inverse_of: :job_annotations + belongs_to :job, + ->(job_annotation) { in_partition(job_annotation) }, + class_name: 'Ci::Build', + partition_foreign_key: :partition_id, + inverse_of: :job_annotations partitionable scope: :job, partitioned: true diff --git a/app/models/ci/pending_build.rb b/app/models/ci/pending_build.rb index dc9a8b7a1bf..80cc74a9946 100644 --- a/app/models/ci/pending_build.rb +++ b/app/models/ci/pending_build.rb @@ -9,7 +9,11 @@ module Ci columns_changing_default :partition_id belongs_to :project - belongs_to :build, class_name: 'Ci::Build' + + belongs_to :build, # rubocop: disable Rails/InverseOf -- this relation is not present on build + ->(pending_build) { in_partition(pending_build) }, + class_name: 'Ci::Build', + partition_foreign_key: :partition_id belongs_to :namespace, inverse_of: :pending_builds, class_name: 'Namespace' partitionable scope: :build diff --git a/app/models/ci/running_build.rb b/app/models/ci/running_build.rb index cfdc47de531..22753930b3c 100644 --- a/app/models/ci/running_build.rb +++ b/app/models/ci/running_build.rb @@ -17,7 +17,10 @@ module Ci partitionable scope: :build belongs_to :project - belongs_to :build, class_name: 'Ci::Build' + belongs_to :build, # rubocop: disable Rails/InverseOf -- this relation is not present on build + ->(running_build) { in_partition(running_build) }, + class_name: 'Ci::Build', + partition_foreign_key: :partition_id belongs_to :runner, class_name: 'Ci::Runner' enum runner_type: ::Ci::Runner.runner_types diff --git a/app/models/ci/sources/pipeline.rb b/app/models/ci/sources/pipeline.rb index 3c6a5563846..6c9d3b9c19f 100644 --- a/app/models/ci/sources/pipeline.rb +++ b/app/models/ci/sources/pipeline.rb @@ -13,7 +13,7 @@ module Ci belongs_to :project, class_name: "::Project" belongs_to :pipeline, class_name: "Ci::Pipeline", inverse_of: :source_pipeline - belongs_to :build, class_name: "Ci::Build", foreign_key: :source_job_id, inverse_of: :sourced_pipelines + belongs_to :build, class_name: 'Ci::Build', foreign_key: :source_job_id, inverse_of: :sourced_pipelines belongs_to :source_project, class_name: "::Project", foreign_key: :source_project_id belongs_to :source_job, class_name: "CommitStatus", foreign_key: :source_job_id diff --git a/app/models/ci/unit_test_failure.rb b/app/models/ci/unit_test_failure.rb index 37893f6cdae..bec74d8be20 100644 --- a/app/models/ci/unit_test_failure.rb +++ b/app/models/ci/unit_test_failure.rb @@ -12,7 +12,11 @@ module Ci validates :unit_test, :build, :failed_at, presence: true belongs_to :unit_test, class_name: "Ci::UnitTest", foreign_key: :unit_test_id - belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id + belongs_to :build, + ->(unit_test_failure) { in_partition(unit_test_failure) }, + class_name: 'Ci::Build', + foreign_key: :build_id, + partition_foreign_key: :partition_id partitionable scope: :build diff --git a/app/models/event.rb b/app/models/event.rb index 3ff5a46f2d9..7b91727f7d4 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -113,8 +113,8 @@ class Event < ApplicationRecord scope :with_associations, -> do # We're using preload for "push_event_payload" as otherwise the association # is not always available (depending on the query being built). - includes(:author, :project, project: [:project_feature, :import_data, :namespace]) - .preload(:target, :push_event_payload) + includes(:project, project: [:project_feature, :import_data, :namespace]) + .preload(:author, :target, :push_event_payload) end scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) } diff --git a/app/models/ml/model.rb b/app/models/ml/model.rb index b6f7e9a0639..bec03c05151 100644 --- a/app/models/ml/model.rb +++ b/app/models/ml/model.rb @@ -31,6 +31,10 @@ module Ml scope :by_name, ->(name) { where("ml_models.name LIKE ?", "%#{sanitize_sql_like(name)}%") } # rubocop:disable GitlabSecurity/SqlInjection scope :by_project, ->(project) { where(project_id: project.id) } + def all_packages + Packages::MlModel::Package.where(project: project, id: versions.select(:package_id)) + end + def valid_default_experiment? return unless default_experiment diff --git a/app/models/vulnerability.rb b/app/models/vulnerability.rb index da607dcb771..6bf63bab82c 100644 --- a/app/models/vulnerability.rb +++ b/app/models/vulnerability.rb @@ -5,7 +5,7 @@ class Vulnerability < ApplicationRecord include EachBatch include IgnorableColumns - ignore_column %i[epic_id milestone_id last_edited_at last_edited_by_id + ignore_column %i[due_date epic_id milestone_id last_edited_at last_edited_by_id start_date start_date_sourcing_milestone_id updated_by_id], remove_with: '16.9', remove_after: '2024-01-19' diff --git a/app/services/jira_import/users_mapper_service.rb b/app/services/jira_import/users_mapper_service.rb index 13e0dd5120e..45a607dd2b0 100644 --- a/app/services/jira_import/users_mapper_service.rb +++ b/app/services/jira_import/users_mapper_service.rb @@ -63,6 +63,7 @@ module JiraImport relations << User.by_emails(jira_emails).select("users.id, users.name, users.username, emails.email as user_email") User.from_union(relations).id_in(project_member_ids).select("users.id as user_id, users.name as name, users.username as username, user_email") + .allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/432608") end end diff --git a/app/services/ml/destroy_model_service.rb b/app/services/ml/destroy_model_service.rb new file mode 100644 index 00000000000..308d289fbe1 --- /dev/null +++ b/app/services/ml/destroy_model_service.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Ml + class DestroyModelService + def initialize(model, user) + @model = model + @user = user + end + + def execute + return unless @model.destroy + + ::Packages::MarkPackagesForDestructionService.new( + packages: @model.all_packages, + current_user: @user + ).execute + end + end +end |