Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-01-13 18:37:04 +00:00
parent 41740d257d
commit 4160a09e2d
96 changed files with 2352 additions and 841 deletions

View File

@ -1 +1 @@
eaf93c0f64c8098eb1c08113ba3d86ae71191847
de06391c38b27ccaa4e86b653d463dfcacb180a1

View File

@ -29,6 +29,7 @@ PATH
gitlab-active-context (0.0.1)
activesupport
connection_pool
elasticsearch
pg
zeitwerk

View File

@ -29,6 +29,7 @@ PATH
gitlab-active-context (0.0.1)
activesupport
connection_pool
elasticsearch
pg
zeitwerk

View File

@ -27,7 +27,7 @@ export default {
<div
v-for="stage in stages"
:key="stage.id"
class="pipeline-mini-graph-stage-container dropdown gl-mr-2 gl-inline-flex gl-align-middle"
class="pipeline-mini-graph-stage-container dropdown gl-my-1 gl-mr-2 gl-inline-flex gl-align-middle"
>
<pipeline-stage-dropdown
:stage="stage"

View File

@ -1,19 +1,17 @@
<!-- eslint-disable vue/multi-word-component-names -->
<script>
import {
GlBadge,
GlIcon,
GlLink,
GlLoadingIcon,
GlBadge,
GlSprintf,
GlTooltipDirective as GlTooltip,
GlTruncate,
} from '@gitlab/ui';
import { __, s__ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { createAlert } from '~/alert';
import deploymentDetails from '../graphql/queries/deployment_details.query.graphql';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import { localeDateFormat } from '~/lib/utils/datetime/locale_dateformat';
import DeploymentStatusLink from './deployment_status_link.vue';
import Commit from './commit.vue';
@ -23,17 +21,15 @@ export default {
Commit,
DeploymentStatusLink,
GlBadge,
GlSprintf,
GlIcon,
GlLink,
GlSprintf,
GlTruncate,
GlLoadingIcon,
TimeAgoTooltip,
TimelineEntryItem,
},
directives: {
GlTooltip,
},
inject: ['projectPath'],
props: {
deployment: {
type: Object,
@ -44,31 +40,37 @@ export default {
default: false,
required: false,
},
visible: {
type: Boolean,
default: false,
required: false,
},
},
computed: {
status() {
return this.deployment?.status;
},
iid() {
return this.deployment?.iid;
},
isTag() {
return this.deployment?.tag;
},
shortSha() {
return this.commit?.shortId;
},
timeStamp() {
return this.deployment?.deployedAt ? __('Deployed %{timeago}') : __('Created %{timeago}');
triggeredText() {
if (this.user && this.displayTime) {
return s__('Deployment|Triggered by %{username} on %{time}');
}
if (this.user && !this.displayTime) {
return s__('Deployment|Triggered by %{username}');
}
if (this.displayTime && !this.user) {
return s__('Deployment|Triggered on %{time}');
}
return '';
},
displayTimeAgo() {
deploymentTime() {
return this.deployment?.deployedAt || this.deployment?.createdAt;
},
displayTime() {
if (!this.deploymentTime) return null;
const dateTime = new Date(this.deploymentTime);
return localeDateFormat.asDateTimeFull.format(dateTime);
},
createdAt() {
return this.deployment?.createdAt;
},
@ -90,12 +92,6 @@ export default {
deployable() {
return this.deployment?.deployable;
},
jobName() {
return this.deployable?.name;
},
jobPath() {
return this.deployable?.buildPath;
},
ref() {
return this.deployment?.ref;
},
@ -108,192 +104,82 @@ export default {
needsApproval() {
return this.deployment.pendingApprovalCount > 0;
},
hasTags() {
return this.tags?.length > 0;
},
displayTags() {
return this.tags?.slice(0, 5);
},
},
apollo: {
// eslint-disable-next-line @gitlab/vue-no-undef-apollo-properties
tags: {
query: deploymentDetails,
variables() {
return {
projectPath: this.projectPath,
iid: this.deployment.iid,
};
},
update(data) {
return data?.project?.deployment?.tags;
},
error(error) {
createAlert({
message: this.$options.i18n.LOAD_ERROR_MESSAGE,
captureError: true,
error,
});
},
skip() {
return !this.visible;
},
},
},
i18n: {
latestBadge: s__('Deployment|Latest Deployed'),
deploymentId: s__('Deployment|Deployment ID'),
copyButton: __('Copy commit SHA'),
commitSha: __('Commit SHA'),
triggerer: s__('Deployment|Triggerer'),
needsApproval: s__('Deployment|Needs Approval'),
job: __('Job'),
api: __('API'),
branch: __('Branch'),
tags: __('Tags'),
tag: s__('Deployment|Tag'),
},
headerClasses: [
'gl-flex',
'gl-items-start',
'md:gl-items-center',
'gl-justify-between',
'gl-pr-6',
],
headerDetailsClasses: [
'gl-flex',
'gl-flex-col',
'md:gl-flex-row',
'gl-items-start',
'md:gl-items-center',
'gl-text-sm',
'gl-text-gray-700',
],
deploymentStatusClasses: [
'gl-flex',
'gl-gap-x-3',
'gl-mr-0',
'md:gl-mr-5',
'gl-mb-3',
'md:gl-mb-0',
],
};
</script>
<template>
<div>
<div :class="$options.headerClasses">
<div :class="$options.headerDetailsClasses">
<div :class="$options.deploymentStatusClasses">
<deployment-status-link
v-if="status"
:deployment="deployment"
:deployment-job="deployable"
:status="status"
/>
<gl-badge v-if="needsApproval" variant="warning">
{{ $options.i18n.needsApproval }}
</gl-badge>
<gl-badge v-if="latest" variant="info">{{ $options.i18n.latestBadge }}</gl-badge>
</div>
<div class="gl-flex gl-items-center gl-gap-x-5">
<div
v-if="iid"
v-gl-tooltip
class="gl-flex"
:title="$options.i18n.deploymentId"
:aria-label="$options.i18n.deploymentId"
>
<gl-icon ref="deployment-iid-icon" name="deployments" />
<span class="gl-ml-2">#{{ iid }}</span>
</div>
<div
v-if="shortSha"
data-testid="deployment-commit-sha"
class="gl-flex gl-items-center gl-font-monospace"
>
<gl-icon ref="deployment-commit-icon" name="commit" class="gl-mr-2" />
<gl-link v-gl-tooltip :title="$options.i18n.commitSha" :href="commitPath">
{{ shortSha }}
</gl-link>
<clipboard-button
:text="shortSha"
category="tertiary"
:title="$options.i18n.copyButton"
size="small"
/>
</div>
<time-ago-tooltip
v-if="displayTimeAgo"
:time="displayTimeAgo"
class="gl-flex"
data-testid="deployment-timestamp"
>
<template #default="{ timeAgo }">
<gl-icon name="calendar" class="gl-mr-2" />
<span class="gl-mr-2 gl-whitespace-nowrap">
<gl-sprintf :message="timeStamp">
<template #timeago>{{ timeAgo }}</template>
</gl-sprintf>
</span>
</template>
</time-ago-tooltip>
</div>
</div>
</div>
<commit v-if="commit" :commit="commit" class="gl-mt-3" />
<div class="gl-mt-3"><slot name="approval"></slot></div>
<div class="gl-mt-5 gl-flex gl-flex-col gl-pr-4 md:gl-flex-row md:gl-items-center md:gl-pr-0">
<div v-if="user" class="gl-flex gl-flex-col md:gl-max-w-3/20">
<span class="gl-text-subtle">{{ $options.i18n.triggerer }}</span>
<gl-link :href="userPath" class="gl-mt-3 gl-font-monospace">
<gl-truncate :text="username" with-tooltip />
</gl-link>
</div>
<div class="gl-mt-4 gl-flex gl-flex-col md:gl-mt-0 md:gl-max-w-3/20 md:gl-pl-7">
<span class="gl-text-subtle" :class="{ 'gl-ml-3': !deployable }">
{{ $options.i18n.job }}
</span>
<gl-link v-if="jobPath" :href="jobPath" class="gl-mt-3 gl-font-monospace">
<gl-truncate :text="jobName" with-tooltip position="middle" />
</gl-link>
<span v-else-if="jobName" class="gl-mt-3 gl-font-monospace">
<gl-truncate :text="jobName" with-tooltip position="middle" />
</span>
<gl-badge v-else class="gl-mt-3 gl-font-monospace" variant="info">
{{ $options.i18n.api }}
</gl-badge>
</div>
<div
v-if="ref && !isTag"
class="gl-mt-4 gl-flex gl-flex-col md:gl-mt-0 md:gl-max-w-3/20 md:gl-pl-7"
>
<span class="gl-text-subtle">{{ $options.i18n.branch }}</span>
<gl-link :href="refPath" class="gl-mt-3 gl-font-monospace">
<gl-truncate :text="refName" with-tooltip />
</gl-link>
</div>
<div
v-if="hasTags || $apollo.queries.tags.loading"
class="gl-mt-4 gl-flex gl-flex-col md:gl-mt-0 md:gl-max-w-3/20 md:gl-pl-7"
>
<span class="gl-text-subtle">{{ $options.i18n.tags }}</span>
<gl-loading-icon
v-if="$apollo.queries.tags.loading"
class="gl-mt-3 gl-font-monospace"
size="sm"
inline
<timeline-entry-item class="system-note gl-relative">
<div
class="system-note-dot gl-relative gl-float-left gl-ml-4 gl-mt-3 gl-h-3 gl-w-3 gl-rounded-full gl-border-2 gl-border-solid gl-border-subtle gl-bg-gray-900"
></div>
<div class="gl-ml-7">
<div class="gl-flex gl-flex-wrap gl-items-center gl-gap-3">
<deployment-status-link
v-if="status"
:deployment="deployment"
:deployment-job="deployable"
:status="status"
/>
<div v-if="hasTags" class="gl-flex gl-flex-row">
<gl-link
v-for="(tag, ndx) in displayTags"
:key="tag.name"
:href="tag.path"
class="gl-mr-3 gl-mt-3 gl-font-monospace"
>
{{ tag.name }}<span v-if="ndx + 1 < tags.length">, </span>
<gl-badge v-if="needsApproval" variant="warning">
{{ $options.i18n.needsApproval }}
</gl-badge>
<gl-badge v-if="latest" variant="info">{{ $options.i18n.latestBadge }}</gl-badge>
</div>
<div class="gl-flex gl-flex-wrap gl-items-center gl-gap-x-3">
<commit v-if="commit" :commit="commit" />
<div
v-if="shortSha"
data-testid="deployment-commit-sha"
class="gl-flex gl-items-center gl-font-monospace"
>
<gl-icon ref="deployment-commit-icon" name="commit" class="gl-mr-2" />
<gl-link v-gl-tooltip :title="$options.i18n.commitSha" :href="commitPath">
{{ shortSha }}
</gl-link>
<clipboard-button
:text="shortSha"
category="tertiary"
:title="$options.i18n.copyButton"
size="small"
/>
</div>
<div
v-if="isTag"
data-testid="deployment-tag"
class="gl-flex gl-items-center gl-font-monospace"
>
<gl-icon ref="deployment-tag-icon" name="tag" class="gl-mr-2" />
<gl-link v-gl-tooltip :title="$options.i18n.tag" :href="refPath">
{{ refName }}
</gl-link>
<div v-if="tags.length > 5" class="gl-mr-3 gl-mt-3 gl-font-monospace">...</div>
</div>
</div>
<div v-if="triggeredText" class="gl-flex gl-flex-wrap gl-items-center gl-gap-x-2">
<gl-sprintf :message="triggeredText">
<template #username>
<gl-link :href="userPath" data-testid="deployment-triggerer">
<gl-truncate :text="username" with-tooltip />
</gl-link>
</template>
<template #time>
<span
v-gl-tooltip
class="gl-truncate-end gl-mr-2 gl-whitespace-nowrap"
data-testid="deployment-timestamp"
:title="displayTime"
>
{{ displayTime }}
</span>
</template>
</gl-sprintf>
</div>
</div>
</div>
</timeline-entry-item>
</template>

View File

@ -55,5 +55,5 @@ export default {
};
</script>
<template>
<ci-icon v-if="status" :status="statusObject" show-status-text />
<ci-icon v-if="status" :status="statusObject" show-status-text class="!gl-border-0" />
</template>

View File

@ -93,6 +93,7 @@ export default {
:aria-label="title"
:items="actionItems"
icon="play"
size="small"
text-sr-only
right
data-container="body"

View File

@ -31,6 +31,7 @@ export default {
:aria-label="$options.i18n.title"
:href="externalUrl"
is-unsafe-link
size="small"
class="external-url"
target="_blank"
icon="external-link"

View File

@ -102,10 +102,7 @@ export default {
};
</script>
<template>
<div
:class="{ 'gl-pb-5': !visible }"
class="gl-border-1 gl-border-default gl-pt-3 gl-border-b-solid"
>
<div :class="{ 'gl-border-b gl-pb-5': !visible }" class="gl-pt-3">
<div class="gl-flex gl-w-full gl-items-center gl-px-3">
<gl-button
class="gl-mr-4"
@ -126,13 +123,12 @@ export default {
v-for="(environment, index) in environments"
:key="environment.name"
:environment="environment"
:class="{ 'gl-mt-5': isFirstEnvironment(index) }"
class="gl-border-1 gl-border-default gl-pt-3 gl-border-t-solid"
:class="{ '!gl-border-t !gl-mt-5': isFirstEnvironment(index) }"
in-folder
/>
<div
v-if="isMessageShowing"
class="gl-border-1 gl-border-default gl-bg-gray-10 gl-py-5 gl-text-center gl-border-t-solid"
class="gl-border-b gl-bg-gray-10 gl-py-3 gl-text-center"
data-testid="environment-folder-message-element"
>
<gl-sprintf :message="$options.i18n.message">

View File

@ -40,7 +40,6 @@ export default {
},
i18n: {
title: s__('Environments|Stop environment'),
stop: s__('Environments|Stop'),
},
data() {
return {
@ -82,11 +81,10 @@ export default {
:loading="isLoading || isEnvironmentStopping"
:title="$options.i18n.title"
:aria-label="$options.i18n.title"
size="small"
icon="stop"
category="secondary"
variant="danger"
@click="onClick"
>
{{ $options.i18n.stop }}
</gl-button>
/>
</template>

View File

@ -87,6 +87,9 @@ export default {
active: __('Active'),
stopped: __('Stopped'),
searchPlaceholder: s__('Environments|Search by environment name'),
name: s__('Environments|Name'),
deployments: s__('Environments|Deployments'),
actions: s__('Environments|Actions'),
},
modalId: 'enable-review-app-info',
stopStaleEnvsModalId: 'stop-stale-environments-modal',
@ -318,17 +321,24 @@ export default {
/></template>
<environments-app-skeleton-loader v-if="loading" />
<template v-else-if="showContent">
<div
v-if="!showEmptyState"
class="gl-border-t gl-border-b gl-hidden lg:gl-flex"
data-testid="environments-table-header"
>
<div class="gl-w-1/5 gl-p-4 gl-font-bold">{{ $options.i18n.name }}</div>
<div class="gl-w-3/5 gl-p-4 gl-font-bold">{{ $options.i18n.deployments }}</div>
<div class="gl-p-4 gl-font-bold">{{ $options.i18n.actions }}</div>
</div>
<environment-folder
v-for="folder in folders"
:key="folder.name"
class="gl-mb-3"
:scope="scope"
:search="search"
:nested-environment="folder" />
<environment-item
v-for="environment in environments"
:key="environment.name"
class="gl-mb-3 gl-border-1 gl-border-default gl-border-b-solid"
:environment="environment.latest"
@change="refetchEnvironments"
/></template>

View File

@ -1,17 +1,14 @@
<script>
import {
GlBadge,
GlButton,
GlCollapse,
GlDisclosureDropdown,
GlLink,
GlSprintf,
GlTooltipDirective as GlTooltip,
} from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { s__ } from '~/locale';
import { truncate } from '~/lib/utils/text_utility';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import isLastDeployment from '../graphql/queries/is_last_deployment.query.graphql';
import ExternalUrl from './environment_external_url.vue';
import Actions from './environment_actions.vue';
@ -26,9 +23,7 @@ import DeployBoardWrapper from './deploy_board_wrapper.vue';
export default {
components: {
GlDisclosureDropdown,
GlCollapse,
GlBadge,
GlButton,
GlLink,
GlSprintf,
Actions,
@ -42,14 +37,11 @@ export default {
TimeAgoTooltip,
Delete,
EnvironmentAlert: () => import('ee_component/environments/components/environment_alert.vue'),
EnvironmentApproval: () =>
import('ee_component/environments/components/environment_approval.vue'),
},
directives: {
GlTooltip,
},
mixins: [glFeatureFlagsMixin()],
inject: ['helpPagePath', 'projectPath'],
inject: ['helpPagePath'],
props: {
environment: {
required: true,
@ -71,38 +63,36 @@ export default {
},
},
i18n: {
collapse: __('Collapse'),
expand: __('Expand'),
emptyState: s__(
'Environments|There are no deployments for this environment yet. %{linkStart}Learn more about setting up deployments.%{linkEnd}',
),
autoStopIn: s__('Environment|Auto stop %{time}'),
tierTooltip: s__('Environment|Deployment tier'),
},
data() {
return { visible: false };
name: s__('Environments|Name'),
deployments: s__('Environments|Deployments'),
actions: s__('Environments|Actions'),
},
computed: {
icon() {
return this.visible ? 'chevron-lg-down' : 'chevron-lg-right';
},
externalUrl() {
return this.environment.externalUrl;
},
name() {
return this.inFolder ? this.environment.nameWithoutType : this.environment.name;
},
label() {
return this.visible ? this.$options.i18n.collapse : this.$options.i18n.expand;
deployments() {
return [this.upcomingDeployment, this.lastDeployment].filter(Boolean);
},
lastDeployment() {
return this.environment?.lastDeployment;
},
upcomingDeployment() {
return this.environment?.upcomingDeployment;
if (!this.environment?.upcomingDeployment) {
return null;
}
return { ...this.environment?.upcomingDeployment, isUpcoming: true };
},
hasDeployment() {
return Boolean(this.environment?.upcomingDeployment || this.environment?.lastDeployment);
return Boolean(this.deployments.length);
},
tier() {
return this.lastDeployment?.tierInYaml;
@ -144,9 +134,6 @@ export default {
return now < autoStopDate;
},
upcomingDeploymentIid() {
return this.environment.upcomingDeployment?.iid.toString() || '';
},
autoStopPath() {
return this.environment?.cancelAutoStopPath ?? '';
},
@ -163,71 +150,73 @@ export default {
return this.environment?.rolloutStatus;
},
},
methods: {
toggleEnvironmentCollapse() {
this.visible = !this.visible;
},
},
deploymentClasses: [
'gl-border-default',
'gl-border-t-solid',
'gl-border-1',
'gl-py-5',
'md:gl-pl-7',
'gl-bg-gray-10',
],
deployBoardClasses: [
'gl-border-default',
'gl-border-t-solid',
'gl-border-1',
'gl-py-4',
'md:gl-pl-7',
'gl-bg-gray-10',
],
};
</script>
<template>
<div>
<div class="gl-flex gl-items-center gl-justify-between gl-px-3 gl-pb-5 gl-pt-3">
<div :class="{ 'gl-ml-7': inFolder }" class="gl-mr-4 gl-flex gl-min-w-0 gl-items-center">
<gl-button
class="gl-mr-4 gl-min-w-fit"
:icon="icon"
:aria-label="label"
size="small"
category="secondary"
@click="toggleEnvironmentCollapse"
/>
<gl-link
v-gl-tooltip
:href="environment.environmentPath"
class="gl-truncate"
:class="{ 'gl-font-bold': visible }"
:title="name"
>
{{ displayName }}
</gl-link>
<gl-badge
v-if="tier"
v-gl-tooltip
:title="$options.i18n.tierTooltip"
class="gl-ml-3 gl-font-monospace"
>{{ tier }}</gl-badge
>
<div
class="gl-border gl-mt-4 gl-flex gl-flex-col lg:gl-mt-0 lg:gl-flex-row lg:gl-border-x-0 lg:gl-border-t-0"
>
<div
class="gl-border-b gl-flex gl-shrink-0 gl-items-baseline gl-p-4 lg:gl-w-1/5 lg:gl-border-b-0"
>
<strong class="gl-block gl-w-1/3 gl-flex-shrink-0 gl-pr-4 md:gl-w-1/4 lg:gl-hidden">{{
$options.i18n.name
}}</strong>
<gl-link v-gl-tooltip :href="environment.environmentPath" class="gl-truncate" :title="name">
{{ displayName }}
</gl-link>
<gl-badge
v-if="tier"
v-gl-tooltip
:title="$options.i18n.tierTooltip"
class="gl-ml-3 gl-font-monospace"
>{{ tier }}</gl-badge
>
</div>
<div
class="issuable-discussion gl-border-b gl-flex gl-shrink-0 gl-flex-wrap gl-py-4 lg:gl-w-3/5 lg:gl-flex-col lg:gl-border-b-0"
>
<template v-if="hasDeployment">
<strong class="gl-block gl-w-1/3 gl-flex-shrink-0 gl-px-4 md:gl-w-1/4 lg:gl-hidden">{{
$options.i18n.deployments
}}</strong>
<ul class="main-notes-list timeline gl-relative -gl-ml-4 gl-w-2/3 lg:gl-w-full">
<deployment
v-for="deployment of deployments"
:key="deployment.id"
:data-testid="
deployment.isUpcoming ? 'upcoming-deployment-content' : 'latest-deployment-content'
"
:deployment="deployment"
:latest="deployment.isLast"
class="[&:nth-child(2)]:gl-mt-4"
/>
</ul>
</template>
<div v-else class="gl-px-4 gl-align-middle" data-testid="deployments-empty-state">
<gl-sprintf :message="$options.i18n.emptyState">
<template #link="{ content }">
<gl-link :href="helpPagePath">{{ content }}</gl-link>
</template>
</gl-sprintf>
</div>
<div class="gl-flex gl-items-center">
<p
v-if="canShowAutoStopDate"
class="gl-mb-0 gl-mr-5 gl-text-sm gl-text-subtle"
data-testid="auto-stop-time"
>
<gl-sprintf :message="$options.i18n.autoStopIn">
<template #time>
<time-ago-tooltip :time="environment.autoStopAt" css-class="gl-font-bold" />
</template>
</gl-sprintf>
</p>
<div class="btn-group table-action-buttons" role="group">
<div v-if="rolloutStatus" class="gl-w-full">
<deploy-board-wrapper
:rollout-status="rolloutStatus"
:environment="environment"
class="gl-mt-4 gl-pl-2"
/>
</div>
<div v-if="hasOpenedAlert" class="gl-w-full">
<environment-alert :environment="environment" class="gl-mt-4 gl-pl-3 gl-pt-3" />
</div>
</div>
<div class="gl-flex gl-flex-grow gl-items-baseline gl-p-4">
<strong class="gl-block gl-w-1/3 gl-flex-shrink-0 gl-pr-4 md:gl-w-1/4 lg:gl-hidden">{{
$options.i18n.actions
}}</strong>
<div class="gl-ml-auto">
<div class="btn-group" role="group">
<external-url
v-if="externalUrl"
:external-url="externalUrl"
@ -258,6 +247,7 @@ export default {
icon="ellipsis_v"
category="secondary"
placement="bottom-end"
size="small"
:toggle-text="__('More actions')"
>
<rollback
@ -294,57 +284,18 @@ export default {
/>
</gl-disclosure-dropdown>
</div>
<p
v-if="canShowAutoStopDate"
class="gl-mb-0 gl-mt-3 gl-text-sm gl-text-subtle"
data-testid="auto-stop-time"
>
<gl-sprintf :message="$options.i18n.autoStopIn">
<template #time>
<time-ago-tooltip :time="environment.autoStopAt" css-class="gl-font-bold" />
</template>
</gl-sprintf>
</p>
</div>
</div>
<gl-collapse :visible="visible">
<template v-if="hasDeployment">
<div v-if="lastDeployment" :class="$options.deploymentClasses">
<deployment
:deployment="lastDeployment"
:visible="visible"
:class="{ 'gl-ml-7': inFolder }"
latest
class="gl-pl-4"
/>
</div>
<div
v-if="upcomingDeployment"
:class="$options.deploymentClasses"
data-testid="upcoming-deployment-content"
>
<deployment
:deployment="upcomingDeployment"
:visible="visible"
:class="{ 'gl-ml-7': inFolder }"
class="gl-pl-4"
>
<template #approval>
<environment-approval
:required-approval-count="environment.requiredApprovalCount"
:deployment-web-path="upcomingDeployment.webPath"
/>
</template>
</deployment>
</div>
</template>
<div v-else :class="$options.deploymentClasses">
<gl-sprintf :message="$options.i18n.emptyState">
<template #link="{ content }">
<gl-link :href="helpPagePath">{{ content }}</gl-link>
</template>
</gl-sprintf>
</div>
<div v-if="rolloutStatus" :class="$options.deployBoardClasses">
<deploy-board-wrapper
:rollout-status="rolloutStatus"
:environment="environment"
:class="{ 'gl-ml-7': inFolder }"
class="gl-pl-4"
/>
</div>
<div v-if="hasOpenedAlert" class="gl-bg-gray-10 md:gl-px-7">
<environment-alert :environment="environment" class="gl-py-5 gl-pl-4" />
</div>
</gl-collapse>
</div>
</template>

View File

@ -6,6 +6,8 @@ module Types
graphql_name 'PackageBase'
description 'Represents a package in the Package Registry'
PROTECTION_RULE_EXISTS_BATCH_SIZE = 20
connection_type_class Types::CountableConnectionType
authorize :read_package
@ -39,12 +41,14 @@ module Types
def protection_rule_exists
object_package_type_value = ::Packages::Package.package_types[object.package_type]
BatchLoader::GraphQL.for([object.name, object_package_type_value]).batch do |inputs, loader|
::Packages::Protection::Rule
.for_push_exists_for_multiple_packages(
package_names: inputs.map(&:first), package_types: inputs.map(&:last), project_id: object.project_id
)
.each { |row| loader.call([row['package_name'], row['package_type']], row['protected']) }
BatchLoader::GraphQL.for([object.project_id, object.name, object_package_type_value]).batch do |tuples, loader|
tuples.each_slice(PROTECTION_RULE_EXISTS_BATCH_SIZE) do |projects_and_packages|
::Packages::Protection::Rule
.for_push_exists_for_projects_and_packages(projects_and_packages)
.each do |row|
loader.call([row['project_id'], row['package_name'], row['package_type']], row['protected'])
end
end
end
end

View File

@ -18,6 +18,30 @@ class ApplicationSetting < ApplicationRecord
encrypted_vertex_ai_access_token_iv
], remove_with: '17.5', remove_after: '2024-09-19'
ignore_columns %i[
elasticsearch_aws
elasticsearch_search
elasticsearch_indexing
elasticsearch_username
elasticsearch_aws_region
elasticsearch_aws_access_key
elasticsearch_limit_indexing
elasticsearch_pause_indexing
elasticsearch_requeue_workers
elasticsearch_max_bulk_size_mb
elasticsearch_retry_on_failure
elasticsearch_max_bulk_concurrency
elasticsearch_client_request_timeout
elasticsearch_worker_number_of_shards
elasticsearch_analyzers_smartcn_search
elasticsearch_analyzers_kuromoji_search
elasticsearch_analyzers_smartcn_enabled
elasticsearch_analyzers_kuromoji_enabled
elasticsearch_indexed_field_length_limit
elasticsearch_indexed_file_size_limit_kb
elasticsearch_max_code_indexing_concurrency
], remove_with: '17.11', remove_after: '2025-04-17'
INSTANCE_REVIEW_MIN_USERS = 50
GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \
'Admin area > Settings > Metrics and profiling > Metrics - Grafana'

View File

@ -47,32 +47,65 @@ module Packages
.exists?
end
def self.for_push_exists_for_multiple_packages(package_names:, package_types:, project_id:)
return none if package_names.blank? || package_types.blank? || project_id.blank?
return none if package_names.size != package_types.size
##
# Accepts a list of projects and packages and returns a result set
# indicating whether the package name is protected.
#
# @param [Array<Array>] projects_and_packages an array of arrays where each sub-array contains
# the project id (bigint), the package name (string) and the package type (smallint).
# @return [ActiveRecord::Result] a result set indicating whether each project, package name and package type
# is protected.
#
# Example:
# Packages::Protection::Rule.for_push_exists_for_projects_and_packages([
# [1, '@my_group/my_project_1/package_1', 2],
# [1, '@my_group/my_project_1/package_2', 2],
# [2, '@my_group/my_project_2/package_1', 3],
# ...
# ])
#
# [
# {'project_id' => 1, 'package_name' => '@my_group/my_project_1/package_1', 'package_type' => 2,
# 'protected' => true},
# {'project_id' => 1, 'package_name' => '@my_group/my_project_1/package_2', 'package_type' => 2,
# 'protected' => false},
# {'project_id' => 2, 'package_name' => '@my_group/my_project_2/package_1', 'package_type' => 3,
# 'protected' => true},
# ...
# ]
#
def self.for_push_exists_for_projects_and_packages(projects_and_packages)
return none if projects_and_packages.blank?
project_ids, package_names, package_types = projects_and_packages.transpose
cte_query_sql = <<~SQL
unnest(
ARRAY[:project_ids]::bigint[],
ARRAY[:package_names]::text[],
ARRAY[:package_types]::smallint[]
) AS projects_and_packages(project_id, package_name, package_type)
SQL
cte_query =
select('*').from(
sanitize_sql_array(
[
"unnest(ARRAY[:package_names], ARRAY[:package_types]) AS x(package_name, package_type)",
{ package_names: package_names, package_types: package_types }
]
)
)
select('*').from(sanitize_sql_array(
[cte_query_sql, { project_ids: project_ids, package_names: package_names, package_types: package_types }]
))
cte_name = :package_names_and_types_cte
cte_name = :projects_and_packages_cte
cte = Gitlab::SQL::CTE.new(cte_name, cte_query)
rules_cte_package_type = "#{cte_name}.#{connection.quote_column_name('package_type')}"
rules_cte_project_id = "#{cte_name}.#{connection.quote_column_name('project_id')}"
rules_cte_package_name = "#{cte_name}.#{connection.quote_column_name('package_name')}"
rules_cte_package_type = "#{cte_name}.#{connection.quote_column_name('package_type')}"
protection_rule_exsits_subquery = select(1)
.where(project_id: project_id)
.where("#{rules_cte_project_id} = project_id")
.where(arel_table[:package_type].eq(Arel.sql(rules_cte_package_type)))
.where("#{rules_cte_package_name} ILIKE #{::Gitlab::SQL::Glob.to_like('package_name_pattern')}")
query = select(
rules_cte_project_id,
rules_cte_package_type,
rules_cte_package_name,
sanitize_sql_array(['EXISTS(?) AS protected', protection_rule_exsits_subquery])

View File

@ -7,9 +7,19 @@ module VirtualRegistries
include FileStoreMounter
include Gitlab::SQL::Pattern
include ::UpdateNamespaceStatistics
include ShaAttribute
self.table_name = 'virtual_registries_packages_maven_cache_entries'
# we're using a composite primary key: upstream_id, relative_path and status
self.primary_key = :upstream_id
query_constraints :upstream_id, :relative_path, :status
belongs_to :group
belongs_to :upstream, class_name: 'VirtualRegistries::Packages::Maven::Upstream', inverse_of: :cached_responses
belongs_to :upstream,
class_name: 'VirtualRegistries::Packages::Maven::Upstream',
inverse_of: :cached_responses,
optional: false
alias_attribute :namespace, :group
@ -20,21 +30,22 @@ module VirtualRegistries
ignore_column :downloaded_at, remove_with: '17.9', remove_after: '2025-01-23'
sha_attribute :file_sha1
sha_attribute :file_md5
validates :group, top_level_group: true, presence: true
validates :relative_path,
:object_storage_key,
:size,
:file_sha1,
presence: true
validates :relative_path,
:object_storage_key,
:upstream_etag,
:content_type,
length: { maximum: 255 }
validates :file_final_path, length: { maximum: 1024 }
validates :upstream_etag, :content_type, length: { maximum: 255 }
validates :relative_path, :object_storage_key, :file_final_path, length: { maximum: 1024 }
validates :file_md5, length: { is: 32 }, allow_nil: true
validates :file_sha1, length: { is: 40 }
validates :relative_path,
uniqueness: { scope: [:upstream_id, :status] },
if: -> { upstream.present? && default? }
if: :default?
validates :file, presence: true
mount_file_store_uploader ::VirtualRegistries::CachedResponseUploader
@ -47,6 +58,7 @@ module VirtualRegistries
fuzzy_search(query, [:relative_path], use_minimum_char_limit: false)
end
scope :for_group, ->(group) { where(group: group) }
scope :order_created_desc, -> { reorder(arel_table['created_at'].desc) }
def self.next_pending_destruction
pending_destruction.lock('FOR UPDATE SKIP LOCKED').take
@ -85,6 +97,13 @@ module VirtualRegistries
(upstream_checked_at + upstream.cache_validity_hours.hours).past?
end
def mark_as_pending_destruction
update_columns(
status: :pending_destruction,
relative_path: "#{relative_path}/deleted/#{SecureRandom.uuid}"
)
end
private
def set_object_storage_key

View File

@ -55,6 +55,10 @@ module VirtualRegistries
{ Authorization: authorization }
end
def default_cached_responses
cached_responses.default
end
private
def reset_credentials

View File

@ -60,7 +60,7 @@ module VirtualRegistries
def cached_response
# TODO change this to support multiple upstreams
# https://gitlab.com/gitlab-org/gitlab/-/issues/480461
registry.upstream.cached_responses.default.find_by_relative_path(relative_path)
registry.upstream.default_cached_responses.find_by_relative_path(relative_path)
end
strong_memoize_attr :cached_response

View File

@ -34,7 +34,10 @@
- if merge_request_dashboard_enabled?(current_user)
= gl_tabs_nav do
= gl_tab_link_to new_lists_enabled ? _('Active') : _('Needs attention'), merge_requests_dashboard_path
= gl_tab_link_to new_lists_enabled ? _('Merged') : _('Following'), merge_requests_following_dashboard_path
- if new_lists_enabled
= gl_tab_link_to _('Merged'), merge_requests_merged_dashboard_path
- else
= gl_tab_link_to _('Following'), merge_requests_following_dashboard_path
= gl_tab_link_to _('Search'), merge_requests_search_dashboard_path, item_active: true
%div{ class: "#{'gl-bg-gray-10' if merge_request_dashboard_enabled?(current_user)}" }

View File

@ -3,8 +3,7 @@
class AutoMergeProcessWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
data_consistency :sticky, feature_flag: :auto_merge_process_worker_sticky
data_consistency :sticky
sidekiq_options retry: 3
# Avoid _simultaneous execution_ of this job for the same MR,

View File

@ -1,9 +0,0 @@
---
name: auto_merge_process_worker_sticky
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/483008
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167234
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/496567
milestone: '17.5'
group: group::ci platform
type: worker
default_enabled: false

View File

@ -588,6 +588,17 @@ user_details:
- table: namespaces
column: enterprise_group_id
on_delete: async_nullify
virtual_registries_packages_maven_cache_entries:
- table: virtual_registries_packages_maven_upstreams
column: upstream_id
on_delete: update_column_to
target_column: status
target_value: 2
- table: namespaces
column: group_id
on_delete: update_column_to
target_column: status
target_value: 2
virtual_registries_packages_maven_cached_responses:
- table: virtual_registries_packages_maven_upstreams
column: upstream_id

View File

@ -0,0 +1,14 @@
---
table_name: virtual_registries_packages_maven_cache_entries
classes:
- VirtualRegistries::Packages::Maven::CachedResponse
feature_categories:
- virtual_registry
description: Cache entry for the Maven virtual packages registry. Mainly the body
of a response of a Maven upstream. Contains references to an object storage file.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/174985
milestone: '17.8'
gitlab_schema: gitlab_main_cell
sharding_key:
group_id: namespaces
table_size: small

View File

@ -0,0 +1,55 @@
# frozen_string_literal: true
class CreateVirtualRegistriesPackagesMavenCacheEntries < Gitlab::Database::Migration[2.2]
include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
milestone '17.8'
TABLE_NAME = :virtual_registries_packages_maven_cache_entries
def up
create_table TABLE_NAME, if_not_exists: true, options: 'PARTITION BY HASH (relative_path)',
primary_key: [:upstream_id, :relative_path, :status] do |t|
t.bigint :group_id, null: false
t.bigint :upstream_id, null: false
t.datetime_with_timezone :upstream_checked_at, null: false, default: -> { 'NOW()' }
t.timestamps_with_timezone null: false
t.integer :file_store, null: false, default: 1
t.integer :size, null: false
t.integer :status, null: false, default: 0, limit: 2
t.text :relative_path, null: false, limit: 1024
t.text :file, null: false, limit: 1024
t.text :object_storage_key, null: false, limit: 1024
t.text :upstream_etag, limit: 255
t.text :content_type, limit: 255, null: false, default: 'application/octet-stream'
t.text :file_final_path, limit: 1024
t.binary :file_md5
t.binary :file_sha1, null: false
# for text search on relative path
t.index :relative_path,
using: :gin,
opclass: :gin_trgm_ops,
name: :idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram
# index on sharding key
t.index %i[group_id status], name: :idx_vreg_pkgs_maven_cache_entries_on_group_id_status
# for cleanup jobs
t.index [:upstream_id, :relative_path],
name: :idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath,
where: 'status = 2' # status: :pending_destruction
# for ordered pagination
t.index [:upstream_id, :created_at],
name: :idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at,
where: 'status = 0' # status: :default
end
create_hash_partitions(TABLE_NAME, 16)
end
def down
drop_table TABLE_NAME
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
class AddCheckConstraintsToVirtualRegistriesPackagesMavenCacheEntries < Gitlab::Database::Migration[2.2]
milestone '17.8'
disable_ddl_transaction!
TABLE_NAME = :virtual_registries_packages_maven_cache_entries
def up
constraint = check_constraint_name(TABLE_NAME.to_s, 'file_md5', 'max_length')
add_check_constraint(TABLE_NAME, 'file_md5 IS NULL OR octet_length(file_md5) = 16', constraint)
constraint = check_constraint_name(TABLE_NAME.to_s, 'file_sha1', 'max_length')
add_check_constraint(TABLE_NAME, 'octet_length(file_sha1) = 20', constraint)
end
def down
constraint = check_constraint_name(TABLE_NAME.to_s, 'file_md5', 'max_length')
remove_check_constraint(TABLE_NAME, constraint)
constraint = check_constraint_name(TABLE_NAME.to_s, 'file_sha1', 'max_length')
remove_check_constraint(TABLE_NAME, constraint)
end
end

View File

@ -0,0 +1 @@
c113f28c36ba46cc7d7abd6b2d309e6e7e07393a0e6287c80ba06774bbf64ead

View File

@ -0,0 +1 @@
e4465f563d1f69938fe522d71db09d17ea79cf8ff3c964ceaeb5108455d81e52

View File

@ -5933,6 +5933,466 @@ CREATE TABLE gitlab_partitions_static.namespace_descendants_31 (
calculated_at timestamp with time zone
);
CREATE TABLE virtual_registries_packages_maven_cache_entries (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
)
PARTITION BY HASH (relative_path);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15 (
group_id bigint NOT NULL,
upstream_id bigint NOT NULL,
upstream_checked_at timestamp with time zone DEFAULT now() NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
file_store integer DEFAULT 1 NOT NULL,
size integer NOT NULL,
status smallint DEFAULT 0 NOT NULL,
relative_path text NOT NULL,
file text NOT NULL,
object_storage_key text NOT NULL,
upstream_etag text,
content_type text DEFAULT 'application/octet-stream'::text NOT NULL,
file_final_path text,
file_md5 bytea,
file_sha1 bytea NOT NULL,
CONSTRAINT check_215f531366 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_2a52b4e0fc CHECK ((char_length(file) <= 1024)),
CONSTRAINT check_36391449ea CHECK ((char_length(object_storage_key) <= 1024)),
CONSTRAINT check_45d3174f8a CHECK ((char_length(relative_path) <= 1024)),
CONSTRAINT check_c9d6e475d9 CHECK ((char_length(file_final_path) <= 1024)),
CONSTRAINT check_cc222855d6 CHECK (((file_md5 IS NULL) OR (octet_length(file_md5) = 16))),
CONSTRAINT check_f2ea43b900 CHECK ((octet_length(file_sha1) = 20)),
CONSTRAINT check_fd9fc90696 CHECK ((char_length(upstream_etag) <= 255))
);
CREATE TABLE abuse_events (
id bigint NOT NULL,
user_id bigint,
@ -23460,6 +23920,38 @@ ALTER TABLE ONLY namespace_descendants ATTACH PARTITION gitlab_partitions_static
ALTER TABLE ONLY namespace_descendants ATTACH PARTITION gitlab_partitions_static.namespace_descendants_31 FOR VALUES WITH (modulus 32, remainder 31);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00 FOR VALUES WITH (modulus 16, remainder 0);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01 FOR VALUES WITH (modulus 16, remainder 1);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02 FOR VALUES WITH (modulus 16, remainder 2);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03 FOR VALUES WITH (modulus 16, remainder 3);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04 FOR VALUES WITH (modulus 16, remainder 4);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05 FOR VALUES WITH (modulus 16, remainder 5);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06 FOR VALUES WITH (modulus 16, remainder 6);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07 FOR VALUES WITH (modulus 16, remainder 7);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08 FOR VALUES WITH (modulus 16, remainder 8);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09 FOR VALUES WITH (modulus 16, remainder 9);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10 FOR VALUES WITH (modulus 16, remainder 10);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11 FOR VALUES WITH (modulus 16, remainder 11);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12 FOR VALUES WITH (modulus 16, remainder 12);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13 FOR VALUES WITH (modulus 16, remainder 13);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14 FOR VALUES WITH (modulus 16, remainder 14);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15 FOR VALUES WITH (modulus 16, remainder 15);
ALTER TABLE ONLY p_ci_builds ATTACH PARTITION ci_builds FOR VALUES IN ('100');
ALTER TABLE ONLY p_ci_builds_metadata ATTACH PARTITION ci_builds_metadata FOR VALUES IN ('100');
@ -25314,6 +25806,57 @@ ALTER TABLE ONLY gitlab_partitions_static.namespace_descendants_30
ALTER TABLE ONLY gitlab_partitions_static.namespace_descendants_31
ADD CONSTRAINT namespace_descendants_31_pkey PRIMARY KEY (namespace_id);
ALTER TABLE ONLY virtual_registries_packages_maven_cache_entries
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_00_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_01_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_02_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_03_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_04_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_05_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_06_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_07_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_08_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_09_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_10_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_11_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_12_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_13_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_14_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15
ADD CONSTRAINT virtual_registries_packages_maven_cache_entries_15_pkey PRIMARY KEY (upstream_id, relative_path, status);
ALTER TABLE ONLY abuse_events
ADD CONSTRAINT abuse_events_pkey PRIMARY KEY (id);
@ -28887,6 +29430,142 @@ CREATE INDEX namespace_descendants_30_namespace_id_idx ON gitlab_partitions_stat
CREATE INDEX namespace_descendants_31_namespace_id_idx ON gitlab_partitions_static.namespace_descendants_31 USING btree (namespace_id) WHERE (outdated_at IS NOT NULL);
CREATE INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ON ONLY virtual_registries_packages_maven_cache_entries USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mav_upstream_id_relative_path_idx10 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mav_upstream_id_relative_path_idx11 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mav_upstream_id_relative_path_idx12 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mav_upstream_id_relative_path_idx13 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mav_upstream_id_relative_path_idx14 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mav_upstream_id_relative_path_idx15 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mave_upstream_id_relative_path_idx1 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mave_upstream_id_relative_path_idx2 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mave_upstream_id_relative_path_idx3 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mave_upstream_id_relative_path_idx4 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mave_upstream_id_relative_path_idx5 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mave_upstream_id_relative_path_idx6 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mave_upstream_id_relative_path_idx7 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mave_upstream_id_relative_path_idx8 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX virtual_registries_packages_mave_upstream_id_relative_path_idx9 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ON ONLY virtual_registries_packages_maven_cache_entries USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven__upstream_id_created_at_idx10 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven__upstream_id_created_at_idx11 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven__upstream_id_created_at_idx12 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven__upstream_id_created_at_idx13 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven__upstream_id_created_at_idx14 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven__upstream_id_created_at_idx15 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_c_upstream_id_created_at_idx1 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_c_upstream_id_created_at_idx2 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_c_upstream_id_created_at_idx3 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_c_upstream_id_created_at_idx4 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_c_upstream_id_created_at_idx5 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_c_upstream_id_created_at_idx6 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_c_upstream_id_created_at_idx7 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_c_upstream_id_created_at_idx8 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_c_upstream_id_created_at_idx9 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX virtual_registries_packages_maven_ca_upstream_id_created_at_idx ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00 USING btree (upstream_id, created_at) WHERE (status = 0);
CREATE INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ON ONLY virtual_registries_packages_maven_cache_entries USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_e_group_id_status_idx10 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_e_group_id_status_idx11 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_e_group_id_status_idx12 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_e_group_id_status_idx13 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_e_group_id_status_idx14 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_e_group_id_status_idx15 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_en_group_id_status_idx1 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_en_group_id_status_idx2 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_en_group_id_status_idx3 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_en_group_id_status_idx4 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_en_group_id_status_idx5 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_en_group_id_status_idx6 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_en_group_id_status_idx7 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_en_group_id_status_idx8 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_en_group_id_status_idx9 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09 USING btree (group_id, status);
CREATE INDEX virtual_registries_packages_maven_cache_ent_group_id_status_idx ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00 USING btree (group_id, status);
CREATE INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ON ONLY virtual_registries_packages_maven_cache_entries USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_ent_relative_path_idx10 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_ent_relative_path_idx11 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_ent_relative_path_idx12 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_ent_relative_path_idx13 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_ent_relative_path_idx14 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_ent_relative_path_idx15 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entr_relative_path_idx1 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entr_relative_path_idx2 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entr_relative_path_idx3 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entr_relative_path_idx4 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entr_relative_path_idx5 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entr_relative_path_idx6 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entr_relative_path_idx7 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entr_relative_path_idx8 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entr_relative_path_idx9 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_cache_entri_relative_path_idx ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00 USING gin (relative_path gin_trgm_ops);
CREATE INDEX virtual_registries_packages_maven_upstream_id_relative_path_idx ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00 USING btree (upstream_id, relative_path) WHERE (status = 2);
CREATE INDEX analytics_index_audit_events_part_on_created_at_and_author_id ON ONLY audit_events USING btree (created_at, author_id);
CREATE INDEX analytics_index_events_on_created_at_and_author_id ON events USING btree (created_at, author_id);
@ -35637,6 +36316,166 @@ ALTER INDEX index_on_namespace_descendants_outdated ATTACH PARTITION gitlab_part
ALTER INDEX namespace_descendants_pkey ATTACH PARTITION gitlab_partitions_static.namespace_descendants_31_pkey;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mav_upstream_id_relative_path_idx10;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mav_upstream_id_relative_path_idx11;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mav_upstream_id_relative_path_idx12;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mav_upstream_id_relative_path_idx13;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mav_upstream_id_relative_path_idx14;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mav_upstream_id_relative_path_idx15;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mave_upstream_id_relative_path_idx1;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mave_upstream_id_relative_path_idx2;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mave_upstream_id_relative_path_idx3;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mave_upstream_id_relative_path_idx4;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mave_upstream_id_relative_path_idx5;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mave_upstream_id_relative_path_idx6;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mave_upstream_id_relative_path_idx7;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mave_upstream_id_relative_path_idx8;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_mave_upstream_id_relative_path_idx9;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven__upstream_id_created_at_idx10;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven__upstream_id_created_at_idx11;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven__upstream_id_created_at_idx12;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven__upstream_id_created_at_idx13;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven__upstream_id_created_at_idx14;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven__upstream_id_created_at_idx15;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_c_upstream_id_created_at_idx1;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_c_upstream_id_created_at_idx2;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_c_upstream_id_created_at_idx3;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_c_upstream_id_created_at_idx4;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_c_upstream_id_created_at_idx5;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_c_upstream_id_created_at_idx6;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_c_upstream_id_created_at_idx7;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_c_upstream_id_created_at_idx8;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_c_upstream_id_created_at_idx9;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_created_at ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_ca_upstream_id_created_at_idx;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_e_group_id_status_idx10;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_e_group_id_status_idx11;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_e_group_id_status_idx12;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_e_group_id_status_idx13;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_e_group_id_status_idx14;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_e_group_id_status_idx15;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_en_group_id_status_idx1;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_en_group_id_status_idx2;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_en_group_id_status_idx3;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_en_group_id_status_idx4;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_en_group_id_status_idx5;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_en_group_id_status_idx6;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_en_group_id_status_idx7;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_en_group_id_status_idx8;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_en_group_id_status_idx9;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_group_id_status ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_ent_group_id_status_idx;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_ent_relative_path_idx10;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_ent_relative_path_idx11;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_ent_relative_path_idx12;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_ent_relative_path_idx13;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_ent_relative_path_idx14;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_ent_relative_path_idx15;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entr_relative_path_idx1;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entr_relative_path_idx2;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entr_relative_path_idx3;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entr_relative_path_idx4;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entr_relative_path_idx5;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entr_relative_path_idx6;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entr_relative_path_idx7;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entr_relative_path_idx8;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entr_relative_path_idx9;
ALTER INDEX idx_vreg_pkgs_maven_cache_entries_on_relative_path_trigram ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entri_relative_path_idx;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14_pkey;
ALTER INDEX virtual_registries_packages_maven_cache_entries_pkey ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15_pkey;
ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_pending_upt_id_relpath ATTACH PARTITION gitlab_partitions_static.virtual_registries_packages_maven_upstream_id_relative_path_idx;
ALTER INDEX p_ci_builds_status_created_at_project_id_idx ATTACH PARTITION ci_builds_gitlab_monitor_metrics;
ALTER INDEX p_ci_builds_metadata_pkey ATTACH PARTITION ci_builds_metadata_pkey;

View File

@ -14,6 +14,10 @@ DETAILS:
Use this API to retrieve details about arbitrary tokens and to revoke them. Unlike other APIs that expose token information, this API allows you to retrieve details or revoke tokens without knowing the specific type of token.
## Token Prefixes
When making a request, tokens must begin with `glpat` or the current [custom prefix](../../administration/settings/account_and_limit_settings.md#personal-access-token-prefix). If the token begins with a previous custom prefix, the operation will fail. Interest in support for previous custom prefixes is tracked in [issue 165663](https://gitlab.com/gitlab-org/gitlab/-/issues/165663).
Prerequisites:
- You must have administrator access to the instance.
@ -47,7 +51,7 @@ Supported attributes:
| Attribute | Type | Required | Description |
|--------------|---------|----------|----------------------------|
| `token` | string | Yes | Existing token to identify |
| `token` | string | Yes | Existing token to identify. Must begin with `glpat` or the current [custom prefix](../../administration/settings/account_and_limit_settings.md#personal-access-token-prefix). |
If successful, returns [`200`](../rest/troubleshooting.md#status-codes) and information about the token.
@ -119,7 +123,7 @@ Supported attributes:
| Attribute | Type | Required | Description |
|--------------|---------|----------|--------------------------|
| `token` | string | Yes | Existing token to revoke |
| `token` | string | Yes | Existing token to revoke. Must begin with `glpat` or the current [custom prefix](../../administration/settings/account_and_limit_settings.md#personal-access-token-prefix). |
If successful, returns [`204`](../rest/troubleshooting.md#status-codes) without content.

View File

@ -696,6 +696,8 @@ The commit status API for use with GitLab.
### List the statuses of a commit
> - `pipeline_id`, `order_by`, and `sort` fields [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176142) in GitLab 17.9.
List the statuses of a commit in a project.
The pagination parameters `page` and `per_page` can be used to restrict the list of references.
@ -703,14 +705,17 @@ The pagination parameters `page` and `per_page` can be used to restrict the list
GET /projects/:id/repository/commits/:sha/statuses
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-paths) |
| `sha` | string | yes | The commit SHA |
| `ref` | string | no | The name of a repository branch or tag or, if not given, the default branch |
| `stage` | string | no | Filter by [build stage](../ci/yaml/index.md#stages), for example, `test` |
| `name` | string | no | Filter by [job name](../ci/yaml/index.md#job-keywords), for example, `bundler:audit` |
| `all` | boolean | no | Return all statuses, not only the latest ones |
| Attribute | Type | Required | Description |
|---------------|----------------| -------- |--------------------------------------------------------------------------------------|
| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-paths). |
| `sha` | string | Yes | Hash of the commit. |
| `ref` | string | No | Name of the branch or tag. Default is the default branch. |
| `stage` | string | No | Filter statuses by [build stage](../ci/yaml/index.md#stages). For example, `test`. |
| `name` | string | No | Filter statuses by [job name](../ci/yaml/index.md#job-keywords). For example, `bundler:audit`. |
| `pipeline_id` | integer | No | Filter statuses by pipeline ID. For example, `1234`. |
| `order_by` | string | No | Values for sorting statuses. Valid values are `id` and `pipeline_id`. Default is `id`. |
| `sort` | string | No | Sort statuses in ascending or descending order. Valid values are `asc` and `desc`. Default is `asc`. |
| `all` | boolean | No | Include all statuses instead of latest only. Default is `false`. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \

View File

@ -42,7 +42,7 @@ Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/7/registry/protection/rules"
--url "https://gitlab.example.com/api/v4/projects/7/registry/protection/repository/rules"
```
Example response:

View File

@ -22670,6 +22670,7 @@ Represents a product analytics dashboard.
| <a id="customizabledashboardconfigurationproject"></a>`configurationProject` | [`Project`](#project) | Project which contains the dashboard definition. |
| <a id="customizabledashboarddescription"></a>`description` | [`String`](#string) | Description of the dashboard. |
| <a id="customizabledashboarderrors"></a>`errors` | [`[String!]`](#string) | Errors on yaml definition. |
| <a id="customizabledashboardfilters"></a>`filters` | [`JSON`](#json) | Dashboard global filters. |
| <a id="customizabledashboardpanels"></a>`panels` | [`CustomizableDashboardPanelConnection`](#customizabledashboardpanelconnection) | Panels shown on the dashboard. (see [Connections](#connections)) |
| <a id="customizabledashboardslug"></a>`slug` | [`String!`](#string) | Slug of the dashboard. |
| <a id="customizabledashboardstatus"></a>`status` **{warning-solid}** | [`String`](#string) | **Introduced** in GitLab 17.0. **Status**: Experiment. Status of the dashboard. |

View File

@ -96,7 +96,7 @@ Predefined variables become available at three different phases of pipeline exec
| `CI_OPEN_MERGE_REQUESTS` | Pre-pipeline | A comma-separated list of up to four merge requests that use the current branch and project as the merge request source. Only available in branch and merge request pipelines if the branch has an associated merge request. For example, `gitlab-org/gitlab!333,gitlab-org/gitlab-foss!11`. |
| `CI_PAGES_DOMAIN` | Pre-pipeline | The instance's domain that hosts GitLab Pages, not including the namespace subdomain. To use the full hostname, use `CI_PAGES_HOSTNAME` instead. |
| `CI_PAGES_HOSTNAME` | Job-only | The full hostname of the Pages deployment. |
| `CI_PAGES_URL` | Job-only | The URL for a GitLab Pages site. Always a subdomain of `CI_PAGES_DOMAIN`. |
| `CI_PAGES_URL` | Job-only | The URL for a GitLab Pages site. Always a subdomain of `CI_PAGES_DOMAIN`. In GitLab 17.8 and later, when the [feature flag](../../administration/feature_flags.md) named `fix_pages_ci_variables` is enabled, the value includes the `path_prefix` when one is specified. Enabled on GitLab.com in GitLab 17.8. |
| `CI_PIPELINE_ID` | Job-only | The instance-level ID of the current pipeline. This ID is unique across all projects on the GitLab instance. |
| `CI_PIPELINE_IID` | Pipeline | The project-level IID (internal ID) of the current pipeline. This ID is unique only in the current project. |
| `CI_PIPELINE_SOURCE` | Pre-pipeline | How the pipeline was triggered. The value can be one of the [pipeline sources](../jobs/job_rules.md#ci_pipeline_source-predefined-variable). |

View File

@ -48,7 +48,7 @@ an integrated GitLab workflow experience.
- **Code generation**: Generates code based on a natural language code comment block.
Write a comment, then press <kbd>Enter</kbd> to generate code based on the context of your
comment, and the rest of your code.
- **Context-aware suggestions**: Uses currently open files, content before and after the cursor,
- **Context-aware suggestions**: Uses open files in your IDE, content before and after the cursor,
filename, and extension type to provide relevant suggestions.
- **Support for multiple languages**: Works with various programming languages supported by your development environment.

View File

@ -46,10 +46,14 @@ To enable GitLab Language Server debug logs:
The debug logs are available in the `idea.log` log file. To view this file, either:
<!-- vale gitlab_base.SubstitutionWarning = NO -->
- In your IDE, go to **Help > Show Log in Finder**.
- Go to the directory `/Users/<user>/Library/Logs/JetBrains/IntelliJIdea<build_version>`, replacing
`<user>` and `<build_version>` with the appropriate values.
<!-- vale gitlab_base.SubstitutionWarning = YES -->
## Certificate errors
If your machine connects to your GitLab instance through a proxy, you might encounter

View File

@ -7,8 +7,8 @@ description: "Connect and use GitLab Duo in Neovim."
# GitLab plugin for Neovim - `gitlab.vim`
The [GitLab plugin](https://gitlab.com/gitlab-org/editor-extensions/gitlab.vim)
integrates GitLab with Neovim, and is built in Lua.
The [GitLab plugin](https://gitlab.com/gitlab-org/editor-extensions/gitlab.vim) is a Lua-based plugin
that integrates GitLab with Neovim.
[Install and configure the extension](setup.md).

View File

@ -54,7 +54,7 @@ To enable more logging:
## Reproduce the problem in a minimal project
To help improve the maintainers' ability to understand and resolve your issue, create a sample
To help project maintainers understand and resolve your issue, create a sample
configuration or project that reproduces your issue. For example, when troubleshooting
a problem with Code Suggestions:

View File

@ -82,7 +82,7 @@ A full list of environment variables is available in the extension's help text a
To configure this extension:
1. Configure your desired file types. For example, because this plugin supports Ruby, it adds a `FileType ruby` auto-command.
To configure this behavior for additional file types, add more file types to the `code_suggestions.auto_filetypes` setup option:
To configure this behavior for more file types, add more file types to the `code_suggestions.auto_filetypes` setup option:
```lua
require('gitlab').setup({

View File

@ -57,5 +57,5 @@ This extension provides these custom commands, which you can configure:
You can access the extension's custom commands with keyboard shortcuts, which you can customize:
1. On the top bar, go to **Tools > Options**.
1. Go to **Environment > Keyboard**. Commands exposed by this extension are prefixed with `GitLab.`.
1. Go to **Environment > Keyboard**. This extension prefixes its commands with `GitLab.`.
1. Select a command, and assign it a keyboard shortcut.

View File

@ -9,7 +9,10 @@ description: "Use the GitLab Workflow extension for VS Code to handle common Git
If your GitLab project uses CI/CD pipelines, you can start, watch, and debug CI/CD pipelines from the
GitLab Workflow extension for VS Code. When you work locally on a Git branch, the bottom status bar
shows the status of its most recent pipeline, or shows **No pipeline** if a pipeline hasn't run yet:
shows either:
- The status of its most recent pipeline.
- **No pipeline** if a pipeline hasn't run yet.
![The bottom status bar, showing the most recent pipeline has failed.](../img/status_bar_pipeline_v17_6.png)

View File

@ -91,10 +91,10 @@ Not all item types support all parameters. These parameters apply to all query t
| `draft` | **{dotted-circle}** No | `no` | Filter merge requests against their draft status: `yes` returns only merge requests in [draft status](../../user/project/merge_requests/drafts.md), `no` returns only merge requests not in draft status. Available only for merge requests. |
| `excludeAssignee` | **{dotted-circle}** No | not applicable | Return items not assigned to the given username. Available only for issues. For the current user, set to `<current_user>`. |
| `excludeAuthor` | **{dotted-circle}** No | not applicable | Return items not created by the given username. Available only for issues. For the current user, set to `<current_user>`. |
| `excludeLabels` | **{dotted-circle}** No | `[]` | Array of label names. Available only for issues. To be returned, items must have none of the labels in the array. Predefined names are case-insensitive. |
| `excludeLabels` | **{dotted-circle}** No | `[]` | Array of label names. Available only for issues. Items returned have none of the labels in the array. Predefined names are case-insensitive. |
| `excludeMilestone` | **{dotted-circle}** No | not applicable | The milestone title to exclude. Available only for issues. |
| `excludeSearch` | **{dotted-circle}** No | not applicable | Search GitLab items that doesn't have the search key in their title or description. Works only with issues. |
| `labels` | **{dotted-circle}** No | `[]` | Array of label names. To be returned, items must have all labels in the array. `None` returns items with no labels. `Any` returns items with at least one label. Predefined names are case-insensitive. |
| `labels` | **{dotted-circle}** No | `[]` | Array of label names. Items returned have all labels in the array. `None` returns items with no labels. `Any` returns items with at least one label. Predefined names are case-insensitive. |
| `maxResults` | **{dotted-circle}** No | 20 | The number of results to show. |
| `milestone` | **{dotted-circle}** No | not applicable | The milestone title. `None` lists all items with no milestone. `Any` lists all items with an assigned milestone. Not available for epics and vulnerabilities. |
| `orderBy` | **{dotted-circle}** No | `created_at` | Return entities ordered by the selected value. Possible values: `created_at`, `updated_at`, `priority`, `due_date`, `relative_position`, `label_priority`, `milestone_due`, `popularity`, `weight`. Some values are specific to issues, and some to merge requests. For more information, see [List merge requests](../../api/merge_requests.md#list-merge-requests). |

View File

@ -171,9 +171,9 @@ Use this extension to review, comment on, and approve merge requests without lea
merge request you want to review. Its sidebar entry expands with more information.
1. Under the merge request's number and title, select **Description** to read more about the merge request.
1. To review the proposed changes to a file, select the file from the list to show it in a VS Code tab.
Diff comments are shown inline in the tab. In the list, deleted files are marked in red:
GitLab shows diff comments inline in the tab. In the list, deleted files are marked in red:
![An alphabetical list of files changed in this merge request, including the type of changes.](../img/vscode_view_changed_file_v17_6.png)
![An alphabetical list of files changed in this merge request, including the change types.](../img/vscode_view_changed_file_v17_6.png)
Use the diff to:
@ -240,11 +240,11 @@ DETAILS:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/1675) in VS Code extension version 5.31.
Static application security testing (SAST) in VS Code detects vulnerabilities in the active file.
With early detection, you can remediate vulnerabilities before your changes are merged into the
With early detection, you can remediate vulnerabilities before you merge your changes into the
default branch.
When you trigger a SAST scan, the content of the active file is passed to GitLab and checked against
SAST vulnerability rules. Scan results are shown in the primary side bar.
SAST vulnerability rules. GitLab shows scan results in the primary side bar.
Prerequisites:
@ -254,6 +254,8 @@ Prerequisites:
To perform SAST scanning of a file in VS Code:
<!-- markdownlint-disable MD044 -->
1. Open the file.
1. Trigger the SAST scan by either:
- Saving the file (if you have [selected the **Enable scanning on file save** option](setup.md#code-security)).
@ -266,11 +268,11 @@ To perform SAST scanning of a file in VS Code:
1. View the results of the SAST scan.
1. View the **Primary Side Bar**.
1. Select GitLab Workflow ({tanuki}) to display the extension sidebar.
<!-- markdownlint-disable MD044 -->
1. Expand the **GITLAB REMOTE SCAN (SAST)** section.
The results of the SAST scan are listed in descending order by severity. To see details of a
finding, select it in the **GITLAB REMOTE SCAN (SAST)** section of the extension sidebar.
<!-- markdownlint-enable MD044 -->
## Search issues and merge requests

View File

@ -57,7 +57,7 @@ The extension shows information in the VS Code status bar if both:
To configure settings, go to **Settings > Extensions > GitLab Workflow**.
By default, Code Suggestions and GitLab Duo Chat are turned on, so if you have
By default, Code Suggestions and GitLab Duo Chat are enabled, so if you have
the GitLab Duo add-on and a seat assigned, you should have access.
### Code security

View File

@ -58,7 +58,7 @@ These instructions were tested on Arch Linux `5.14.3-arch1-1` and VS Code 1.60.0
### MacOS
NOTE:
These instructions are untested, but likely work as intended. If you can confirm this setup,
These instructions are untested, but should work as intended. If you can confirm this setup,
create a documentation issue with more information.
Make sure you see the self-signed CA in your keychain:

View File

@ -123,9 +123,8 @@ A workaround exists for MacOS:
### Ubuntu workaround
When VS Code is installed with `snap` in Ubuntu 20.04 and 22.04, it can't read passwords from the OS keychain that extension versions 3.44.0
and later use for secure token storage.
When you install VS Code with `snap` in Ubuntu 20.04 and 22.04, VS Code can't read passwords from the
OS keychain. Extension versions 3.44.0 and later use the OS keychain for secure token storage.
A workaround exists for Ubuntu users who use versions of VS Code earlier than 1.68.0:
- You can downgrade the GitLab Workflow extension to version 3.43.1.
@ -141,7 +140,7 @@ the last three steps to re-authenticate.
## Set token with environment variables
If you often delete your VS Code storage, such as in Gitpod containers, you can create environment variables
If you often delete your VS Code storage, such as in Gitpod containers, set environment variables
before starting VS Code. If you set the token in a
[VS Code environment variable](https://code.visualstudio.com/docs/editor/variables-reference#_environment-variables),
you don't have to set a personal access token each time you delete your VS Code storage. Set these variables:

View File

@ -13,6 +13,7 @@ DETAILS:
> - Introduced in GitLab 15.9 as an [experiment](../../policy/development_stages_support.md#experiment) feature [with a flag](../../administration/feature_flags.md) named `combined_analytics_dashboards`. Disabled by default.
> - `combined_analytics_dashboards` [enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/389067) by default in GitLab 16.11.
> - `combined_analytics_dashboards` [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/454350) in GitLab 17.1.
> - `filters` configuration [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/505317) in GitLab 17.9. Disabled by default.
Analytics dashboards help you visualize the collected data.
You can use built-in dashboards by GitLab or create your own dashboards with custom visualizations.
@ -193,6 +194,27 @@ and one visualization (line chart) that applies to all dashboards, the file stru
│ └── example_line_chart.yaml
```
### Dashboard filters
Dashboards support the following filters:
- **Date range**: Date selector to filter data by date.
- **Anonymous users**: Toggle to include or exclude anonymous users from the dataset.
To enable filters, in the `.yaml` configuration file set the filter's `enabled` option to `true`:
```yaml
title: My dashboard
...
filters:
excludeAnonymousUsers:
enabled: true
dateRange:
enabled: true
```
See a complete [dashboard configuration example](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/analytics/product_analytics/dashboards/audience.yaml).
## Define a chart visualization
You can define different charts and add visualization options to some of them, such as:

View File

@ -246,7 +246,12 @@ GitLab.com customers must contact their Customer Success Manager to enable this
You can add files in your VS Code workspace to ask GitLab Duo Chat about.
You cannot add local files that are not part of a repository.
Prerequisites:
- You cannot add local files that are not part of a repository.
- Only text-based files can be included. Binary files (such as PDFs or images) are not supported.
To do this:
1. In your IDE, in GitLab Duo Chat, type `/include`.
1. To add files, you can either:

View File

@ -343,7 +343,7 @@ possibility. For example:
deploy-pages:
stage: deploy
script:
- echo "Pages accessible through ${CI_PAGES_URL}/${PAGES_PREFIX}"
- echo "Pages accessible through ${CI_PAGES_URL}"
variables:
PAGES_PREFIX: "" # No prefix by default (master)
pages: # specifies that this is a Pages job
@ -379,14 +379,14 @@ For example:
deploy-pages:
stage: deploy
script:
- echo "Pages accessible through ${CI_PAGES_URL}/${PAGES_PREFIX}"
- echo "Pages accessible through ${CI_PAGES_URL}"
variables:
PAGES_PREFIX: "" # no prefix by default (master)
pages: # specifies that this is a Pages job
path_prefix: "$PAGES_PREFIX"
environment:
name: "Pages ${PAGES_PREFIX}"
url: "${CI_PAGES_URL}/${PAGES_PREFIX}"
url: $CI_PAGES_URL
artifacts:
paths:
- public

View File

@ -2,4 +2,7 @@ inherit_from:
- ../config/rubocop.yml
Gemfile/MissingFeatureCategory:
Enabled: false
Enabled: false
Search/NamespacedClass:
Enabled: false

View File

@ -4,6 +4,7 @@ PATH
gitlab-active-context (0.0.1)
activesupport
connection_pool
elasticsearch
pg
zeitwerk
@ -56,7 +57,22 @@ GEM
date (3.4.1)
diff-lcs (1.5.1)
drb (2.2.1)
elasticsearch (7.17.11)
elasticsearch-api (= 7.17.11)
elasticsearch-transport (= 7.17.11)
elasticsearch-api (7.17.11)
multi_json
elasticsearch-transport (7.17.11)
base64
faraday (>= 1, < 3)
multi_json
erubi (1.13.0)
faraday (2.12.2)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.0)
net-http (>= 0.5.0)
gitlab-styles (13.0.2)
rubocop (~> 1.68.0)
rubocop-capybara (~> 2.21.0)
@ -81,6 +97,9 @@ GEM
nokogiri (>= 1.12.0)
mini_portile2 (2.8.8)
minitest (5.25.4)
multi_json (1.15.0)
net-http (0.6.0)
uri
nokogiri (1.17.1)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)

View File

@ -30,13 +30,23 @@ ActiveContext.configure do |config|
config.databases = {
es1: {
adapter: 'elasticsearch',
prefix: 'gitlab',
prefix: 'gitlab_active_context',
options: ::Gitlab::CurrentSettings.elasticsearch_config
}
}
end
```
#### Elasticsearch Configuration Options
| Option | Description | Required | Default | Example |
|--------|-------------|----------|---------|---------|
| `url` | The URL of the Elasticsearch server | Yes | N/A | `'http://localhost:9200'` |
| `prefix` | The prefix for Elasticsearch indices | No | `'gitlab_active_context'` | `'my_custom_prefix'` |
| `client_request_timeout` | The timeout for client requests in seconds | No | N/A | `60` |
| `retry_on_failure` | The number of times to retry a failed request | No | `0` (no retries) | `3` |
| `debug` | Enable or disable debug logging | No | `false` | `true` |
## Contributing
TODO

View File

@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
spec.add_dependency 'activesupport'
spec.add_dependency 'connection_pool'
spec.add_dependency 'elasticsearch'
spec.add_dependency 'pg'
spec.add_dependency 'zeitwerk'

View File

@ -4,7 +4,7 @@ module ActiveContext
module Databases
module Concerns
module Client
DEFAULT_PREFIX = 'gitlab'
DEFAULT_PREFIX = 'gitlab_active_context'
attr_reader :options

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
module ActiveContext
module Databases
module Elasticsearch
class Adapter
include ActiveContext::Databases::Concerns::Adapter
def client_klass
ActiveContext::Databases::Elasticsearch::Client
end
end
end
end
end

View File

@ -0,0 +1,46 @@
# frozen_string_literal: true
module ActiveContext
module Databases
module Elasticsearch
class Client
include ActiveContext::Databases::Concerns::Client
OPEN_TIMEOUT = 5
NO_RETRY = 0
def initialize(options)
@options = options
end
def search(_query)
res = client.search
QueryResult.new(res)
end
def client
::Elasticsearch::Client.new(elasticsearch_config)
end
private
def elasticsearch_config
{
adapter: :net_http,
urls: options[:url],
transport_options: {
request: {
timeout: options[:client_request_timeout],
open_timeout: OPEN_TIMEOUT
}
},
randomize_hosts: true,
retry_on_failure: options[:retry_on_failure] || NO_RETRY,
log: options[:debug],
debug: options[:debug]
}.compact
end
end
end
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
module ActiveContext
module Databases
module Elasticsearch
class QueryResult
include ActiveContext::Databases::Concerns::QueryResult
def initialize(result)
@result = result
end
def count
result['hits']['total']['value']
end
def each
return enum_for(:each) unless block_given?
result['hits']['hits'].each do |hit|
yield hit['_source']
end
end
private
attr_reader :result
end
end
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
RSpec.describe ActiveContext::Databases::Elasticsearch::Adapter do
let(:options) { { url: 'http://localhost:9200' } }
subject(:adapter) { described_class.new(options) }
it 'delegates search to client' do
query = ActiveContext::Query.filter(foo: :bar)
expect(adapter.client).to receive(:search).with(query)
adapter.search(query)
end
end

View File

@ -0,0 +1,65 @@
# frozen_string_literal: true
RSpec.describe ActiveContext::Databases::Elasticsearch::Client do
let(:options) { { url: 'http://localhost:9200' } }
subject(:client) { described_class.new(options) }
describe '#search' do
let(:elasticsearch_client) { instance_double(Elasticsearch::Client) }
let(:search_response) { { 'hits' => { 'total' => 5, 'hits' => [] } } }
before do
allow(client).to receive(:client).and_return(elasticsearch_client)
allow(elasticsearch_client).to receive(:search).and_return(search_response)
end
it 'calls search on the Elasticsearch client' do
expect(elasticsearch_client).to receive(:search)
client.search('query')
end
it 'returns a QueryResult object' do
result = client.search('query')
expect(result).to be_a(ActiveContext::Databases::Elasticsearch::QueryResult)
end
end
describe '#client' do
let(:elasticsearch_config) { client.send(:elasticsearch_config) }
let(:options) { { url: 'http://localhost:9200', client_request_timeout: 30, retry_on_failure: 3, debug: true } }
it 'returns an instance of Elasticsearch::Client' do
expect(Elasticsearch::Client).to receive(:new).with(elasticsearch_config)
client.client
end
it 'includes all expected keys with correct values' do
expect(elasticsearch_config).to include(
adapter: :net_http,
urls: 'http://localhost:9200',
transport_options: {
request: {
timeout: 30,
open_timeout: 5
}
},
randomize_hosts: true,
retry_on_failure: 3,
log: true,
debug: true
)
end
end
describe '#prefix' do
it 'returns default prefix when not specified' do
expect(client.prefix).to eq('gitlab_active_context')
end
it 'returns configured prefix' do
client = described_class.new(options.merge(prefix: 'custom'))
expect(client.prefix).to eq('custom')
end
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
RSpec.describe ActiveContext::Databases::Elasticsearch::QueryResult do
let(:elasticsearch_result) do
{
'hits' => {
'total' => { 'value' => 2 },
'hits' => [
{ '_source' => { 'id' => 1, 'name' => 'test1' } },
{ '_source' => { 'id' => 2, 'name' => 'test2' } }
]
}
}
end
subject(:query_result) { described_class.new(elasticsearch_result) }
describe '#count' do
it 'returns the total number of hits' do
expect(query_result.count).to eq(2)
end
end
describe '#each' do
it 'yields each hit source' do
expected_sources = [
{ 'id' => 1, 'name' => 'test1' },
{ 'id' => 2, 'name' => 'test2' }
]
expect { |b| query_result.each(&b) }.to yield_successive_args(*expected_sources)
end
it 'returns an enumerator when no block is given' do
expect(query_result.each).to be_a(Enumerator)
end
end
describe 'enumerable behavior' do
it 'implements Enumerable methods' do
expect(query_result.map { |hit| hit['id'] }).to eq([1, 2]) # rubocop: disable Rails/Pluck -- pluck not implemented
expect(query_result.select { |hit| hit['id'] == 1 }).to eq([{ 'id' => 1, 'name' => 'test1' }])
end
end
end

View File

@ -44,7 +44,7 @@ RSpec.describe ActiveContext::Databases::Postgresql::Client do
describe '#prefix' do
it 'returns default prefix when not specified' do
expect(client.prefix).to eq('gitlab')
expect(client.prefix).to eq('gitlab_active_context')
end
it 'returns configured prefix' do

View File

@ -2,6 +2,7 @@
require "active_context"
require 'logger'
require 'elasticsearch'
RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure

View File

@ -7,8 +7,14 @@ module API
feature_category :continuous_integration
urgency :low
ALLOWED_SORT_VALUES = %w[id pipeline_id].freeze
DEFAULT_SORT_VALUE = 'id'
ALLOWED_SORT_DIRECTIONS = %w[asc desc].freeze
DEFAULT_SORT_DIRECTION = 'asc'
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
requires :id, types: [String, Integer], desc: 'ID or URL-encoded path of the project.'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
include PaginationParams
@ -25,11 +31,23 @@ module API
is_array true
end
params do
requires :sha, type: String, desc: 'The commit hash', documentation: { example: '18f3e63d05582537db6d183d9d557be09e1f90c8' }
optional :ref, type: String, desc: 'The ref', documentation: { example: 'develop' }
optional :stage, type: String, desc: 'The stage', documentation: { example: 'test' }
optional :name, type: String, desc: 'The name', documentation: { example: 'bundler:audit' }
optional :all, type: Boolean, desc: 'Show all statuses', documentation: { default: false }
requires :sha, type: String, desc: 'Hash of the commit.', documentation: { example: '18f3e63d05582537db6d183d9d557be09e1f90c8' }
optional :ref, type: String, desc: 'Name of the branch or tag. Default is the default branch.', documentation: { example: 'develop' }
optional :stage, type: String, desc: 'Filter statuses by build stage.', documentation: { example: 'test' }
optional :name, type: String, desc: 'Filter statuses by job name.', documentation: { example: 'bundler:audit' }
optional :pipeline_id, type: Integer, desc: 'Filter statuses by pipeline ID.', documentation: { example: 1234 }
optional :all, type: Boolean, desc: 'Include all statuses instead of latest only. Default is `false`.', documentation: { default: false }
optional :order_by,
type: String,
values: ALLOWED_SORT_VALUES,
default: DEFAULT_SORT_VALUE,
desc: 'Values for sorting statuses. Valid values are `id` and `pipeline_id`. Default is `id`.',
documentation: { default: DEFAULT_SORT_VALUE }
optional :sort,
type: String,
values: ALLOWED_SORT_DIRECTIONS,
desc: 'Sort statuses in ascending or descending order. Valid values are `asc` and `desc`. Default is `asc`.',
documentation: { default: DEFAULT_SORT_DIRECTION }
use :pagination
end
# rubocop: disable CodeReuse/ActiveRecord
@ -39,11 +57,13 @@ module API
not_found!('Commit') unless user_project.commit(params[:sha])
pipelines = user_project.ci_pipelines.where(sha: params[:sha])
pipelines = pipelines.where(id: params[:pipeline_id]) if params[:pipeline_id].present?
statuses = ::CommitStatus.where(pipeline: pipelines)
statuses = statuses.latest unless to_boolean(params[:all])
statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
statuses = statuses.where(stage: params[:stage]) if params[:stage].present?
statuses = statuses.where(name: params[:name]) if params[:name].present?
statuses = order_and_sort_statuses(statuses)
present paginate(statuses), with: Entities::CommitStatus
end
# rubocop: enable CodeReuse/ActiveRecord
@ -95,6 +115,14 @@ module API
updatable_optional_attributes = %w[target_url description coverage]
attributes_for_keys(updatable_optional_attributes)
end
# rubocop: disable CodeReuse/ActiveRecord -- Better code maintainability here, this won't be reused anywhere
def order_and_sort_statuses(statuses)
sort_direction = params[:sort].presence || DEFAULT_SORT_DIRECTION
order_column = ALLOWED_SORT_VALUES.include?(params[:order_by]) ? params[:order_by] : DEFAULT_SORT_VALUE
statuses.order(order_column => sort_direction)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end

View File

@ -30,7 +30,7 @@ module API
strong_memoize_attr :upstream
def cached_responses
upstream.cached_responses.default.search_by_relative_path(params[:search])
upstream.default_cached_responses.order_created_desc.search_by_relative_path(params[:search])
end
def cached_response
@ -108,7 +108,7 @@ module API
authorize! :destroy_virtual_registry, cached_response.upstream
destroy_conditionally!(cached_response) do |cached_response|
render_validation_error!(cached_response) unless cached_response.update(upstream: nil)
render_validation_error!(cached_response) unless cached_response.mark_as_pending_destruction
end
end
end

View File

@ -4,10 +4,14 @@
module Gitlab
module Database
module Sos
TASKS = [].freeze
TASKS = [
Sos::ShowAllSettings
].freeze
def self.run
TASKS
def self.run(output_file)
Output.writing(output_file, mode: :directory) do |output|
TASKS.each { |t| t.run(output) }
end
end
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
module Gitlab
module Database
module Sos
class Output
def self.writing(path, mode:)
output = new(path, mode: mode)
yield output
ensure
output.finish
end
def initialize(path, mode:)
@mode = mode
if mode == :zip
@zip = Zip::File.open(path, create: true) # rubocop:disable Performance/Rubyzip -- opening a file so no performance issues
elsif mode == :directory # Used in testing, it's a lot easier to inspect a directory than a zip file
@dir = path
else
raise "mode must be one of :zip, :directory"
end
end
def finish
@zip.close if @mode == :zip
end
def write_file(relative_path)
if @mode == :zip
@zip.get_output_stream(relative_path) do |f|
yield f
end
else
abs_path = File.join(@dir, relative_path)
FileUtils.mkdir_p(File.dirname(abs_path))
File.open(abs_path, 'w+') do |f|
yield f
end
end
end
end
end
end
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'csv'
# Create a csv with all the pg settings
module Gitlab
module Database
module Sos
class ShowAllSettings
def self.run(output)
query_results = ApplicationRecord.connection.execute('SHOW ALL;')
output.write_file('pg_settings.csv') do |f|
CSV.open(f, 'w+') do |csv|
# headers [name, setting, description]
csv << query_results.fields
# data
query_results.each do |row|
csv << row.values
end
end
end
end
end
end
end
end

View File

@ -21,7 +21,7 @@ namespace :gitlab do
desc 'Gitlab | DB | Troubleshoot issues with the database'
task sos: :environment do
Gitlab::Database::Sos.run
Gitlab::Database::Sos.run("tmp/sos")
end
namespace :mark_migration_complete do

View File

@ -17024,9 +17024,6 @@ msgstr ""
msgid "Created %{time_ago}"
msgstr ""
msgid "Created %{timeago}"
msgstr ""
msgid "Created %{timestamp}"
msgstr ""
@ -19636,9 +19633,6 @@ msgstr ""
msgid "DeployTokens|Your new project deploy token has been created."
msgstr ""
msgid "Deployed %{timeago}"
msgstr ""
msgid "Deployed to"
msgstr ""
@ -19800,9 +19794,6 @@ msgstr ""
msgid "Deployment|Deployment #%{iid}"
msgstr ""
msgid "Deployment|Deployment ID"
msgstr ""
msgid "Deployment|Deployment approvals require a Premium or Ultimate subscription"
msgstr ""
@ -19913,6 +19904,15 @@ msgstr ""
msgid "Deployment|There was an issue fetching the deployment, please try again later."
msgstr ""
msgid "Deployment|Triggered by %{username}"
msgstr ""
msgid "Deployment|Triggered by %{username} on %{time}"
msgstr ""
msgid "Deployment|Triggered on %{time}"
msgstr ""
msgid "Deployment|Triggerer"
msgstr ""
@ -22250,6 +22250,9 @@ msgstr ""
msgid "Environments|A freeze period is in effect from %{startTime} to %{endTime}. Deployments might fail during this time. For more information, see the %{docsLinkStart}deploy freeze documentation%{docsLinkEnd}."
msgstr ""
msgid "Environments|Actions"
msgstr ""
msgid "Environments|An error occurred while canceling the auto stop, please try again"
msgstr ""
@ -22322,6 +22325,9 @@ msgstr ""
msgid "Environments|Deployment history"
msgstr ""
msgid "Environments|Deployments"
msgstr ""
msgid "Environments|Edit environment"
msgstr ""
@ -22370,6 +22376,9 @@ msgstr ""
msgid "Environments|Learn more about stopping environments"
msgstr ""
msgid "Environments|Name"
msgstr ""
msgid "Environments|New environment"
msgstr ""

View File

@ -300,7 +300,7 @@
"swagger-cli": "^4.0.4",
"tailwindcss": "^3.4.1",
"timezone-mock": "^1.0.8",
"vite": "^6.0.6",
"vite": "^6.0.7",
"vite-plugin-ruby": "^5.1.1",
"vue-loader-vue3": "npm:vue-loader@17.4.2",
"vue-test-utils-compat": "0.0.14",

View File

@ -1,7 +1,6 @@
# Lines beginning with a hash are ignored, like this one.
# Do not add new specs to this file.
ee/spec/frontend/access_tokens/components/expires_at_field_spec.js
ee/spec/frontend/admin/application_settings/deletion_protection/index_spec.js
ee/spec/frontend/admin/application_settings/general/components/license_dropzone_spec.js
ee/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js
ee/spec/frontend/admin/subscriptions/show/components/subscription_activation_form_spec.js
@ -276,7 +275,6 @@ spec/frontend/super_sidebar/components/nav_item_router_link_spec.js
spec/frontend/super_sidebar/components/organization_switcher_spec.js
spec/frontend/super_sidebar/components/sidebar_portal_spec.js
spec/frontend/super_sidebar/components/user_menu_spec.js
spec/frontend/tags/init_delete_tag_modal_spec.js
spec/frontend/todos/components/filtered_search_tokens/group_token_spec.js
spec/frontend/todos/components/filtered_search_tokens/project_token_spec.js
spec/frontend/tooltips/components/tooltips_spec.js

View File

@ -10,8 +10,8 @@ FactoryBot.define do
upstream_etag { OpenSSL::Digest.hexdigest('SHA256', 'test') }
content_type { 'text/plain' }
file_final_path { '5f/9c/5f9c/@final/c7/4c/240c' }
file_md5 { '54ce07f4124259b2ea58548e9d620004' }
file_sha1 { 'bbde7c9fb6d74f9a2393bb36b0d4ac7e72c227ee' }
file_md5 { 'd8e8fca2dc0f896fd7cb4cb0031ba249' }
file_sha1 { '4e1243bd22c66e76c2ba9eddc1f91394e57f9f83' }
status { :default }
transient do

View File

@ -135,8 +135,6 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery
context 'when there are no deployments' do
before do
visit_environments(project)
page.click_button _('Expand')
end
it 'shows environments names and counters' do
@ -162,13 +160,11 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery
create(:deployment, :success, environment: environment, sha: project.commit.id)
end
it 'shows deployment SHA and internal ID' do
it 'shows deployment SHA' do
visit_environments(project)
page.click_button _('Expand')
expect(page).to have_text(deployment.short_sha)
expect(page).to have_link(deployment.commit.full_title)
expect(page).to have_content(deployment.iid)
end
context 'when builds and manual actions are present' do
@ -353,7 +349,6 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery
it 'does not show deployments', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/409990' do
visit_environments(project)
page.click_button _('Expand')
expect(page).to have_content(s_('Environments|There are no deployments for this environment yet. Learn more about setting up deployments.'))
end
end
@ -368,10 +363,8 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery
it "renders the upcoming deployment", :aggregate_failures do
visit_environments(project)
page.click_button _('Expand')
within(upcoming_deployment_content_selector) do
expect(page).to have_content("##{deployment.iid}")
expect(page).to have_content(project.commit.short_id)
expect(page).to have_link(href: /#{deployment.user.username}/)
end
end

View File

@ -1,5 +1,7 @@
import { initCatalog } from '~/ci/catalog/';
import { createWrapper } from '@vue/test-utils';
import { initCatalog } from '~/ci/catalog';
import * as Router from '~/ci/catalog/router';
import GlobalCatalog from '~/ci/catalog/global_catalog.vue';
import CiResourcesPage from '~/ci/catalog/components/pages/ci_resources_page.vue';
describe('~/ci/catalog/index', () => {
@ -7,7 +9,7 @@ describe('~/ci/catalog/index', () => {
const SELECTOR = 'SELECTOR';
let el;
let component;
let wrapper;
const baseRoute = '/explore/catalog';
const createElement = () => {
@ -21,15 +23,17 @@ describe('~/ci/catalog/index', () => {
el = null;
});
const findGlobalCatalog = () => wrapper.findComponent(GlobalCatalog);
describe('when the element exists', () => {
beforeEach(() => {
createElement();
jest.spyOn(Router, 'createRouter');
component = initCatalog(`#${SELECTOR}`);
wrapper = createWrapper(initCatalog(`#${SELECTOR}`));
});
it('returns a Vue Instance', () => {
expect(component.$options.name).toBe('GlobalCatalog');
it('renders the GlobalCatalog component', () => {
expect(findGlobalCatalog().exists()).toBe(true);
});
it('creates a router with the received base path and component', () => {
@ -40,7 +44,7 @@ describe('~/ci/catalog/index', () => {
describe('When the element does not exist', () => {
it('returns `null`', () => {
expect(initCatalog('foo')).toBe(null);
expect(initCatalog('foo')).toBeNull();
});
});
});

View File

@ -1,23 +1,12 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { GlLoadingIcon } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { useFakeDate } from 'helpers/fake_date';
import { stubTransition } from 'helpers/stub_transition';
import { localeDateFormat } from '~/lib/utils/datetime_utility';
import { GlIcon, GlLink, GlTruncate, GlSprintf } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import Deployment from '~/environments/components/deployment.vue';
import Commit from '~/environments/components/commit.vue';
import DeploymentStatusLink from '~/environments/components/deployment_status_link.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import getDeploymentDetails from '~/environments/graphql/queries/deployment_details.query.graphql';
import { resolvedEnvironment, resolvedDeploymentDetails } from './graphql/mock_data';
import { resolvedEnvironment } from './graphql/mock_data';
describe('~/environments/components/deployment.vue', () => {
Vue.use(VueApollo);
useFakeDate(2022, 0, 8, 16);
let deployment;
let wrapper;
@ -25,32 +14,35 @@ describe('~/environments/components/deployment.vue', () => {
deployment = resolvedEnvironment.lastDeployment;
});
const createWrapper = ({ propsData = {}, options = {} } = {}) => {
const mockApollo = createMockApollo([
[getDeploymentDetails, jest.fn().mockResolvedValue(resolvedDeploymentDetails)],
]);
return mountExtended(Deployment, {
stubs: { transition: stubTransition() },
const createWrapper = ({ propsData = {} } = {}) => {
return shallowMountExtended(Deployment, {
propsData: {
deployment,
visible: true,
...propsData,
},
apolloProvider: mockApollo,
provide: { projectPath: '/1' },
...options,
stubs: { GlTruncate, GlSprintf },
});
};
afterEach(() => {
wrapper?.destroy();
});
const findStatusLink = () => wrapper.findComponent(DeploymentStatusLink);
const findLatestBadge = () => wrapper.findByText('Latest Deployed');
const findApprovalBadge = () => wrapper.findByText('Needs Approval');
const findCommit = () => wrapper.findComponent(Commit);
const findShortSha = () => wrapper.findByTestId('deployment-commit-sha');
const findCopyButton = () => wrapper.findComponent(ClipboardButton);
const findShortShaLink = () => findShortSha().findComponent(GlLink);
const findShortShaIcon = () => findShortSha().findComponent(GlIcon);
const findTag = () => wrapper.findByTestId('deployment-tag');
const findTagLink = () => findTag().findComponent(GlLink);
const findTagIcon = () => findTag().findComponent(GlIcon);
const findTimestamp = () => wrapper.findByTestId('deployment-timestamp');
const findTriggerer = () => wrapper.findByTestId('deployment-triggerer');
const findUserLink = () => findTriggerer().findComponent(GlLink);
describe('status', () => {
it('should pass the deployable status to the link', () => {
wrapper = createWrapper();
expect(wrapper.findComponent(DeploymentStatusLink).props()).toEqual({
expect(findStatusLink().props()).toEqual({
status: deployment.status,
deploymentJob: deployment.deployable,
deployment,
@ -62,53 +54,52 @@ describe('~/environments/components/deployment.vue', () => {
it('should show a badge if the deployment is latest', () => {
wrapper = createWrapper({ propsData: { latest: true } });
const badge = wrapper.findByText('Latest Deployed');
expect(badge.exists()).toBe(true);
expect(findLatestBadge().exists()).toBe(true);
});
it('should not show a badge if the deployment is not latest', () => {
wrapper = createWrapper();
const badge = wrapper.findByText('Latest Deployed');
expect(badge.exists()).toBe(false);
expect(findLatestBadge().exists()).toBe(false);
});
});
describe('iid', () => {
const findIid = () => wrapper.findByTitle('Deployment ID');
const findDeploymentIcon = () => wrapper.findComponent({ ref: 'deployment-iid-icon' });
describe('approval badge', () => {
it('should show a badge if the deployment needs approval', () => {
wrapper = createWrapper({
propsData: { deployment: { ...deployment, pendingApprovalCount: 5 } },
});
describe('is present', () => {
expect(findApprovalBadge().exists()).toBe(true);
});
it('should not show a badge if the deployment does not need approval', () => {
wrapper = createWrapper();
expect(findApprovalBadge().exists()).toBe(false);
});
});
describe('commit message', () => {
describe('with commit', () => {
beforeEach(() => {
wrapper = createWrapper();
});
it('should show the iid', () => {
const iid = findIid();
expect(iid.exists()).toBe(true);
});
it('should show an icon for the iid', () => {
const deploymentIcon = findDeploymentIcon();
expect(deploymentIcon.props('name')).toBe('deployments');
it('shows the commit component', () => {
expect(findCommit().props('commit')).toBe(deployment.commit);
});
});
describe('is not present', () => {
beforeEach(() => {
wrapper = createWrapper({ propsData: { deployment: { ...deployment, iid: '' } } });
});
describe('without a commit', () => {
it('displays nothing', () => {
const noCommit = {
...deployment,
commit: null,
};
wrapper = createWrapper({ propsData: { deployment: noCommit } });
it('should not show the iid', () => {
const iid = findIid();
expect(iid.exists()).toBe(false);
});
it('should not show an icon for the iid', () => {
const deploymentIcon = findDeploymentIcon();
expect(deploymentIcon.exists()).toBe(false);
expect(findCommit().exists()).toBe(false);
});
});
});
@ -120,21 +111,16 @@ describe('~/environments/components/deployment.vue', () => {
});
it('shows the short SHA for the commit of the deployment', () => {
const sha = wrapper.findByRole('link', { name: 'Commit SHA' });
expect(sha.exists()).toBe(true);
expect(sha.text()).toBe(deployment.commit.shortId);
expect(sha.attributes('href')).toBe(deployment.commit.commitPath);
expect(findShortShaLink().text()).toBe(deployment.commit.shortId);
expect(findShortShaLink().attributes('href')).toBe(deployment.commit.commitPath);
});
it('shows the commit icon', () => {
const icon = wrapper.findComponent({ ref: 'deployment-commit-icon' });
expect(icon.props('name')).toBe('commit');
expect(findShortShaIcon().props('name')).toBe('commit');
});
it('shows a copy button for the sha', () => {
const button = wrapper.findComponent(ClipboardButton);
expect(button.props()).toMatchObject({
expect(findCopyButton().props()).toMatchObject({
text: deployment.commit.shortId,
title: 'Copy commit SHA',
});
@ -151,144 +137,98 @@ describe('~/environments/components/deployment.vue', () => {
},
},
});
const sha = wrapper.findByTestId('deployment-commit-sha');
expect(sha.exists()).toBe(false);
expect(findShortSha().exists()).toBe(false);
});
});
});
describe('deployedAt', () => {
describe('is present', () => {
it('shows the timestamp the deployment was deployed at', () => {
wrapper = createWrapper();
const date = wrapper.findByTestId('deployment-timestamp');
expect(date.text()).toBe('Deployed 1 day ago');
});
});
});
describe('created at time', () => {
describe('is present and deploymentAt is null', () => {
it('shows the timestamp the deployment was created at', () => {
wrapper = createWrapper({ propsData: { deployment: { ...deployment, deployedAt: null } } });
const date = wrapper.findByTestId('deployment-timestamp');
expect(date.text()).toBe('Created 1 day ago');
describe('tag', () => {
describe('is present', () => {
const ref = {
name: 'v1.0.0',
refPath: '/tags/v1.0.0',
};
beforeEach(() => {
const deploymentWithTag = {
...deployment,
tag: true,
ref,
};
wrapper = createWrapper({
propsData: {
deployment: deploymentWithTag,
},
});
});
it('shows tag information', () => {
expect(findTagLink().text()).toBe(ref.name);
expect(findTagLink().attributes('href')).toBe(ref.refPath);
});
it('displays the tag icon', () => {
expect(findTagIcon().props('name')).toBe('tag');
});
});
describe('is not present', () => {
it('does not show the tag', () => {
wrapper = createWrapper();
expect(findTag().exists()).toBe(false);
});
});
});
describe('triggered text', () => {
it('shows the timestamp the deployment was created at if deployedAt is null', () => {
wrapper = createWrapper({
propsData: { deployment: { ...deployment, deployedAt: null } },
});
expect(findTimestamp().text()).toBe('January 7, 2022 at 3:46:27 PM GMT');
});
it('shows the timestamp the deployment was deployed at if deployedAt is present', () => {
wrapper = createWrapper();
expect(findTimestamp().text()).toBe('January 7, 2022 at 3:47:32 PM GMT');
});
describe('is not present', () => {
it('does not show the timestamp', () => {
wrapper = createWrapper({ propsData: { deployment: { ...deployment, createdAt: null } } });
const date = wrapper.findByTitle(
localeDateFormat.asDateTimeFull.format(deployment.createdAt),
);
wrapper = createWrapper({
propsData: { deployment: { ...deployment, createdAt: null, deployedAt: null } },
});
expect(date.exists()).toBe(false);
expect(findTimestamp().exists()).toBe(false);
});
});
});
describe('commit message', () => {
describe('with commit', () => {
describe('deployment user', () => {
describe('is present', () => {
beforeEach(() => {
wrapper = createWrapper();
});
it('shows the commit component', () => {
const commit = wrapper.findComponent(Commit);
expect(commit.props('commit')).toBe(deployment.commit);
it('shows triggerer information', () => {
expect(findUserLink().text()).toBe('@root');
expect(findUserLink().attributes('href')).toBe('/root');
});
});
describe('without a commit', () => {
it('displays nothing', () => {
const noCommit = {
...deployment,
commit: null,
};
wrapper = createWrapper({ propsData: { deployment: noCommit } });
describe('is not present', () => {
const deploymentWithoutUser = {
...deployment,
user: null,
};
const commit = wrapper.findComponent(Commit);
expect(commit.exists()).toBe(false);
it('does not show the tag', () => {
wrapper = createWrapper({
propsData: {
deployment: deploymentWithoutUser,
},
});
expect(findTriggerer().exists()).toBe(false);
});
});
});
describe('details', () => {
beforeEach(() => {
wrapper = createWrapper();
});
it('shows information about the deployment', () => {
const username = wrapper.findByRole('link', { name: `@${deployment.user.username}` });
expect(username.attributes('href')).toBe(deployment.user.path);
const job = wrapper.findByRole('link', { name: deployment.deployable.name });
expect(job.attributes('href')).toBe(deployment.deployable.buildPath);
const apiBadge = wrapper.findByText('API');
expect(apiBadge.exists()).toBe(false);
const branchLabel = wrapper.findByText('Branch');
expect(branchLabel.exists()).toBe(true);
const tagLabel = wrapper.findByText('Tag');
expect(tagLabel.exists()).toBe(false);
const ref = wrapper.findByRole('link', { name: deployment.ref.name });
expect(ref.attributes('href')).toBe(deployment.ref.refPath);
});
it('shows information about tags related to the deployment', async () => {
expect(wrapper.findByText('Tags').exists()).toBe(true);
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
await waitForPromises();
for (let i = 1; i < 6; i += 1) {
const tagName = `testTag${i}`;
const testTag = wrapper.findByText(tagName);
expect(testTag.exists()).toBe(true);
expect(testTag.attributes('href')).toBe(`tags/${tagName}`);
}
expect(wrapper.findByText('testTag6').exists()).toBe(false);
expect(wrapper.findByText('Tag').exists()).toBe(false);
// with more than 5 tags, show overflow marker
expect(wrapper.findByText('...').exists()).toBe(true);
});
});
describe('with tagged deployment', () => {
beforeEach(() => {
wrapper = createWrapper({ propsData: { deployment: { ...deployment, tag: true } } });
});
it('shows tags instead of branch', () => {
const refLabel = wrapper.findByText('Tags');
expect(refLabel.exists()).toBe(true);
const branchLabel = wrapper.findByText('Branch');
expect(branchLabel.exists()).toBe(false);
});
});
describe('with API deployment', () => {
beforeEach(() => {
wrapper = createWrapper({ propsData: { deployment: { ...deployment, deployable: null } } });
});
it('shows API instead of a job name', () => {
const apiBadge = wrapper.findByText('API');
expect(apiBadge.exists()).toBe(true);
});
});
describe('without a job path', () => {
beforeEach(() => {
wrapper = createWrapper({
propsData: {
deployment: { ...deployment, deployable: { name: deployment.deployable.name } },
},
});
});
it('shows a span instead of a link', () => {
const job = wrapper.findByTitle(deployment.deployable.name);
expect(job.attributes('href')).toBeUndefined();
});
});
});

View File

@ -195,6 +195,16 @@ describe('~/environments/components/environments_app.vue', () => {
expect(text).toContainEqual(expect.stringMatching('production'));
});
it('should show environments table headers when there are environments are present', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
folder: resolvedFolder,
});
const tableHeaders = wrapper.findByTestId('environments-table-header');
expect(tableHeaders.text()).toBe('Name Deployments Actions');
});
it('should show an empty state with no environments', async () => {
await createWrapperWithMocked({
environmentsApp: { ...resolvedEnvironmentsApp, environments: [] },
@ -203,6 +213,15 @@ describe('~/environments/components/environments_app.vue', () => {
expect(wrapper.findComponent(EmptyState).exists()).toBe(true);
});
it('should not show environments table headers with no environment', async () => {
await createWrapperWithMocked({
environmentsApp: { ...resolvedEnvironmentsApp, environments: [] },
});
const tableHeaders = wrapper.findByTestId('environments-table-header');
expect(tableHeaders.exists()).toBe(false);
});
it('should show a button to create a new environment', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,

View File

@ -89,7 +89,7 @@ describe('EnvironmentsFolderAppComponent', () => {
await waitForPromises();
});
it('should list environmnets in folder', () => {
it('should list environments in folder', () => {
const items = findEnvironmentItems();
expect(items.length).toBe(resolvedFolder.environments.length);
});

View File

@ -555,7 +555,7 @@ export const resolvedEnvironment = {
sha: 'f3ba6dd84f8f891373e9b869135622b954852db1',
ref: { name: 'main', refPath: '/h5bp/html5-boilerplate/-/tree/main' },
status: 'success',
createdAt: '2022-01-07T15:47:27.415Z',
createdAt: '2022-01-07T15:46:27.415Z',
deployedAt: '2022-01-07T15:47:32.450Z',
tierInYaml: 'staging',
tag: false,
@ -870,7 +870,7 @@ export const resolvedEnvironmentToRollback = {
sha: 'f3ba6dd84f8f891373e9b869135622b954852db1',
ref: { name: 'main', refPath: '/h5bp/html5-boilerplate/-/tree/main' },
status: 'success',
createdAt: '2022-01-07T15:47:27.415Z',
createdAt: '2022-01-07T15:46:27.415Z',
deployedAt: '2022-01-07T15:47:32.450Z',
tierInYaml: 'staging',
tag: false,

View File

@ -1,6 +1,6 @@
import VueApollo from 'vue-apollo';
import Vue from 'vue';
import { GlCollapse, GlLink, GlSprintf, GlButton } from '@gitlab/ui';
import { GlLink, GlSprintf } from '@gitlab/ui';
import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
@ -36,18 +36,13 @@ describe('~/environments/components/new_environment_item.vue', () => {
projectPath: '/1',
...provideData,
},
stubs: { GlSprintf, TimeAgoTooltip, GlCollapse },
stubs: { GlSprintf, TimeAgoTooltip },
});
const findDeployment = () => wrapper.findComponent(Deployment);
const findActions = () => wrapper.findComponent(EnvironmentActions);
const findNameLink = () => wrapper.findComponent(GlLink);
const findCollapse = () => wrapper.findComponent(GlCollapse);
const expandCollapsedSection = async () => {
const button = wrapper.findComponent(GlButton);
await button.vm.$emit('click');
};
const findEmptyState = () => wrapper.findByTestId('deployments-empty-state');
it('displays the name when not in a folder', () => {
wrapper = createWrapper({ apolloProvider: createApolloProvider() });
@ -315,31 +310,6 @@ describe('~/environments/components/new_environment_item.vue', () => {
});
});
describe('collapse', () => {
const findCollapseButton = () => wrapper.findComponent(GlButton);
beforeEach(() => {
wrapper = createWrapper({ apolloProvider: createApolloProvider() });
});
it('is collapsed by default', () => {
expect(findCollapse().props('visible')).toBe(false);
expect(findCollapseButton().props('icon')).toBe('chevron-lg-right');
expect(findNameLink().classes('gl-font-bold')).toBe(false);
});
it('opens on click', async () => {
expect(findDeployment().props('visible')).toBe(false);
await expandCollapsedSection();
expect(findCollapseButton().attributes('aria-label')).toBe('Collapse');
expect(findCollapseButton().props('icon')).toBe('chevron-lg-down');
expect(findNameLink().classes('gl-font-bold')).toBe(true);
expect(findCollapse().props('visible')).toBe(true);
expect(findDeployment().props('visible')).toBe(true);
});
});
describe('last deployment', () => {
it('should pass the last deployment to the deployment component when it exists', () => {
wrapper = createWrapper({ apolloProvider: createApolloProvider() });
@ -373,7 +343,7 @@ describe('~/environments/components/new_environment_item.vue', () => {
});
const deployment = findDeployment();
expect(deployment.props('deployment')).toEqual(upcomingDeployment);
expect(deployment.props('deployment')).toMatchObject(upcomingDeployment);
});
it('should not show the upcoming deployment when it is missing', () => {
const environment = {
@ -393,7 +363,7 @@ describe('~/environments/components/new_environment_item.vue', () => {
});
describe('empty state', () => {
it('should link to documentation', async () => {
it('should link to documentation', () => {
const environment = {
...resolvedEnvironment,
lastDeployment: null,
@ -405,27 +375,23 @@ describe('~/environments/components/new_environment_item.vue', () => {
apolloProvider: createApolloProvider(),
});
await expandCollapsedSection();
expect(findCollapse().text()).toBe(
expect(findEmptyState().text()).toBe(
'There are no deployments for this environment yet. Learn more about setting up deployments.',
);
expect(findCollapse().findComponent(GlLink).attributes('href')).toBe('/help');
expect(findEmptyState().findComponent(GlLink).attributes('href')).toBe('/help');
});
it('should not link to the documentation when there are deployments', async () => {
it('should not show empty state when there are deployments', () => {
wrapper = createWrapper({
apolloProvider: createApolloProvider(),
});
await expandCollapsedSection();
expect(findCollapse().findComponent(GlLink).exists()).toBe(false);
expect(findEmptyState().exists()).toBe(false);
});
});
describe('deploy boards', () => {
it('should show a deploy board if the environment has a rollout status', async () => {
it('should show a deploy board if the environment has a rollout status', () => {
const environment = {
...resolvedEnvironment,
rolloutStatus,
@ -436,20 +402,15 @@ describe('~/environments/components/new_environment_item.vue', () => {
apolloProvider: createApolloProvider(),
});
await expandCollapsedSection();
const deployBoard = wrapper.findComponent(DeployBoardWrapper);
expect(deployBoard.exists()).toBe(true);
expect(deployBoard.props('rolloutStatus')).toBe(rolloutStatus);
});
it('should not show a deploy board if the environment has no rollout status', async () => {
it('should not show a deploy board if the environment has no rollout status', () => {
wrapper = createWrapper({
apolloProvider: createApolloProvider(),
});
await expandCollapsedSection();
const deployBoard = wrapper.findComponent(DeployBoardWrapper);
expect(deployBoard.exists()).toBe(false);
});

View File

@ -1,23 +1,42 @@
import Vue from 'vue';
import { resetHTMLFixture, setHTMLFixture } from 'helpers/fixtures';
import initDeleteTagModal from '../../../app/assets/javascripts/tags/init_delete_tag_modal';
import { createWrapper } from '@vue/test-utils';
import initDeleteTagModal from '~/tags/init_delete_tag_modal';
import DeleteTagModal from '~/tags/components/delete_tag_modal.vue';
describe('initDeleteTagModal', () => {
beforeEach(() => {
setHTMLFixture('<div class="js-delete-tag-modal"></div>');
});
let appRoot;
let wrapper;
const createAppRoot = () => {
appRoot = document.createElement('div');
appRoot.setAttribute('class', 'js-delete-tag-modal');
document.body.appendChild(appRoot);
wrapper = createWrapper(initDeleteTagModal());
};
afterEach(() => {
resetHTMLFixture();
if (appRoot) {
appRoot.remove();
appRoot = null;
}
});
it('should mount the delete tag modal', () => {
expect(initDeleteTagModal()).toBeInstanceOf(Vue);
expect(document.querySelector('.js-delete-tag-modal')).toBeNull();
const findDeleteTagModal = () => wrapper.findComponent(DeleteTagModal);
describe('when there is no app root', () => {
it('returns false', () => {
expect(initDeleteTagModal()).toBe(false);
});
});
it('should return false if the mounting element is missing', () => {
document.querySelector('.js-delete-tag-modal').remove();
expect(initDeleteTagModal()).toBe(false);
describe('when there is an app root', () => {
beforeEach(() => {
createAppRoot();
});
it('renders the modal', () => {
expect(findDeleteTagModal().exists()).toBe(true);
});
});
});

View File

@ -29,7 +29,8 @@ RSpec.describe Gitlab::Database::NamespaceProjectIdsEachBatch, feature_category:
end
context 'when passed an optional resolver' do
it 'returns the correct project IDs filtered by resolver' do
it 'returns the correct project IDs filtered by resolver',
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/497833' do
resolver = ->(batch) {
Project.where(id: batch).where(path: [project1.path, project2.path]).pluck_primary_key
}

View File

@ -0,0 +1,55 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::Sos::Output, feature_category: :database do
let(:temp_directory) { Dir.mktmpdir }
let(:zip_path) { File.join(temp_directory, 'test.zip') }
let(:directory_path) { File.join(temp_directory, 'test_directory') }
after do
FileUtils.remove_entry(temp_directory)
end
describe '#writing' do
it 'yields an output object and ensures finish is called' do
expect_next_instance_of(described_class) do |instance|
expect(instance).to receive(:finish)
end
described_class.writing(zip_path, mode: :zip) do |output|
expect(output).to be_a(described_class)
end
end
end
describe '#initialize' do
it 'raises an error for invalid mode' do
expect { described_class.new(zip_path, mode: :invalid) }
.to raise_error(RuntimeError, "mode must be one of :zip, :directory")
end
end
describe '#write_file' do
let(:relative_path) { 'test/file.txt' }
let(:content) { 'PG Settings' }
it 'writes content to a file in zip mode' do
described_class.writing(zip_path, mode: :zip) do |output|
output.write_file(relative_path) { |f| f.write(content) }
end
Zip::File.open(zip_path) do |zip_file|
expect(zip_file.read(relative_path)).to eq(content)
end
end
it 'writes content to a file in directory mode' do
described_class.writing(directory_path, mode: :directory) do |output|
output.write_file(relative_path) { |f| f.write(content) }
end
expect(File.read(File.join(directory_path, relative_path))).to eq(content)
end
end
end

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::Sos::ShowAllSettings, feature_category: :database do
describe '#run' do
let(:temp_directory) { Dir.mktmpdir }
let(:output_file_path) { temp_directory }
let(:expected_file_path) { File.join(temp_directory, 'pg_settings.csv') }
let(:output) { Gitlab::Database::Sos::Output.new(output_file_path, mode: :directory) }
after do
FileUtils.remove_entry(temp_directory)
end
it 'creates a CSV file with the correct headers and data' do
described_class.run(output)
output.finish
expect(File.exist?(expected_file_path)).to be true
csv_content = CSV.read(expected_file_path)
expect(csv_content.first).to eq(%w[name setting description])
block_size_row = csv_content.find { |row| row[0] == 'block_size' }
expect(block_size_row).not_to be_nil
# NOTE: 8192 bytes is the default value for the block size in Postgres so
# it's safe to say this value will not change for us.
expect(block_size_row[1]).to eq('8192')
end
end
end

View File

@ -5,8 +5,15 @@ require 'spec_helper'
# WIP
RSpec.describe Gitlab::Database::Sos, feature_category: :database do
describe '#run' do
let(:temp_directory) { Dir.mktmpdir }
let(:output_file_path) { temp_directory }
after do
FileUtils.remove_entry(temp_directory)
end
it "executes sos" do
result = described_class.run
result = described_class.run(output_file_path)
expect(result).to eq(Gitlab::Database::Sos::TASKS)
end
end

View File

@ -308,73 +308,113 @@ RSpec.describe Packages::Protection::Rule, type: :model, feature_category: :pack
end
end
describe '.for_push_exists_for_multiple_packages' do
let_it_be(:project_with_ppr) { create(:project) }
let_it_be(:ppr_for_maintainer) do
describe '.for_push_exists_for_projects_and_packages' do
let_it_be(:project1) { create(:project) }
let_it_be(:project1_ppr) do
create(:package_protection_rule,
package_name_pattern: '@my-scope/my-package-prod*',
project: project_with_ppr,
project: project1,
package_type: :npm
)
end
let(:package_names) {
%w[
@my-scope/my-package-prod-1
@my-scope/my-package-prod-unmatched-package-type
@my-scope/unmatched-package-name
@my-scope/unmatched-package-name-and-package-type
]
}
let_it_be(:project2) { create(:project) }
let_it_be(:project2_ppr) do create(:package_protection_rule, project: project2) end
let(:package_types) do
let_it_be(:unprotected_project) { create(:project) }
let(:package_type_npm) { Packages::Package.package_types[:npm] }
let(:single_project_input) do
[
Packages::Package.package_types[:npm],
Packages::Package.package_types[:maven],
Packages::Package.package_types[:npm],
Packages::Package.package_types[:maven]
[project1.id, '@my-scope/my-package-prod-1', Packages::Package.package_types[:npm]],
[project1.id, '@my-scope/my-package-prod-unmatched-package-type', Packages::Package.package_types[:maven]],
[project1.id, '@my-scope/unmatched-package-name', Packages::Package.package_types[:npm]],
[project1.id, '@my-scope/unmatched-package-name-and-package-type', Packages::Package.package_types[:maven]]
]
end
subject do
described_class
.for_push_exists_for_multiple_packages(
project_id: project_with_ppr.id,
package_names: package_names,
package_types: package_types
)
.to_a
let(:single_project_expected_result) do
[
{ 'project_id' => project1.id,
'package_name' => '@my-scope/my-package-prod-1',
'package_type' => Packages::Package.package_types[:npm],
'protected' => true },
{ 'project_id' => project1.id,
'package_name' => '@my-scope/my-package-prod-unmatched-package-type',
'package_type' => Packages::Package.package_types[:maven],
'protected' => false },
{ 'project_id' => project1.id,
'package_name' => '@my-scope/unmatched-package-name',
'package_type' => Packages::Package.package_types[:npm],
'protected' => false },
{ 'project_id' => project1.id,
'package_name' => '@my-scope/unmatched-package-name-and-package-type',
'package_type' => Packages::Package.package_types[:maven],
'protected' => false }
]
end
it do
is_expected.to eq([
{ "package_name" => '@my-scope/my-package-prod-1',
"package_type" => Packages::Package.package_types[:npm],
"protected" => true },
{ "package_name" => '@my-scope/my-package-prod-unmatched-package-type',
"package_type" => Packages::Package.package_types[:maven],
"protected" => false },
{ "package_name" => '@my-scope/unmatched-package-name',
"package_type" => Packages::Package.package_types[:npm],
"protected" => false },
{ "package_name" => '@my-scope/unmatched-package-name-and-package-type',
"package_type" => Packages::Package.package_types[:maven],
"protected" => false }
])
let(:multi_projects_input) do
[
*single_project_input,
[project2.id, project2_ppr.package_name_pattern, Packages::Package.package_types[project2_ppr.package_type]],
[project2.id, "#{project2_ppr.package_name_pattern}-unprotected",
Packages::Package.package_types[project2_ppr.package_type]]
]
end
context 'when edge cases' do
where(:package_names, :package_types, :expected_result) do
nil | nil | []
[] | [] | []
nil | [] | []
%w[@my-scope/my-package-prod-1] | [] | []
end
let(:multi_projects_expected_result) do
[
*single_project_expected_result,
{ 'project_id' => project2.id,
'package_name' => project2_ppr.package_name_pattern,
'package_type' => Packages::Package.package_types[project2_ppr.package_type],
'protected' => true },
{ 'project_id' => project2.id,
'package_name' => "#{project2_ppr.package_name_pattern}-unprotected",
'package_type' => Packages::Package.package_types[project2_ppr.package_type],
'protected' => false }
]
end
with_them do
it { is_expected.to eq([]) }
end
let(:unprotected_projects_input) do
[
*multi_projects_input,
[unprotected_project.id, "#{unprotected_project.full_path}-unprotected1", package_type_npm],
[unprotected_project.id, "#{unprotected_project.full_path}-unprotected2", package_type_npm]
]
end
let(:unprotected_projects_expected_result) do
[
*multi_projects_expected_result,
{ 'project_id' => unprotected_project.id,
'package_name' => "#{unprotected_project.full_path}-unprotected1",
'package_type' => package_type_npm,
'protected' => false },
{ 'project_id' => unprotected_project.id,
'package_name' => "#{unprotected_project.full_path}-unprotected2",
'package_type' => package_type_npm,
'protected' => false }
]
end
subject { described_class.for_push_exists_for_projects_and_packages(projects_and_packages).to_a }
# rubocop:disable Layout/LineLength -- Avoid formatting to ensure one-line table syntax
where(:projects_and_packages, :expected_result) do
ref(:single_project_input) | ref(:single_project_expected_result)
ref(:multi_projects_input) | ref(:multi_projects_expected_result)
ref(:unprotected_projects_input) | ref(:unprotected_projects_expected_result)
nil | []
[] | []
[[nil, nil, nil]] | [{ "package_name" => nil, "package_type" => nil, "project_id" => nil, "protected" => false }]
end
# rubocop:enable Layout/LineLength
with_them do
it { is_expected.to match_array expected_result }
end
end
end

View File

@ -18,10 +18,16 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponse, type: :model,
it { is_expected.to validate_presence_of(attr) }
end
%i[relative_path upstream_etag content_type].each do |attr|
%i[upstream_etag content_type].each do |attr|
it { is_expected.to validate_length_of(attr).is_at_most(255) }
end
it { is_expected.to validate_length_of(:file_final_path).is_at_most(1024) }
%i[relative_path object_storage_key file_final_path].each do |attr|
it { is_expected.to validate_length_of(attr).is_at_most(1024) }
end
it { is_expected.to validate_length_of(:file_md5).is_equal_to(32).allow_nil }
it { is_expected.to validate_length_of(:file_sha1).is_equal_to(40) }
context 'with persisted cached response' do
before do
@ -30,20 +36,6 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponse, type: :model,
it { is_expected.to validate_uniqueness_of(:relative_path).scoped_to(:upstream_id, :status) }
context 'when upstream_id is nil' do
let(:new_cached_response) { build(:virtual_registries_packages_maven_cached_response) }
before do
cached_response.update!(upstream_id: nil)
new_cached_response.upstream = nil
end
it 'does not validate uniqueness of relative_path' do
new_cached_response.validate
expect(new_cached_response.errors.messages_for(:relative_path)).not_to include 'has already been taken'
end
end
context 'with a similar cached response in a different status' do
let!(:cached_response_in_error) do
create(
@ -77,6 +69,7 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponse, type: :model,
it 'belongs to an upstream' do
is_expected.to belong_to(:upstream)
.class_name('VirtualRegistries::Packages::Maven::Upstream')
.required
.inverse_of(:cached_responses)
end
end
@ -116,12 +109,12 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponse, type: :model,
end
it 'can not be too large' do
cached_response.object_storage_key = 'a' * 256
cached_response.object_storage_key = 'a' * 1025
cached_response.relative_path = nil
expect(cached_response).to be_invalid
expect(cached_response.errors.full_messages)
.to include('Object storage key is too long (maximum is 255 characters)')
.to include('Object storage key is too long (maximum is 1024 characters)')
end
it 'is set before saving' do
@ -186,7 +179,7 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponse, type: :model,
upstream: upstream,
group_id: upstream.group_id,
relative_path: '/test',
updates: { file: file, size: size, file_sha1: 'test' }
updates: { file: file, size: size, file_sha1: '4e1243bd22c66e76c2ba9eddc1f91394e57f9f95' }
)
end
end
@ -274,10 +267,45 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponse, type: :model,
end
end
describe '#mark_as_pending_destruction' do
let_it_be_with_refind(:cached_response) { create(:virtual_registries_packages_maven_cached_response, :default) }
subject(:execute) { cached_response.mark_as_pending_destruction }
shared_examples 'updating the status and relative_path properly' do
it 'updates the status and relative_path' do
previous_path = cached_response.relative_path
expect { execute }.to change { cached_response.status }.from('default').to('pending_destruction')
.and not_change { cached_response.object_storage_key }
expect(cached_response.relative_path).to start_with(previous_path)
expect(cached_response.relative_path).to include('/deleted/')
end
end
it_behaves_like 'updating the status and relative_path properly'
context 'with an existing pending destruction record with same relative_path and upstream_id' do
let_it_be(:already_pending_destruction) do
create(
:virtual_registries_packages_maven_cached_response,
:pending_destruction,
upstream: cached_response.upstream,
relative_path: cached_response.relative_path
)
end
it_behaves_like 'updating the status and relative_path properly'
end
end
context 'with loose foreign key on virtual_registries_packages_maven_cached_responses.upstream_id' do
it_behaves_like 'update by a loose foreign key' do
let_it_be(:parent) { create(:virtual_registries_packages_maven_upstream) }
let_it_be(:model) { create(:virtual_registries_packages_maven_cached_response, upstream: parent) }
let(:find_model) { model.reload }
end
end
@ -285,6 +313,8 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponse, type: :model,
it_behaves_like 'update by a loose foreign key' do
let_it_be(:parent) { create(:group) }
let_it_be(:model) { create(:virtual_registries_packages_maven_cached_response, group: parent) }
let(:find_model) { model.reload }
end
end

View File

@ -231,4 +231,20 @@ RSpec.describe VirtualRegistries::Packages::Maven::Upstream, type: :model, featu
it { is_expected.not_to include('username', 'password') }
end
describe '#default_cached_responses' do
let_it_be(:upstream) { create(:virtual_registries_packages_maven_upstream) }
let_it_be(:default_cached_response) do
create(:virtual_registries_packages_maven_cached_response, upstream: upstream)
end
let_it_be(:pending_destruction_cached_response) do
create(:virtual_registries_packages_maven_cached_response, :pending_destruction, upstream: upstream)
end
subject { upstream.default_cached_responses }
it { is_expected.to contain_exactly(default_cached_response) }
end
end

View File

@ -31,8 +31,8 @@ RSpec.describe API::CommitStatuses, :clean_gitlab_redis_cache, feature_category:
context "reporter user" do
let(:statuses_id) { json_response.map { |status| status['id'] } }
def create_status(commit, opts = {})
create(:commit_status, { pipeline: commit, ref: commit.ref }.merge(opts))
def create_status(pipeline, opts = {})
create(:commit_status, { pipeline: pipeline, ref: pipeline.ref }.merge(opts))
end
let!(:status1) { create_status(master, status: 'running', retried: true) }
@ -58,44 +58,73 @@ RSpec.describe API::CommitStatuses, :clean_gitlab_redis_cache, feature_category:
end
end
context 'all commit statuses' do
shared_examples_for 'get commit statuses' do
before do
get api(get_url, reporter), params: { all: 1 }
get api(get_url, reporter), params: params
end
it 'returns all commit statuses' do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(
status1.id, status2.id, status3.id, status4.id, status5.id, status6.id
)
expect(statuses_id).to eq(expected_statuses)
end
end
context 'Get all commit statuses' do
let(:params) { { all: 1 } }
let(:expected_statuses) { [status1.id, status2.id, status3.id, status4.id, status5.id, status6.id] }
it_behaves_like 'get commit statuses'
end
context 'latest commit statuses for specific ref' do
before do
get api(get_url, reporter), params: { ref: 'develop' }
end
let(:params) { { ref: 'develop' } }
let(:expected_statuses) { [status3.id, status5.id] }
it 'returns latest commit statuses for specific ref' do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(status3.id, status5.id)
end
it_behaves_like 'get commit statuses'
end
context 'latest commit statues for specific name' do
before do
get api(get_url, reporter), params: { name: 'coverage' }
end
let(:params) { { name: 'coverage' } }
let(:expected_statuses) { [status4.id, status5.id] }
it 'return latest commit statuses for specific name' do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(status4.id, status5.id)
it_behaves_like 'get commit statuses'
end
context 'latest commit statuses for specific pipeline' do
let(:params) { { pipeline_id: develop.id } }
let(:expected_statuses) { [status3.id, status5.id] }
it_behaves_like 'get commit statuses'
end
context 'return commit statuses sort by desc id' do
let(:params) { { all: 1, sort: "desc" } }
let(:expected_statuses) { [status6.id, status5.id, status4.id, status3.id, status2.id, status1.id] }
it_behaves_like 'get commit statuses'
end
context 'return commit statuses sort by desc pipeline_id' do
let(:params) { { all: 1, order_by: "pipeline_id", sort: "desc" } }
let(:expected_statuses) { [status3.id, status5.id, status1.id, status2.id, status4.id, status6.id] }
it_behaves_like 'get commit statuses'
end
context 'return commit statuses sort by asc pipeline_id' do
let(:params) { { all: 1, order_by: "pipeline_id" } }
let(:expected_statuses) { [status1.id, status2.id, status4.id, status6.id, status3.id, status5.id] }
it_behaves_like 'get commit statuses'
end
context 'Bad filter commit statuses' do
it 'return commit statuses order by an unmanaged field' do
get api(get_url, reporter), params: { all: 1, order_by: "name" }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end

View File

@ -43,4 +43,93 @@ RSpec.describe 'getting a package list for a group', feature_category: :package_
expect(graphql_data_at(:b, :packages)).to be(nil)
end
end
describe 'protectionRuleExists' do
let_it_be(:project1_package_protected) { create(:npm_package, project: project1) }
let_it_be(:project1_package) { create(:npm_package, project: project1) }
let_it_be(:package_protection_rule1) do
create(:package_protection_rule, project: project1,
package_name_pattern: project1_package_protected.name,
package_type: project1_package_protected.package_type,
minimum_access_level_for_push: :admin)
end
let_it_be(:project2_package_protected) { create(:npm_package, project: project2) }
let_it_be(:project2_package) { create(:npm_package, project: project2) }
let_it_be(:package_protection_rule2) do
create(:package_protection_rule, project: project2,
package_name_pattern: project2_package_protected.name,
package_type: project2_package_protected.package_type,
minimum_access_level_for_push: :admin)
end
let_it_be_with_reload(:project3) { create(:project, :private, group: resource) }
let_it_be(:project3_package_protected) { create(:npm_package, project: project3) }
let_it_be(:project3_package) { create(:npm_package, project: project3) }
let_it_be(:package_protection_rule3) do
create(:package_protection_rule, project: project3,
package_name_pattern: project3_package_protected.name,
package_type: project3_package_protected.package_type,
minimum_access_level_for_push: :admin)
end
let(:packages) { graphql_data_at(resource_type, :packages, :nodes) }
let(:query) do
graphql_query_for(
resource_type,
{ 'fullPath' => resource.full_path },
query_graphql_field('packages', {}, fields)
)
end
let(:fields) do
<<~QUERY
nodes {
name
protectionRuleExists
}
QUERY
end
subject(:send_graphql_request) { post_graphql(query, current_user: current_user) }
before do
resource.add_reporter(current_user)
end
it 'returns true for all protected packages' do
send_graphql_request
expect(packages).to match_array([
a_hash_including('name' => project1_package_protected.name, 'protectionRuleExists' => true),
a_hash_including('name' => project2_package_protected.name, 'protectionRuleExists' => true),
a_hash_including('name' => project3_package_protected.name, 'protectionRuleExists' => true),
a_hash_including('name' => project1_package.name, 'protectionRuleExists' => false),
a_hash_including('name' => project2_package.name, 'protectionRuleExists' => false),
a_hash_including('name' => project3_package.name, 'protectionRuleExists' => false)
])
end
it 'executes only one database queries for all projects at once' do
expect { send_graphql_request }.to match_query_count(1).for_model(::Packages::Protection::Rule)
end
context 'when 25 packages belong to group' do
let_it_be(:resource) { create(:group) }
let_it_be(:projects) { create_list(:project, 5, :private, group: resource) }
before_all do
projects.each do |project|
package = create_list(:npm_package, 5, project: project)
create(:package_protection_rule, project: project, package_name_pattern: package.first.name,
package_type: package.first.package_type)
end
end
it 'executes only two database queries to check the protection rules for packages in batches of 20' do
expect { send_graphql_request }.to match_query_count(2).for_model(::Packages::Protection::Rule)
end
end
end
end

View File

@ -115,21 +115,11 @@ RSpec.describe API::VirtualRegistries::Packages::Maven::CachedResponses, :aggreg
let(:id) { Base64.urlsafe_encode64("#{upstream.id} #{cached_response.relative_path}") }
let(:url) { "/virtual_registries/packages/maven/cached_responses/#{id}" }
let_it_be(:processing_cached_response) do
create(
:virtual_registries_packages_maven_cached_response,
:processing,
upstream: upstream,
group: upstream.group,
relative_path: cached_response.relative_path
)
end
subject(:api_request) { delete api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
expect { api_request }.to change { upstream.cached_responses.count }.by(-1)
expect { api_request }.to change { cached_response.reload.status }.from('default').to('pending_destruction')
expect(response).to have_gitlab_http_status(:no_content)
end
end
@ -192,7 +182,7 @@ RSpec.describe API::VirtualRegistries::Packages::Maven::CachedResponses, :aggreg
before do
allow_next_found_instance_of(cached_response.class) do |instance|
errors = ActiveModel::Errors.new(instance).tap { |e| e.add(:cached_response, 'error message') }
allow(instance).to receive_messages(save: false, errors: errors)
allow(instance).to receive_messages(mark_as_pending_destruction: false, errors: errors)
end
end

View File

@ -217,7 +217,11 @@ RSpec.describe API::VirtualRegistries::Packages::Maven::Endpoints, :aggregate_fa
api(url),
file_key: :file,
headers: headers,
params: { file: file_upload, 'file.md5' => 'md5', 'file.sha1' => 'sha1' },
params: {
file: file_upload,
'file.md5' => 'd8e8fca2dc0f896fd7cb4cb0031ba249',
'file.sha1' => '4e1243bd22c66e76c2ba9eddc1f91394e57f9f83'
},
send_rewritten_field: true
)
end
@ -228,7 +232,7 @@ RSpec.describe API::VirtualRegistries::Packages::Maven::Endpoints, :aggregate_fa
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to eq('')
expect(upstream.cached_responses.last).to have_attributes(
expect(upstream.default_cached_responses.search_by_relative_path(path).last).to have_attributes(
relative_path: "/#{path}",
upstream_etag: nil,
upstream_checked_at: Time.zone.now,

View File

@ -12,7 +12,13 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponses::CreateOrUpda
let(:etag) { 'test' }
let(:content_type) { 'text/xml' }
let(:params) { { path: path, file: file, etag: etag, content_type: content_type } }
let(:file) { UploadedFile.new(Tempfile.new(etag).path, sha1: 'sha1', md5: 'md5') }
let(:file) do
UploadedFile.new(
Tempfile.new(etag).path,
sha1: '4e1243bd22c66e76c2ba9eddc1f91394e57f9f83',
md5: 'd8e8fca2dc0f896fd7cb4cb0031ba249'
)
end
let(:service) do
described_class.new(upstream: upstream, current_user: user, params: params)
@ -22,7 +28,7 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponses::CreateOrUpda
subject(:execute) { service.execute }
shared_examples 'returning a service response success response' do
shared_examples 'creating a new cached response' do |with_md5: 'md5'|
shared_examples 'creating a new cached response' do |with_md5: 'd8e8fca2dc0f896fd7cb4cb0031ba249'|
it 'returns a success service response', :freeze_time do
expect { execute }.to change { upstream.cached_responses.count }.by(1)
expect(execute).to be_success
@ -36,7 +42,7 @@ RSpec.describe VirtualRegistries::Packages::Maven::CachedResponses::CreateOrUpda
relative_path: "/#{path}",
upstream_etag: etag,
content_type: content_type,
file_sha1: 'sha1',
file_sha1: '4e1243bd22c66e76c2ba9eddc1f91394e57f9f83',
file_md5: with_md5
)
end

View File

@ -75,7 +75,7 @@ RSpec.describe VirtualRegistries::Packages::Maven::HandleFileRequestService, :ag
end
context 'with a cached response' do
let_it_be_with_reload(:cached_response) do
let_it_be_with_refind(:cached_response) do
create(:virtual_registries_packages_maven_cached_response,
:upstream_checked,
upstream: registry.upstream,

View File

@ -14858,10 +14858,10 @@ vite-plugin-ruby@^5.1.1:
debug "^4.3.4"
fast-glob "^3.3.2"
vite@^6.0.6:
version "6.0.6"
resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.6.tgz#a851674fcff55b0c1962f72082354b8802e48505"
integrity sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==
vite@^6.0.7:
version "6.0.7"
resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.7.tgz#f0f8c120733b04af52b4a1e3e7cb54eb851a799b"
integrity sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==
dependencies:
esbuild "^0.24.2"
postcss "^8.4.49"